var imports = require('soop').imports();

var hex = function(hex) {return new Buffer(hex, 'hex');};

var fs         = require('fs');
var EncFile    = require('./util/EncFile');
var Address    = require('./Address');
var networks   = require('./networks');
var util       = imports.util || require('./util/util');
var ENC_METHOD = 'aes-256-cbc';

var skeleton = {
  client: 'libcoin',
  client_version: '0.0.1',
  network: 'testnet',
  version: 1,
  best_hash: null,
  best_height: -1,
  keys: [],
  sin: {},
  scripts: {},
};

function Wallet(cfg) {
  if (typeof cfg !== 'object')
    cfg = {};

  // deep copy (no references)
  if (cfg.datastore)
    this.datastore = JSON.parse(JSON.stringify(cfg.datastore));
  else
    this.datastore = JSON.parse(JSON.stringify(skeleton));

  this.network = undefined;
  this.dirty = cfg.dirty || true;
};

Wallet.prototype.readSync = function(filename, passphrase) {
  this.datastore = EncFile.readJFileSync(ENC_METHOD,
                  passphrase, filename);
  this.dirty = false;
};

Wallet.prototype.writeSync = function(filename, passphrase) {
  var tmp_fn = filename + ".tmp";

  EncFile.writeJFileSync(ENC_METHOD, passphrase, tmp_fn,
              this.datastore);
  fs.renameSync(tmp_fn, filename);

  this.dirty = false;
};

Wallet.prototype.setNetwork = function(netname) {
  if (!netname)
    netname = this.datastore.network;

  switch (netname) {
  case "mainnet":
  case "livenet":
    this.network = networks.livenet;
    break;
  case "testnet":
    this.network = networks.testnet;
    break;
  default:
    throw new Error("Unsupported network");
  }

  // store+canonicalize name
  this.datastore['network'] = this.network.name;
  this.dirty = true;
};

Wallet.prototype.addKey = function(wkey) {
  this.datastore.keys.push(wkey);
  this.dirty = true;
};

Wallet.prototype.addSIN = function(sinObj) {
  this.datastore.sin[sinObj.sin] = sinObj;
  this.dirty = true;
};

Wallet.prototype.findKeyHash = function(pubKeyHash) {
  var pkhStr = pubKeyHash.toString();

  for (var i = 0; i < this.datastore.keys.length; i++) {
    var obj = this.datastore.keys[i];
    var addrStr = obj.addr;
    var addr = new Address(addrStr);
    if (addr.payload().toString() == pkhStr)
      return obj;
  }

  return undefined;
};

Wallet.prototype.expandKey = function(key) {
  var addr = new Address(key);
  var isAddr = true;

  try {
    addr.validate();
    var b = addr.payload();
    var obj = this.findKeyHash(b);
    key = obj.pub;
  } catch(e) {
    // do nothing
  }

  var re = /^[a-fA-F0-9]+$/;
  if (!key.match(re))
    throw new Error("Unknown key type");
  return hex(key);
};

Wallet.prototype.expandKeys = function(keys) {
  var res = [];
  var us = this;
  keys.forEach(function(key) {
    var expKey = us.expandKey(key);
    res.push(expKey);
  });
  return res;
};

Wallet.prototype.addScript = function(script) {
  var buf = script.getBuffer();
  var hash = util.sha256ripe160(buf);
  var addr = new Address(this.network.addressScript, hash);
  var addrStr = addr.as('base58');
  this.datastore.scripts[addrStr] = buf.toString('hex');
  this.dirty = true;

  return addrStr;
};

module.exports = require('soop')(Wallet);