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

function ClassSpec(b) {
  var fs = require('fs');
  var EncFile = require('./util/EncFile');
  var Address = require('./Address').class();
  var networks = require('./networks');
    var util = b.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;
  };

  return Wallet;
};
module.defineClass(ClassSpec);