<commit_before>
'use strict';

var extend = require('extend');
var inherits = require('util').inherits;
var typeforce = require('typeforce');
var omit = require('object.omit');
var utils = require('./utils');
var stringify = require('tradle-utils').stringify;
var find = utils.find;
var Keys = require('./keys');
var Key = require('./keys/key');
var toKey = require('./toKey');
var assert = require('assert');
var arrayMethods = ['some', 'forEach', 'map', 'reduce'];
// var DEFAULT_KEY_CATEGORY = 'identity';
var TYPE = 'tradle.Identity';
var KEYS_KEY = 'pubkeys';
var Types = require('./sectionTypes');
var SIMPLE_PROPS = ['name', 'location'];
var PROP_TO_CL = {
  contact: Types.Contact,
  websites: Types.Website,
  photos: Types.Photo,
  payments: Types.Payment,
  profiles: Types.Profile,
  statements: Types.Statement,
  connections: Types.Connection
};

// https://github.com/openname/specifications/blob/master/profiles/profiles-v03.md
var RESERVED_KEYS = [
  'name', 'location', 'summary', 'websites', 'contact', 'photos',
  'pubkeys', 'payments', 'profiles', 'connections', 'statements',
  'auth', 'v'
];

function Identity() {
  this._props = {};
  this._keys = [];
}

Identity.TYPE = TYPE;
Identity.VERSION = '0.3';

Identity.prototype.set = function(prop, val) {
  this._props[prop] = val;
  return this;
}

Identity.prototype.summary = function(val) {
  if (typeof val === 'undefined') return this._props.summary;
  else this.set('summary', val);

  return this;
}

Identity.prototype.keys = function(type) {
  if (!type) return this._keys;

  return this._keys.filter(function(key) {
    return key.type() === type;
  });
}

Identity.prototype.addKey = function(key) {
  key = toKey(key);
  assert(key instanceof Key);

  var foundDuplicate = this._keys.some(function(dup) {
    return dup.equals(key);
  });

  if (foundDuplicate) throw new Error('duplicate key');

  this._keys.push(key);
  return this;
}

Identity.prototype.exportKeys = function(exportPrivateKeys) {
  return this._keys.map(function(key) {
    return key.toJSON(exportPrivateKeys);
  });
}

/**
 * get the private key for a given public key
 * @param  {String|Key} key - public key
 * @return {KeyPair} the corresponding private key if this Identity has it, undefined otherwise
 */
Identity.prototype.getPrivateKey = function(key) {
  var pubKeyStr = typeof key === 'string' ? key : key.pubKeyString();
  return find(this._keys, function(k) {
    return k.pubKeyString() === pubKeyStr && k.priv() && k;
  });
}

Identity.verify = function(data, key, signature) {
  return key.verify(stringify(data), signature);
}

Identity.prototype.sign = function(data, pubKey) {
  var key = this.getPrivateKey(pubKey);

  if (!key) throw new Error('key not found');

  return key.sign(data);
}

/**
 * export identity to json
 * @param  {Boolean} exportPrivateKeys - include private keys in exported json
 * @return {Object}
 */
Identity.prototype.toJSON = function(exportPrivateKeys) {
  var props = extend({
    _type: Identity.TYPE
  }, this._props);

  props[KEYS_KEY] = this.exportKeys(exportPrivateKeys);
  props.v = Identity.VERSION;
  return props;
}

/**<commit_msg>fix toJSON to serialize nested classes<commit_after>
'use strict';

var extend = require('extend');
var inherits = require('util').inherits;
var typeforce = require('typeforce');
var omit = require('object.omit');
var utils = require('./utils');
var stringify = require('tradle-utils').stringify;
var find = utils.find;
var Keys = require('./keys');
var Key = require('./keys/key');
var toKey = require('./toKey');
var assert = require('assert');
var arrayMethods = ['some', 'forEach', 'map', 'reduce'];
// var DEFAULT_KEY_CATEGORY = 'identity';
var TYPE = 'tradle.Identity';
var KEYS_KEY = 'pubkeys';
var Types = require('./sectionTypes');
var SIMPLE_PROPS = ['name', 'location'];
var PROP_TO_CL = {
  contact: Types.Contact,
  websites: Types.Website,
  photos: Types.Photo,
  payments: Types.Payment,
  profiles: Types.Profile,
  statements: Types.Statement,
  connections: Types.Connection
};

// https://github.com/openname/specifications/blob/master/profiles/profiles-v03.md
var RESERVED_KEYS = [
  'name', 'location', 'summary', 'websites', 'contact', 'photos',
  'pubkeys', 'payments', 'profiles', 'connections', 'statements',
  'auth', 'v'
];

function Identity() {
  this._props = {};
  this._keys = [];
}

Identity.TYPE = TYPE;
Identity.VERSION = '0.3';

Identity.prototype.set = function(prop, val) {
  this._props[prop] = val;
  return this;
}

Identity.prototype.summary = function(val) {
  if (typeof val === 'undefined') return this._props.summary;
  else this.set('summary', val);

  return this;
}

Identity.prototype.keys = function(type) {
  if (!type) return this._keys;

  return this._keys.filter(function(key) {
    return key.type() === type;
  });
}

Identity.prototype.addKey = function(key) {
  key = toKey(key);
  assert(key instanceof Key);

  var foundDuplicate = this._keys.some(function(dup) {
    return dup.equals(key);
  });

  if (foundDuplicate) throw new Error('duplicate key');

  this._keys.push(key);
  return this;
}

Identity.prototype.exportKeys = function(exportPrivateKeys) {
  return this._keys.map(function(key) {
    return key.toJSON(exportPrivateKeys);
  });
}

/**
 * get the private key for a given public key
 * @param  {String|Key} key - public key
 * @return {KeyPair} the corresponding private key if this Identity has it, undefined otherwise
 */
Identity.prototype.getPrivateKey = function(key) {
  var pubKeyStr = typeof key === 'string' ? key : key.pubKeyString();
  return find(this._keys, function(k) {
    return k.pubKeyString() === pubKeyStr && k.priv() && k;
  });
}

Identity.verify = function(data, key, signature) {
  return key.verify(stringify(data), signature);
}

Identity.prototype.sign = function(data, pubKey) {
  var key = this.getPrivateKey(pubKey);

  if (!key) throw new Error('key not found');

  return key.sign(data);
}

/**
 * export identity to json
 * @param  {Boolean} exportPrivateKeys - include private keys in exported json
 * @return {Object}
 */
Identity.prototype.toJSON = function(exportPrivateKeys) {
  var props = {
    _type: Identity.TYPE
  }

  for (var p in this._props) {
    var val = this._props[p];
    if (p in PROP_TO_CL) {
      if (val) {
        props[p] = this._props[p].map(function(i) { return i.toJSON() });
      }
    }
    else {
      props[p] = extend(true, {}, this._props[p]);
    }
  }

  props[KEYS_KEY] = this.exportKeys(exportPrivateKeys);
  props.v = Identity.VERSION;
  return props;
}

/**
{'fraction_non_alphanumeric': 0.161005331302361, 'fraction_numerical': 0.0012185833968012185, 'mean_word_length': 4.240223463687151, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4353004', 'n_tokens_mistral': 2133, 'n_tokens_neox': 2100, 'n_words': 527}