import { L20nIntl } from './shims';

class FTLBase {
  constructor(value, opts) {
    this.value = value;
    this.opts = opts;
  }
  valueOf() {
    return this.value;
  }
}

export class FTLNumber extends FTLBase {
  constructor(value, opts) {
    super(parseFloat(value), opts);
  }
  toString(rc) {
    const nf = rc.ctx._memoizeIntlObject(
      L20nIntl.NumberFormat, rc.lang, this.opts
    );
    return nf.format(this.value);
  }
}

export class FTLDateTime extends FTLBase {
  constructor(value, opts) {
    super(new Date(value), opts);
  }
  toString(rc) {
    const dtf = rc.ctx._memoizeIntlObject(
      L20nIntl.DateTimeFormat, rc.lang, this.opts
    );
    return dtf.format(this.value);
  }
}

export class FTLKeyword extends FTLBase {
  toString() {
    const { name, namespace } = this.value;
    return namespace ? `${namespace}:${name}` : name;
  }
  match(rc, other) {
    const { name, namespace } = this.value;
    if (other instanceof FTLKeyword) {
      return name === other.value.name && namespace === other.value.namespace;
    } else if (namespace) {
      return false;
    } else if (typeof other === 'string') {
      return name === other;
    } else if (other instanceof FTLNumber) {
      const pr = rc.ctx._memoizeIntlObject(
        L20nIntl.PluralRules, rc.lang, other.opts
      );
      return name === pr.select(other.valueOf());
    }
  }
}

export class FTLList extends Array {
  constructor(arr = [], opts) {
    super(arr.length);
    this.opts = opts;
    for (let [index, elem] of arr.entries()) {
      this[index] = elem;
    }
  }
  toString(rc) {
    const lf = rc.ctx._memoizeIntlObject(
      L20nIntl.ListFormat, rc.lang, this.opts
    );
    const elems = this.map(
      elem => elem.toString(rc)
    );
    return lf.format(elems);
  }
  concat(elem) {
    return new FTLList([...this, elem]);
  }
}

// each builtin takes two arguments:
//  - args = an array of positional args
//  - opts  = an object of key-value args

export default {
  'NUMBER': ([arg], opts) => new FTLNumber(arg.valueOf(), valuesOf(opts)),
  'PLURAL': ([arg], opts) => new FTLNumber(arg.valueOf(), valuesOf(opts)),
  'DATETIME': ([arg], opts) => new FTLDateTime(arg.valueOf(), valuesOf(opts)),
  'LIST': (args) => new FTLList(args),
  'LEN': ([arg], opts) => new FTLNumber(arg.valueOf().length, valuesOf(opts)),
  'TAKE': ([num, arg], opts) =>
    new FTLList(arg.value.slice(0, num.value), valuesOf(opts)),
  'DROP': ([num, arg], opts) =>
    new FTLList(arg.value.slice(num.value), valuesOf(opts)),
};

function valuesOf(opts) {
  return Object.keys(opts).reduce(
    (seq, cur) => Object.assign({}, seq, {
      [cur]: opts[cur].valueOf()
    }), {});
}

@@ -1418,168 +1418,8 @@
 y %7B%0A
-  constructor(arr = %5B%5D, opts) %7B%0A    super(arr.length);%0A    this.opts = opts;%0A    for (let %5Bindex, elem%5D of arr.entries()) %7B%0A      this%5Bindex%5D = elem;%0A    %7D%0A  %7D%0A
   to
@@ -1499,33 +1499,43 @@
 tFormat, rc.lang
-,
+ // XXX add
  this.opts%0A    )
@@ -1664,27 +1664,28 @@
  return 
-new 
 FTLList
+.from
 (%5B...thi
@@ -2073,47 +2073,8 @@
 )),%0A
-  'LIST': (args) =%3E new FTLList(args),%0A
   'L
@@ -2155,52 +2155,78 @@
 %0A  '
-TAKE': (%5Bnum, arg%5D, opts) =%3E%0A    new
+LIST': (args) =%3E FTLList.from(args),%0A  'TAKE': (%5Bnum, arg%5D) =%3E
  FTLList
 (arg
@@ -2213,32 +2213,37 @@
 arg%5D) =%3E FTLList
+.from
 (arg.value.slice
@@ -2232,24 +2232,28 @@
 om(arg.value
+Of()
 .slice(0, nu
@@ -2260,32 +2260,16 @@
 m.value)
-, valuesOf(opts)
 ),%0A  'DR
@@ -2288,34 +2288,20 @@
 arg%5D
-, opts) =%3E%0A    new
+) =%3E
  FTLList
 (arg
@@ -2296,16 +2296,21 @@
  FTLList
+.from
 (arg.val
@@ -2311,16 +2311,20 @@
 rg.value
+Of()
 .slice(n
@@ -2332,32 +2332,16 @@
 m.value)
-, valuesOf(opts)
 ),%0A%7D;%0A%0Af

Make FTLList's constructor compatible with Array's