var names = require('color-name');
var util = require('util');
var fs = require('fs');
var s = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
var nums = '0123456789';
var i, j, k, t;
var tokens = {};
var keys = Object.keys(names);
var startWithLetter = {};

function addToken(tok, letter) {
  if (tok.length < 2) {
    return;
  }
  tokens[tok] = (tokens[tok] || 0) + 1;
  if (letter) {
    startWithLetter[tok] = true;
  }
}

function getKey(tok) {
  var key;
  if (!s.length) {
    return null;
  }
  if (startWithLetter[tok]) {
    key = s[s.length - 1];
    if (nums.indexOf(key) === -1) {
      s = s.substring(0, s.length - 1);
      return key;
    }
    return null;
  }
  key = s[0];
  s = s.substring(1);
  return key;
}

function tokenize() {
  // lets loop those keys, and add count every possible substring
  for (i = 0; i < keys.length; i++) {
    var key = keys[i];
    for (j = 0; j < key.length; j++) {
      if (s.indexOf(key[j]) >= 0) {
        s = s.replace(key[j], '');
      }
      if (j > 1 && j < key.length) {
        for (k = 0; k < key.length - j; k++) {
          addToken(key.substring(k, k + j), k === 0);
        }
      }
    }
  }
}

function calcSavings() {
  // now calculate possible saved bytes per substring
  var tkeys = Object.keys(tokens);
  var ar = [];
  for (i = 0; i < tkeys.length; i++) {
    k = tkeys[i];
    t = tokens[k];
    j = k.length * (t - 1) - 6; // substring length * (count - 1) - 4 [x:substring,]
    if (j > 0) {
      ar.push([k, j]);
    }
  }
  // sort by most savings
  return ar.sort(function(a, b) {
    return a[1] > b[1] ? -1 : a[1] < b[1] ? 1 : 0;
  });
}

function createMap(sorted) {
  // build final map, starting from greatest savings
  var final = {};
  for (i = 0; i < sorted.length; i++) {
    var x = sorted[i][0];
    var add = true;
    var fk = Object.keys(final);
    for (j = 0; j < fk.length; j++) {
      if (final[fk[j]].indexOf(x) >= 0 || x.indexOf(final[fk[j]]) >= 0) {
        // if substring of this key already exists, do not add
        add = false;
        break;
      }
    }
    if (add) {
      k = x.length * (tokens[x] - 1) - 6;
      if (k > 0) {
        var ix = getKey(x);
        if (ix) {
          // we have suitable keys left for this substring, add it to final map
          final[ix] = x;
        }
      }
    }
  }
  return final;
}

function mangle(map) {
  var tkeys = Object.keys(map);
  var result = {};
  var nk, key;
  for (i = 0; i < keys.length; i++) {
    nk = key = keys[i];
    for (j = 0; j < tkeys.length; j++) {
      k = tkeys[j];
      nk = nk.replace(map[k], k);
    }
    result[nk] = key;
  }
  return result;
}

function compress(map) {
  var packed = {};
  var v, nk, key;
  keys = Object.keys(map);
  for (i = 0; i < keys.length; i++) {
    nk = keys[i];
    key = map[nk];
    v = names[key];
    packed[nk] = (0xFFFFFF && v[0] << 16 | v[1] << 8 | v[2]).toString(16);
  }
  return packed;
}

tokenize();
var sorted = calcSavings();
var mapped = createMap(sorted);
var mangled = mangle(mapped);
var packed = compress(mangled);
var unpack = `
const map = ${util.inspect(mapped).replace(/ {2}/g, '\t')};
const names = ${util.inspect(packed).replace(/ {2}/g, '\t')};
export default function unpack() {
  const unpacked = {};
  const keys = Object.keys(names);
  const tkeys = Object.keys(map);
  let i, j, k, ok, nk;
  for (i = 0; i < keys.length; i++) {
    ok = nk = keys[i];
    for (j = 0; j < tkeys.length; j++) {
      k = tkeys[j];
      nk = nk.replace(k, map[k]);
    }
    k = parseInt(names[ok], 16);
    unpacked[nk] = [k >> 16 & 0xFF, k >> 8 & 0xFF, k & 0xFF];
  }
  return unpacked;
}
`;
fs.writeFileSync('./packed.js', unpack, 'utf-8');
