var async = require('async');
var Compiler = require('./compiler.js');
var DeployTracker = require('./deploy_tracker.js');
var ABIGenerator = require('./abi.js');
var web3;

var Deploy = function(options) {
  this.web3 = options.web3;
  this.contractsManager = options.contractsManager;
  this.logger = options.logger;
  this.env = options.env;

  this.deployTracker = new DeployTracker({
    logger: options.logger, chainConfig: options.chainConfig, web3: options.web3, env: this.env
  });
};

Deploy.prototype.checkAndDeployContract = function(contract, params, callback) {
  var self = this;
  var suppliedArgs;
  var realArgs;
  var arg;
  var l;
  var contractName;
  var referedContract;
  contract.error = false;

  if (contract.deploy === false) {
    self.logger.contractsState(self.contractsManager.contractsState());
    return callback();
  }

  if (contract.address !== undefined) {

    // determine arguments
    suppliedArgs = (params || contract.args);
    realArgs = [];

    for (l = 0; l < suppliedArgs.length; l++) {
      arg = suppliedArgs[l];
      if (arg[0] === "$") {
        contractName = arg.substr(1);
        referedContract = this.contractsManager.getContract(contractName);
        realArgs.push(referedContract.deployedAddress);
      } else {
        realArgs.push(arg);
      }
    }

    contract.deployedAddress = contract.address;
    self.deployTracker.trackContract(contract.className, contract.code, realArgs, contract.address);
    self.deployTracker.save();
    self.logger.contractsState(self.contractsManager.contractsState());
    return callback();
  }

  var trackedContract = self.deployTracker.getContract(contract.className, contract.code, contract.args);

  if (trackedContract && this.web3.eth.getCode(trackedContract.address) !== "0x") {
    self.logger.info(contract.className + " already deployed " + trackedContract.address);
    contract.deployedAddress = trackedContract.address;
    self.logger.contractsState(self.contractsManager.contractsState());
    callback();
  } else {

    // determine arguments
    suppliedArgs = (params || contract.args);
    realArgs = [];

    for (l = 0; l < suppliedArgs.length; l++) {
      arg = suppliedArgs[l];
      if (arg[0] === "$") {
        contractName = arg.substr(1);
        referedContract = this.contractsManager.getContract(contractName);
        realArgs.push(referedContract.deployedAddress);
      } else {
        realArgs.push(arg);
      }
    }

    this.deployContract(contract, realArgs, function(err, address) {
      self.deployTracker.trackContract(contract.className, contract.code, realArgs, address);
      self.deployTracker.save();
      self.logger.contractsState(self.contractsManager.contractsState());

      // TODO: replace evals with separate process so it's isolated and with
      // a callback
      if (contract.onDeploy !== undefined) {
        self.logger.info('executing onDeploy commands');
        var abiGenerator = new ABIGenerator({}, self.contractsManager);
        web3 = self.web3;
        var abi = abiGenerator.generateContracts(false);
        eval(abi); // jshint ignore:line

        var cmds = contract.onDeploy.join(';\n');
        eval(cmds); // jshint ignore:line
      }

      callback();
    });
  }

};

Deploy.prototype.deployContract = function(contract, params, callback) {
  var self = this;
  var contractObject = this.web3.eth.contract(contract.abiDefinition);

  var contractParams = (params || contract.args).slice();

  this.web3.eth.getAccounts(function(err, accounts) {
    //console.log("using address" + this.web3.eth.accounts[0]);

    // TODO: probably needs to be defaultAccount
    // TODO: it wouldn't necessary be the first address
    // use defined blockchain address or first address
    contractParams.push({
      //from: this.web3.eth.coinbase,
      from: accounts[0],
      data: contract.code,
      gas: contract.gas,
      gasPrice: contract.gasPrice
    });

    self.logger.info("deploying " + contract.className + " with " + contract.gas + " gas");
    contractParams.push(function(err, transaction) {
      self.logger.contractsState(self.contractsManager.contractsState());

      if (err) {
        self.logger.error("error deploying contract: " + contract.className);
        var errMsg = err.toString();
        if (errMsg === 'Error: The contract code couldn\'t be stored, please check your gas amount.') {
          errMsg = 'The contract code couldn\'t be stored, out of gas or constructor error';
        }
        self.logger.error(errMsg);
        contract.error = errMsg;
        callback(new Error(err));
      } else if (transaction.address !== undefined) {
        self.logger.info(contract.className + " deployed at " + transaction.address);
        contract.deployedAddress = transaction.address;
        contract.transactionHash = transaction.transactionHash;
        callback(null, transaction.address);
      }
    });

    contractObject["new"].apply(contractObject, contractParams);
  });
};

Deploy.prototype.deployAll = function(done) {
  var self = this;
  this.logger.info("deploying contracts");

  async.eachOfSeries(this.contractsManager.listContracts(),
                     function(contract, key, callback) {
                       self.logger.trace(arguments);
                       self.checkAndDeployContract(contract, null, callback);
                     },
                     function(err, results) {
                       self.logger.info("finished");
                       self.logger.trace(arguments);
                       done();
                     }
                    );

};

module.exports = Deploy;

@@ -2006,24 +2006,31 @@
 ate());%0A    
+return 
 callback();%0A

return callback