/**
 * @license
 * Copyright Google Inc. All Rights Reserved.
 *
 * Use of this source code is governed by an MIT-style license that can be
 * found in the LICENSE file at https://angular.io/license
 */

const cldr = require('cldr');
// locale list
const locales = cldr.localeIds;
const langToRule = {};
const ruleToLang = {};
const variants = [];
const localeToVariant = {};
const DEFAULT_RULE = `function anonymous(n\n/**/) {\nreturn"other"\n}`;
const EMPTY_RULE = `function anonymous(n\n/**/) {\n\n}`;

locales.forEach(locale => {
  const rule = normalizeRule(cldr.extractPluralRuleFunction(locale).toString());
  const lang = getVariantLang(locale, rule);

  if (!lang || !rule) {
    return;
  }

  if (!ruleToLang[rule]) {
    ruleToLang[rule] = [];
  } else if (ruleToLang[rule].indexOf(lang) > -1) {
    return;
  }

  ruleToLang[rule].push(lang);
});

let nextVariantCode = 'a'.charCodeAt(0);

variants.forEach(locale => {
  const rule = normalizeRule(cldr.extractPluralRuleFunction(locale).toString());
  if (!rule) {
    return;
  }

  let mapTo = null;

  if (ruleToLang[rule]) {
    mapTo = ruleToLang[rule][0];
    localeToVariant[locale] = mapTo;
    return;
  }

  if (!mapTo) {
    mapTo = '_' + String.fromCharCode(nextVariantCode++);

    langToRule[mapTo] = rule;
    ruleToLang[rule] = [mapTo];
    localeToVariant[locale] = mapTo;
  }
});

console.log(generateCode());

function generateCode() {
  checkMapping();

  return `
// This is generated code DO NOT MODIFY
// see angular/script/cldr/gen_plural_rules.js

/** @experimental */
export enum Plural {
  Zero,
  One,
  Two,
  Few,
  Many,
  Other,
}
` + generateVars() +
    generateRules() + `
}`;
}

function generateRules() {
  const codeParts = [`
const lang = locale.split('-')[0].toLowerCase();

switch (lang) {`];

  Object.keys(ruleToLang).forEach(rule => {
    const langs = ruleToLang[rule];
    codeParts.push(...langs.map(l => `  case '${l}': `));
    codeParts.push(`    ${rule}`);
  });

  codeParts.push(`  // When there is no specification, the default is always other
  // see http://cldr.unicode.org/index/cldr-spec/plural-rules
  // "other (required—general plural form — also used if the language only has a single form)"
  default:
    return Plural.Other;
}`);

  return codeParts.join('\n');
}

function generateVars(){
  return `
/**
 * Returns the plural case based on the locale
 *
 * @experimental
 */
export function getPluralCase(locale: string, nLike: number | string): Plural {
// TODO(vicb): lazy compute
if (typeof nLike === 'string') {
  nLike = parseInt(<string>nLike, 10);
}
const n: number = nLike as number;
const nDecimal = n.toString().replace(/^[^.]*\\.?/, ''); 
const i = Math.floor(Math.abs(n));
const v = nDecimal.length;
const f = parseInt(nDecimal, 10); 
const t = parseInt(n.toString().replace(/^[^.]*\\.?|0+$/g,''), 10) || 0;
`;
}

function checkMapping() {
  if (localeToVariant.length) {
    console.log(`Mapping required:`);
    console.log(localeToVariant);
    throw new Error('not implemented');
  }
}

/**
 * If the language rule do not match an existing language rule, flag it as variant and handle it at the end
 */
function getVariantLang(locale, rule) {
  let lang = locale.split('_')[0];

  if (!langToRule[lang]) {
    langToRule[lang] = rule;
    return lang;
  }

  if (langToRule[lang] === rule) {
    return lang;
  }

  variants.push(locale);
  return null;
}

function normalizeRule(fn) {
  if (fn === DEFAULT_RULE || fn === EMPTY_RULE) return;

  return fn
    .replace(toRegExp('function anonymous(n\n/**/) {\n'), '')
    .replace(toRegExp('var'), 'let')
    .replace(toRegExp('"zero"'), ' Plural.Zero')
    .replace(toRegExp('"one"'), ' Plural.One')
    .replace(toRegExp('"two"'), ' Plural.Two')
    .replace(toRegExp('"few"'), ' Plural.Few')
    .replace(toRegExp('"many"'), ' Plural.Many')
    .replace(toRegExp('"other"'), ' Plural.Other')
    .replace(toRegExp('\n}'), '')
    .replace(toRegExp('let'), '')
    .replace(toRegExp('if(typeof n==="string")n=parseInt(n,10);'), '')
    .replace(toRegExp('i=Math.floor(Math.abs(n))'), '')
    .replace(/v=n.toString.*?.length/g, '')
    .replace(/f=parseInt.*?\|\|0/g, '')
    .replace(/t=parseInt.*?\|\|0/g, '')
    .replace(/^[ ,;]*/, '')
  + ';';
}

function toRegExp(s) {
  return new RegExp(s.replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1'), 'g');
}