text: 'use strict';
  var bitcore = require('bitcore-lib-safe');
  var _ = bitcore.deps._;
  var $ = bitcore.util.preconditions;
  var Common = require('./common');
  var Decimal = require("decimal.js");
  var async = require('async');
  
  var AnalysisAsset = require("../analysisReserve/AnalysisAsset");
  
  var aAsset = new AnalysisAsset();
  var MAXINT = 0xffffffff; // Math.pow(2, 32) - 1;
  var assetContro = {};
  
  function TxController(node, asset) {
      this.node = node;
      this.common = new Common({log: this.node.log});
      // this.asset = asset;
      assetContro = asset;
  }
  
  TxController.prototype.show = function (req, res) {
      if (req.transaction) {
          res.jsonp(req.transaction);
      }
  };
  
  /**
   * Find transaction by hash ...
   */
  TxController.prototype.transaction = function (req, res, next) {
      var self = this;
      var txid = req.params.txid;
  
      this.node.getDetailedTransaction(txid, function (err, transaction) {
  
          if (err && err.code === -5) {
              return self.common.handleErrors(null, res);
          } else if (err) {
              return self.common.handleErrors(err, res);
          }
          self.transformTransaction(transaction, function (err, transformedTransaction) {
              if (err) {
                  return self.common.handleErrors(err, res);
              }
              req.transaction = transformedTransaction;
              next();
          });
      });
  };
  
  TxController.prototype.transformTransaction = function (transaction, options, callback) {
      var self = this;
      if (_.isFunction(options)) {
          callback = options;
          options = {};
      }
      $.checkArgument(_.isFunction(callback));
  
      var confirmations = 0;
      if (transaction.height >= 0) {
          confirmations = this.node.services.bitcoind.height - transaction.height + 1;
      }
  
      var transformed = {
          txid: transaction.hash,
          version: transaction.version,
          locktime: transaction.locktime
      };
  
      if (transaction.coinbase) {
          transformed.vin = [
              {
                  coinbase: transaction.inputs[0].script,
                  sequence: transaction.inputs[0].sequence,
                  n: 0
              }
          ];
      } else {
          transformed.vin = transaction.inputs.map(this.transformInput.bind(this, options));
      }
      transformed.vout = transaction.outputs.map(this.transformOutput.bind(this, options));
  
      transformed.blockhash = transaction.blockHash;
      transformed.blockheight = transaction.height;
      transformed.confirmations = confirmations;
      // TODO consider mempool txs with receivedTime?
      // var time = transaction.blockTimestamp ? transaction.blockTimestamp : Math.round(Date.now() / 1000);
      transformed.time = transaction.blockTimestamp ? transaction.blockTimestamp : Math.round(Date.now() / 1000);
      if (transformed.confirmations) {
          transformed.blocktime = transformed.time;
      }
  
      if (transaction.coinbase) {
          transformed.isCoinBase = true;
      }
  
      transformed.size = transaction.hex.length / 2; // in bytes
  
      transformed.txlock = transaction.txlock;
      transformed.assetSend = 0;
  
      transformed.inputSatoshis = 0;
      transformed.outputSatoshis = 0;
      transformed.valueOut = 0;
  
      // 解析reverse 中的数据
      self.outputAnalysisReserve(transformed, function (err, outputs) {
          if (!transaction.coinbase) {
              transformed.fees = parseFloat(Decimal(transformed.inputSatoshis).sub(transformed.outputSatoshis)).toString();  //费用
              transformed.valueIn = transformed.inputSatoshis;
          }
          transformed.valueOut = transformed.outputSatoshis;
          self.node.getDepositFlag(function (flag, beforeHeight) {
              if (flag) {
                  if (transformed.blockheight > 0 && transformed.blockheight < beforeHeight) {
                      transformed.vout.forEach(function (v,i) {
                          if (v.spentTxId == null) {
                              transformed.vout[i].DepositFlag = flag;
                          }else{
                              transformed.vout[i].DepositFlag = !flag;
                          }
                      });
                  }
              }
              transformed.assetSend = convertNum(transformed.assetSend);
              callback(null, transformed);
          });
      });
  };
  
  //处理资产问题
  TxController.prototype.outputAnalysisReserve = function (transaction, callback) {
      var self = this;
      var isfrist = true;
      var outputs = [];
      async.eachSeries(transaction.vout, function (output, callback) {
          if (output.reserve && output.reserve.length > 8) {
              output.value = Decimal(output.value || 0).toString();
              var object = self.analysisReserve(output.reserve);  //获取资产类型
              output.originalReserve = output.reserve;    //保存原始 reserve 数据
              //如果不是发行资产
              if (object.appType > 200 && object.appType < 300) {
                  //获取资产详情，拿到资产名称
                  self.getAsssetInfo(object.assetId, function (err, assetObj) {
                      if (err) {
                          return callback(err);
                      }
                      output.reserve = {
                          shortName: assetObj.assetShortName,     //资产简称
                          appType: object.appType,                //应用类型
                          assetId: output.assetId,
                          remarks: object.remarks
                      };
                      delete output.assetId;
                      transaction.assetSend = Decimal(transaction.assetSend).add(output.value).toString();
                      if (outputs[0] && outputs[0].originalReserve) {   //将appType 小的优先放到前面
                          if (outputs[0].reserve.appType < output.reserve.appType) {
                              if (outputs.length > 1) {
                                  var arrayb = outputs.splice(1);
                                  outputs.push(output);
                                  outputs = outputs.concat(arrayb);
                              } else {
                                  outputs.push(output);
                              }
                          } else {
                              outputs.unshift(output);
                          }
                      } else {
                          outputs.unshift(output);
                      }
                      if (object.appType >= 202 && object.appType < 300 && isfrist) { //如果是转让，糖果，销毁等
                          self.getAssetTransactionName(transaction, assetObj.assetShortName, function (err, result) {
                              isfrist = false;
                              callback(null);
                          });
                      } else {
                          callback(null);
                      }
                  })
              } else {
                  output.reserve = {
                      shortName: object.shortName,    //资产简称
                      appType: object.appType,
                      assetId: output.assetId,
                      remarks: object.remarks || object.extendData || object.devName //资产描述，外带数据，应用描述
                  };
                  delete output.assetId;
                  if (object.appType == 300 || object.appType < 200) {
                      transaction.outputSatoshis = Decimal(transaction.outputSatoshis).add(output.value).toString();
                  }
                  transaction.assetSend = Decimal(transaction.assetSend).add(output.value).toString();
                  outputs.unshift(output);
                  callback(null);
              }
          } else {
              transaction.outputSatoshis = Decimal(transaction.outputSatoshis).add(output.value).toString();
              outputs.push(output);
              callback(null);
          }
      }, function (err) {
          transaction.vout = outputs;
          if (isfrist && !transaction.isCoinBase) {
              self.getNotAppTypeInputValue(transaction, callback);
          } else {
              callback(null);
          }
      })
  };
  
  //获取资产交易的输入类型
  TxController.prototype.getAssetTransactionName = function (transaction, shortName, callback) {
      var self = this;
      async.eachSeries(transaction.vin, function (input, eachCB) {
          if (input.is_asset) {
              delete input.is_asset;
              input.shortName = shortName;
              self.node.getRawTransactionJson(input.txid, function (err, tx) {
                  if (err) {
                      return eachCB(err);
                  }
                  input.value = tx.vout[input.vout].value;
                  input.addr = tx.vout[input.vout].scriptPubKey.addresses[0];
                  input.assetId = tx.vout[input.vout].assetId;
                  eachCB(null);
              })
          } else {
              delete input.is_asset;
              transaction.inputSatoshis = Decimal(transaction.inputSatoshis).add(input.value).toString();
              eachCB(null);
          }
      }, function (err) {
          if (err) {
              return callback(err);
          }
          callback(err);
      })
  };
  
  TxController.prototype.getNotAppTypeInputValue = function (transaction, callback) {
      var inputs = transaction.vin;
      inputs.forEach(function (v) {
          transaction.inputSatoshis = Decimal(transaction.inputSatoshis).add(v.value).toString();
      });
      callback(null);
  };
  
  //解析reserve
  TxController.prototype.analysisReserve = function (reserse) {
      return aAsset.fromString(reserse);
  };
  
  TxController.prototype.getAsssetInfo = function (assetId, callback) {
      var assetObj = assetContro.assetDetialList[assetId];
      if (assetObj) {
          return callback(null, assetObj);
      } else {
          this.node.getAssetsObj(assetId, function (err, assetInfo) {
              if (err) {
                  return callback(err);
              }
              return callback(null, assetInfo);
          })
      }
  };
  
  TxController.prototype.transformInput = function (options, input, index) {
      // Input scripts are validated and can be assumed to be valid
      var transformed = {
          txid: input.prevTxId,
          vout: input.outputIndex,
          sequence: input.sequence,
          n: index
      };
  
      if (!options.noScriptSig) {
          transformed.scriptSig = {
              hex: input.script
          };
          if (!options.noAsm) {
              transformed.scriptSig.asm = input.scriptAsm;
          }
      }
  
      transformed.addr = input.address;
      // transformed.valueSat = input.satoshis;
      transformed.value = input.satoshis;
      transformed.doubleSpentTxID = null; // TODO
      transformed.is_asset = input.is_asset;
      //transformed.isConfirmed = null; // TODO
      //transformed.confirmations = null; // TODO
      //transformed.unconfirmedInput = null; // TODO
  
      return transformed;
  };
  
  TxController.prototype.transformOutput = function (options, output, index) {
      var transformed = {
          value: output.satoshis,
          n: index,
          scriptPubKey: {
              hex: output.script
          }
      };
  
      if (!options.noAsm) {
          transformed.scriptPubKey.asm = output.scriptAsm;
      }
  
      if (!options.noSpent) {
          transformed.spentTxId = output.spentTxId || null;
          transformed.spentIndex = _.isUndefined(output.spentIndex) ? null : output.spentIndex;
          transformed.spentHeight = output.spentHeight || null;
          transformed.unlockHeight = output.nUnlockHeight || null;
          transformed.reserve = output.vReserve;
          transformed.assetId = output.assetId;
      }
  
      if (output.address) {
          transformed.scriptPubKey.addresses = [output.address];
          var address = bitcore.Address(output.address); //TODO return type from bitcore-node
          transformed.scriptPubKey.type = address.type;
      }
      return transformed;
  };
  
  TxController.prototype.transformInvTransaction = function (transaction) {
      var self = this;
      var valueOut = 0;
      var vout = [];
      var assetSent = 0;
      var assetId = " SAFE";
      var isAsset = false;
      for (var i = 0; i < transaction.outputs.length; i++) {
          var output = transaction.outputs[i];
          if (output.reserve.length > 8) {
              var obj = self.analysisReserve(output.reserve.toString("hex"));
              if (obj.appType > 200 && obj.appType < 300) {
                  var detail = assetContro.assetDetialList[obj.assetId];
                  if (detail) {
                      // var pow = Math.pow(10, detail.assetDecimals - 8);
                      // assetSent = Decimal(assetSent).add(Decimal(output.satoshis).mul(pow));
                      var pow = Math.pow(10, detail.assetDecimals);
                      assetSent = Decimal(assetSent).add(Decimal(output.satoshis).div(pow));
                      assetId = " " + detail.assetShortName;
                  }
              }
          } else {
              valueOut += output.satoshis;
          }
          if (output.script) {
              var address = output.script.toAddress(self.node.network);
              if (address) {
                  var obj = {};
                  obj[address.toString()] = output.satoshis;
                  vout.push(obj);
              }
          }
      }
  
      var isRBF = _.any(_.pluck(transaction.inputs, 'sequenceNumber'), function (seq) {
          return seq < MAXINT - 1;
      });
      valueOut = assetSent > 0 ? assetSent : valueOut / 1e8;
      return {
          txid: transaction.hash,
          valueOut: valueOut,
          vout: vout,
          isRBF: isRBF,
          txlock: false,
          assetId: assetId
      };
  };
  
  TxController.prototype.rawTransaction = function (req, res, next) {
      var self = this;
      var txid = req.params.txid;
  
      this.node.getTransaction(txid, function (err, transaction) {
          if (err && err.code === -5) {
              return self.common.handleErrors(null, res);
          } else if (err) {
              return self.common.handleErrors(err, res);
          }
          req.rawTransaction = {
              'rawtx': transaction.toBuffer().toString('hex')
          };
  
          next();
      });
  };
  
  TxController.prototype.showRaw = function (req, res) {
      if (req.rawTransaction) {
          res.jsonp(req.rawTransaction);
      }
  };
  
  TxController.prototype.list = function (req, res) {
      var self = this;
      var blockHash = req.query.block;
      var address = req.query.address;
      var page = parseInt(req.query.pageNum) || 0;
      var pageLength = 10;
      var pagesTotal = 1;
  
      if (blockHash) {
          self.node.getBlockOverview(blockHash, function (err, block) {
              if (err && err.code === -5) {
                  return self.common.handleErrors(null, res);
              } else if (err) {
                  return self.common.handleErrors(err, res);
              }
  
              var totalTxs = block.txids.length;
              var txids;
  
              if (!_.isUndefined(page)) {
                  var start = page * pageLength;
                  txids = block.txids.slice(start, start + pageLength);
                  pagesTotal = Math.ceil(totalTxs / pageLength);
              } else {
                  txids = block.txids;
              }
              async.mapSeries(txids, function (txid, next) {
                  self.node.getDetailedTransaction(txid, function (err, transaction) {
                      if (err) {
                          return next(err);
                      }
                      self.transformTransaction(transaction, next);
                  });
              }, function (err, transformed) {
                  if (err) {
                      return self.common.handleErrors(err, res);
                  }
  
                  res.jsonp({
                      pagesTotal: pagesTotal,
                      txs: transformed
                  });
              });
          });
      } else if (address) {
          var options = {
              from: page * pageLength,
              to: (page + 1) * pageLength,
              assetsId: req.query.assetsId
          };
  
          self.node.getAddressHistory(address, options, function (err, result) {
              if (err) {
                  return self.common.handleErrors(err, res);
              }
              var txs = result.items.map(function (info) {
                  return info.tx;
              }).filter(function (value, index, self) {
                  return self.indexOf(value) === index;
              });
  
              async.map(
                  txs,
                  function (tx, next) {
                      self.transformTransaction(tx, next);
                  },
                  function (err, transformed) {
                      if (err) {
                          return self.common.handleErrors(err, res);
                      }
                      res.jsonp({
                          pagesTotal: Math.ceil(result.totalCount / pageLength),
                          txs: transformed
                      });
                  }
              );
          });
      } else {
          return self.common.handleErrors(new Error('Block hash or address expected'), res);
      }
  };
  
  TxController.prototype.send = function (req, res) {
      var self = this;
      this.node.sendTransaction(req.body.rawtx, function (err, txid) {
          if (err) {
              // TODO handle specific errors
              return self.common.handleErrors(err, res);
          }
          res.json({'txid': txid});
      });
  };
  
  function convertNum(amount) {
      // 判断是否科学计数法,是则进行转换
      var num = 0;
      var result = [];
      amount = new String(amount);
      if ((amount.indexOf('E') != -1) || (amount.indexOf('e') != -1)) {
          (amount.indexOf('E') != -1) ? num = amount.indexOf('E') : num = amount.indexOf('e');
          var decimal = amount.substr(0, num);
          // devide the sentice efficial number
          var decArr = decimal.split('.', 2);
          // total 10 power
          var power = amount.substr(num + 1, amount.length);
          // symbol + -
          var symbol = power.substr(0, 1);
          power = power.substr(1, power.length);
          power = power * 1.0;
          if ('+' == symbol) {
              // the number of 0 is power - decArr[1]
              power = power - decArr[1].length;
              // the return data
              while (power > 0) {
                  result.unshift(0);
                  power -= 1;
              }
              result.unshift(decArr[1] * 1.0);
              result.unshift(decArr[0] * 1.0);
          } else {
              if (decArr[1]) {
                  power = power - decArr[1].length;
              }
              var result = [0];
              result.push('.');
              while (power > 0) {
                  result.push(0);
                  power -= 1;
              }
              result.push(decArr);
          }
          result.join("");
          result = result + '';
          amount = result.replace(/,/g, '');
      }
      return amount;
  }
  
  module.exports = TxController;