// Set the default rpc provider address
if (!LocalStore.get('contracts'))
  LocalStore.set('contracts', {
    testnet: {
      WeiFund: '0x4412dc562b3f79b9687f8e0dc423c9668f68ff87',
      WeiHash: '0x4530e7402c6164fb69e48a7317e920f6eb336b33',
      PersonaRegistry: '0xfc48a7147c804d730b68211cd4d5fb6d4e62576d',
      CampaignAccountFactory: '0xaf505c483953eef7ab1546c812170603adcb49cc',
      WeiControllerFactory: '0x03334620e66500b0216cf6b1f95789efd59072fb',
      WeiFundTokenFactory: '0xdbf36aa536d7cb201f8a0920d7b986f40ce28055',
      MultiServiceFactory: '',
      StaffPicks: '',
    }
  });

// Network
if (!LocalStore.get('network'))
  LocalStore.set('network', 'testnet');

// get local contract addresses
var contracts = LocalStore.get('contracts');
var network = LocalStore.get('network');

if (!network)
  network = 'testnet';

// Setup objects global for contract and helper connector objects
objects = {
  contracts: {
    WeiFund: WeiFund.at(contracts[network].WeiFund),
    WeiHash: WeiHash.at(contracts[network].WeiHash),
    PersonaRegistry: PersonaRegistry.at(contracts[network].PersonaRegistry),
    CampaignAccountFactory: CampaignAccountFactory.at(contracts[network].CampaignAccountFactory),
    WeiFundTokenFactory: WeiFundTokenFactory.at(contracts[network].WeiFundTokenFactory),
    WeiControllerFactory: WeiControllerFactory.at(contracts[network].WeiControllerFactory),
  },
  helpers: {}
};

// Build Campaign Validation Method
objects.helpers.validateCampaignData = function(chainData, ipfsData, callback) {
  // check for undefined callback
  if (_.isUndefined(callback))
    callback = function(e, r) {};

  try {
    // if ipfs object has the campaignSchema property
    if (!_.has(ipfsData, 'campaignSchema'))
      return callback("Invalid IPFS data schema, must contain `campaignSchema`", null);

    // Check beneficiary against the campaign schema
    if (chainData.beneficiary != ipfsData.campaignSchema.beneficiary)
      return callback("Invalid IPFS data schema, must have matching campaign beneficiaries", null);

    // setup numbers as big numbers and get url extensions
    var chainCampaignID = new BigNumber(chainData.id),
      ipfsCampaignID = new BigNumber(ipfsData.campaignSchema.id),
      ipfsAvatarExtension = ipfsData.campaignSchema.avatar.contentUrl.split('.').pop().replace(/(\?.*)|(#.*)/g, ""),
      ipfsBannerExtension = ipfsData.campaignSchema.banner.contentUrl.split('.').pop().replace(/(\?.*)|(#.*)/g, "");

    // does the campaign ID's equate
    if (!chainCampaignID.equals(ipfsCampaignID))
      return callback("Invalid IPFS data schema, must have matching campaign ID's", null);

    // setup funding goal big numbers
    var ipfsFundingGoal = new BigNumber(ipfsData.campaignSchema.fundingGoal),
      chainFundingGoal = new BigNumber(chainData.fundingGoal);

    // do the funding goals equate
    if (!ipfsFundingGoal.equals(chainFundingGoal))
      return callback("Invalid IPFS data schema, campaign funding goals must be matching", null);

    // is the avatar image of a valid extention
    if (ipfsAvatarExtension != 'jpeg' && ipfsAvatarExtension != 'jpg' && ipfsAvatarExtension != 'png')
      return callback("Invalid campaign avatar image extension (must be jpg, jpeg or png)", null);

    // is the banner image of a valid extention
    if (ipfsBannerExtension != 'jpeg' && ipfsBannerExtension != 'jpg' && ipfsBannerExtension != 'png')
      return callback("Invalid campaign banner image extension (must be jpg, jpeg or png)", null);

    // Check data kinds
    /*_.each(Object.keys(ipfsData.campaignSchema), function(key, index){
    	if(!_.isNumber(ipfsData.campaignSchema[key])
    	   && !_.isString(ipfsData.campaignSchema[key])
    	   && !_.isBoolean(ipfsData.campaignSchema[key]))
    		return callback("Invalid IPFS data schema, campaign data structure must be strings, bools and numbers only", null);
    });*/

    // setup config address as nothing
    if (ipfsData.campaignSchema.config == '')
      ipfsData.campaignSchema.config = web3.address(0);

    // setup campaign owner as nothing
    if (_.isUndefined(ipfsData.campaignSchema.owner)) // temp fix
      ipfsData.campaignSchema.owner = web3.address(0);

    // check addresses
    if (!web3.isAddress(ipfsData.campaignSchema.beneficiary) || !web3.isAddress(ipfsData.campaignSchema.config) || !web3.isAddress(ipfsData.campaignSchema.owner))
      return callback("Invalid IPFS data schema, campaign owner, beneficiary and config must be valid Ethereum addresses", null);

    // return true
    return callback(null, true);
  } catch (err) {
    return callback(err, null);
  }
};

// Build Persona Standard Repository
objects.helpers.importPersona = function(personaAddress, callback) {
  personaAddress = Helpers.cleanAscii(personaAddress);

  if (typeof callback === 'undefined')
    callback = function(e, r) {};

  if (!web3.isAddress(personaAddress))
    return callback('Invalid address', null);

  // get persona atributes
  objects.contracts.PersonaRegistry.getPersonaAttributes(personaAddress, function(err, ipfsHashHex) {
    try {
      if (err)
        return callback(err, null);

      // build ipfs hash from hex
      var ipfsHash = ipfs.utils.hexToBase58(ipfsHashHex.slice(2));

      // get ipfs object stats
      ipfs.api.object.stat(ipfsHash, function(err, ipfsDataStats) {
        if (err)
          return callback(err.Message, null);

        // Check repository size
        if (ipfsDataStats.CumulativeSize > 300)
          return callback('Cumulative IPFS repository size exceeds max size limit (the repository is just too big)', null); // get IPFS hash

        // Load repository
        ipfs.catJson(ipfsHash, function(err, ipfsData) {
          if (err)
            return callback(err.Message, null);

          // Clean IPFS Data w/ Google's Caja
          ipfsData = Helpers.cleanXSS(ipfsData);

          // build persona return object
          var personaObject = {
            address: personaAddress,
            dataHex: ipfsHashHex,
            ipfsHash: ipfsHash,
            isValid: true,
            dataValid: true,
            dataError: '',
            data: ipfsData
          };

          if (personaObject.isValid)
            Personas.upsert({
              address: personaObject.address
            }, personaObject);

          return callback(err, personaObject);
        });
      });
    } catch (err) {
      return callback(err, null);
    }
  });
};

// Build Campaign Import Method
objects.helpers.importCampaign = function(campaignID, callback) {
  // clean campaign ID
  campaignID = Helpers.cleanAscii(campaignID);

  // set undefined callback, if any
  if (_.isUndefined(callback))
    callback = function(e, r) {};

  // import campaign data from the Ethereum blockchain
  objects.contracts.WeiFund.campaigns(campaignID, function(err, campaignRaw) {
    if (err)
      return callback(err, null);

		// load cached campaign, if any available
		var cachedCampaign = Campaigns.findOne({id: campaignID});

    // setup campaign object
    var campaign = web3.returnObject('campaigns', campaignRaw, objects.contracts.WeiFund.abi);
    campaign.id = String(campaignID);
    campaign.isValid = true;
    //campaign.data = {};
    campaign.status = {
      type: 'open'
    };
    campaign.progress = 0;
    campaign.account = cachedCampaign && cachedCampaign.account || web3.address(0);
    campaign.validAccount = cachedCampaign && cachedCampaign.validAccount || false;

    // is the campaign valid?
    if (new BigNumber(campaign.created).equals(0))
      campaign.isValid = false;

    // build amount and funding big numbers
    var amountRaised = new BigNumber(campaign.amountRaised),
      fundingGoal = new BigNumber(campaign.fundingGoal);

    // build progress property
    campaign.progress = Math.round(amountRaised.dividedBy(fundingGoal) * 100);

    // has the campaign failed? set status
    if (moment().unix() > parseInt(campaign.expiry) && amountRaised.lessThan(fundingGoal))
      campaign.status = {
        type: 'failed',
        reason: 'expired'
      };

    // is the campaign a success, build a status
    if (amountRaised.greaterThanOrEqualTo(fundingGoal)) {
      campaign.status = {
        type: 'success',
        reason: 'reached'
      };
      campaign.progress = 100;
    }

    // is the campaign paid out, set status
    if (campaign.paidOut) {
      campaign.status = {
        type: 'paidout'
      };
      campaign.progress = 100;
    }

    // if campaign progress is too high, set to 100
    if (campaign.progess > 100)
      campaign.progress = 100;

    // set campaign num contributors to integer
    campaign.numContributions = campaign.numContributions; //maybe error here

    // set campaign created property to integer
    campaign.created = parseInt(campaign.created);

    // return first callback
    callback(err, campaign);

    // Insert into Campaign collection
    if (campaign.isValid)
      Campaigns.upsert({
        id: campaign.id
      }, campaign);

    // see if config is verified
    if (campaign.config !== web3.address(0)) {
      objects.contracts.WeiControllerFactory.isService(campaign.config, function(err, _verifiedController) {
        if (err) return;

        Campaigns.upsert({
          id: campaignID
        }, {
          $set: {
            'controller.verified': _verifiedController
          }
        });

        var controller = WeiController.at(campaign.config);

        controller.total_issued(function(err, total_issued) {
          if (err) return;

          Campaigns.upsert({
            id: campaignID
          }, {
            $set: {
              'controller.total_issued': total_issued.toString(10)
            }
          });

          controller.tokenValue(function(err, tokenValue) {
            if (err) return;

            Campaigns.upsert({
              id: campaignID
            }, {
              $set: {
                'controller.tokenValue': tokenValue.toString(10)
              }
            });

            controller.autoDisperse(function(err, autoDisperse) {
              if (err) return;

              Campaigns.upsert({
                id: campaignID
              }, {
                $set: {
                  'controller.autoDisperse': autoDisperse
                }
              });

              controller.version(function(err, version) {
                if (err) return;

                Campaigns.upsert({
                  id: campaignID
                }, {
                  $set: {
                    'controller.version': version.toString(10)
                  }
                });

                controller.owner(function(err, owner) {
                  if (err) return;

                  Campaigns.upsert({
                    id: campaignID
                  }, {
                    $set: {
                      'controller.owner': owner
                    }
                  });

                  controller.token(function(err, tokenAddress) {
                    if (err) return;

                    Campaigns.upsert({
                      id: campaignID
                    }, {
                      $set: {
                        'controller.token': tokenAddress
                      }
                    });

                    Standard_Token.at(tokenAddress).totalSupply(function(err, totalSupply) {
                      //if(err) return;

                      Campaigns.upsert({
                        id: campaignID
                      }, {
                        $set: {
                          'controller.tokenSupply': totalSupply.toString(10)
                        }
                      });

                      objects.contracts.WeiFundTokenFactory.isService(tokenAddress, function(err, _verifiedToken) {
                        if (!err)
                          Campaigns.upsert({
                            id: campaignID
                          }, {
                            $set: {
                              'controller.verifiedToken': _verifiedToken
                            }
                          });

                        Standard_Token.at(tokenAddress).balanceOf(campaign.config, function(err, controllerBalance) {
                          if (!err)
                            Campaigns.upsert({
                              id: campaignID
                            }, {
                              $set: {
                                'controller.tokenBalance': controllerBalance.toString(10)
                              }
                            });
                        });
                      });
                    });
                  });
                });
              });
            });
          });
        });
      });
    }

    // get weiaccount if any
    objects.contracts.CampaignAccountFactory.accountOf(campaignID, function(err, account) {
      var campaign = Campaigns.findOne({
        id: campaignID
      });

      if (err) return callback(err, null);

      if (account != web3.address(0) && web3.isAddress(account) && account != '0x')
        campaign.validAccount = true;

      // set campaign contribution account
      campaign.account = account;

      // get WeiHash campaign hash
      objects.contracts.WeiHash.hashOf(campaignID, function(err, hashRaw) {
        if (err)
          return callback(err, null);

        // set rawHash data
        campaign.hashRaw = hashRaw;

        // if hash is IPFS Hash... needs better filter
        if (hashRaw != '0x' && hashRaw.length > 5) {
          campaign.hash = ipfs.utils.hexToBase58(hashRaw.slice(2));

          // return second callback
          callback(err, campaign);

          // Insert into Campaign collection
          if (campaign.isValid)
            Campaigns.upsert({
              id: campaign.id
            }, campaign);

          // get ipfs object stats
          ipfs.api.object.stat(campaign.hash, function(err, ipfsDataStats) {
            // no IPFS data statistics available
            if (ipfsDataStats == null)
              return callback('IPFS repository is not available. No statistics could be gathered.', null);

            // Check repository size
            if (ipfsDataStats.CumulativeSize > 2000)
              return callback('Cumulative IPFS campaign repository size exceeds max size limit (the repository is just too big)', null);

            // Lookup Campaign Data
            ipfs.catJson(campaign.hash, function(err, ipfsData) {
              if (err)
                return callback(err.Message, null);

              // Clean IPFS Data w/ Google's Caja
              ipfsData = Helpers.cleanXSS(ipfsData);

              // validate IPFS hash against campaign data
              objects.helpers.validateCampaignData(campaign, ipfsData, function(err, result) {
                if (err) {
                  campaign.dataValid = false;
                  campaign.dataError = err;
                } else {
                  campaign.dataValid = true;
                  campaign.dataError = null;
                  campaign.data = ipfsData.campaignSchema;
                }

                // Insert into Campaign collection
                if (campaign.isValid)
                  Campaigns.upsert({
                    id: campaign.id
                  }, campaign);

                callback(null, campaign);
              });
            });
          });
        } else {
          campaign.dataValid = false;
          campaign.dataError = 'No valid IPFS hash stored';

          callback(null, campaign);
        }
      });
    });
  });
};

// Build Contribution Import Method
objects.helpers.importContribution = function(campaignID, contributionID, callback) {
  campaignID = Helpers.cleanAscii(campaignID),
    contributionID = Helpers.cleanAscii(contributionID);

  if (_.isUndefined(callback))
    callback = function(e, r) {};

  // get contribution at contribution ID
  objects.contracts.WeiFund.contributionAt(campaignID, contributionID, function(err, contributionRaw) {
    if (err)
      return callback(err, null);

    // setup contribution object and amount contributed big number
    var contribution = web3.returnObject('contributionAt', contributionRaw, objects.contracts.WeiFund.abi),
      amountContributed = new BigNumber(contribution.amountContributed);

    // setup contribution db entry defaults
    contribution.isValid = true;
    contribution.campaignID = campaignID;
    contribution.id = contributionID;
    contribution.created = parseInt(contribution.created);

    // check contributor address, amount validation and set valid bool
    if (contribution.contributor == web3.address(0) || amountContributed.equals(0))
      contribution.isValid = false;

    // If the contribution data is valid, insert into local store
    if (contribution.isValid)
      Contributions.upsert({
        campaignID: campaignID,
        id: contribution.id
      }, contribution);

    // callback results
    callback(err, contribution);
  });
};

// Build Contributor Import Method
objects.helpers.importContributor = function(campaignID, contributorAddress, callback) {
  campaignID = Helpers.cleanAscii(campaignID),
    contributorAddress = Helpers.cleanAscii(contributorAddress);

  if (_.isUndefined(callback))
    callback = function(e, r) {};

  // is the account a campaign contributor
  objects.contracts.WeiFund.isContributor(campaignID, contributorAddress, function(err, isContributor) {
    if (err)
      return callback(err, null);

    // if not contirbutor return nothing
    if (!isContributor)
      return callback(null, null);

    // get total contributions count
    objects.contracts.WeiFund.totalContributionsBy(campaignID, contributorAddress, function(err, numContributions) {
      if (err)
        return callback(err, null);

      // if no contributions count, return null
      if (numContributions.equals(0))
        return callback(null, null);

      // index through contributions, contributions can have multiple contributions
      for (var contributionIndex = 0; contributionIndex < numContributions.toNumber(10); contributionIndex++) {

        // get contribution ID given the contribution index
        objects.contracts.WeiFund.contributionID(campaignID, contributorAddress, contributionIndex, function(err, contributionID) {
          if (err)
            return callback(err, null);

          // import contribution by ID
          objects.helpers.importContribution(campaignID, contributionID, callback);
        });
      }
    });
  });
};
