var levn, ref$, deepEqual, equal, throws, isItNaN, q, toString$ = {}.toString;
levn = require('../..');
ref$ = require('assert'), deepEqual = ref$.deepEqual, equal = ref$.strictEqual, throws = ref$.throws;
isItNaN = require('prelude-ls').isItNaN;
q = function(type, input, expected, options){
  var result;
  result = levn.parse(type, input, options);
  equal(toString$.call(result).slice(8, -1), toString$.call(expected).slice(8, -1));
  if (isItNaN(expected)) {
    return isItNaN(result);
  } else {
    switch (toString$.call(expected).slice(8, -1)) {
    case 'Array':
    case 'Object':
      return deepEqual(result, expected);
    case 'Date':
      return equal(result.getTime(), expected.getTime());
    case 'RegExp':
      return equal(result.toString(), expected.toString());
    default:
      return equal(result, expected);
    }
  }
};
describe('cast', function(){
  it('Undefined', function(){
    q('Undefined', 'undefined', void 8);
    return throws(function(){
      return q('Undefined', 'null');
    }, /Value "null" does not type check against/);
  });
  it('Null', function(){
    q('Null', 'null', null);
    return throws(function(){
      return q('Null', 'undefined');
    }, /Value "undefined" does not type check against/);
  });
  it('NaN', function(){
    q('NaN', 'NaN', NaN);
    return throws(function(){
      return q('NaN', '1');
    }, /Value "1" does not type check against/);
  });
  it('Boolean', function(){
    q('Boolean', 'true', true);
    q('Boolean', 'false', false);
    return throws(function(){
      return q('Boolean', '0');
    }, /Value "0" does not type check against/);
  });
  it('Number', function(){
    q('Number', '2', 2);
    q('Number', '-2', -2);
    q('Number', '2.1', 2.1);
    q('Number', '-2.1', -2.1);
    return throws(function(){
      return q('Number', 'NaN');
    }, /Value "NaN" does not type check against/);
  });
  it('Int', function(){
    q('Int', '2', 2);
    q('Int', '-2', -2);
    throws(function(){
      return q('Int', '2.1');
    }, /Value "2.1" does not type check against/);
    return throws(function(){
      return q('Int', 'NaN');
    }, /Value "NaN" does not type check against/);
  });
  it('Float', function(){
    q('Float', '2', 2);
    q('Float', '-2', -2);
    q('Float', '2.1', 2.1);
    q('Float', '-2.1', -2.1);
    return throws(function(){
      return q('Float', 'NaN');
    }, /Value "NaN" does not type check against/);
  });
  it('Date', function(){
    q('Date', '2011-11-11', new Date('2011-11-11'));
    q('Date', '#2011-11-11#', new Date('2011-11-11'));
    q('Date', '1320969600000', new Date('2011-11-11'));
    q('Date', '#1320969600000#', new Date('2011-11-11'));
    return throws(function(){
      return q('Date', '#2011-13#');
    }, /Value "#2011-13#" does not type check against/);
  });
  it('RegExp', function(){
    q('RegExp', 'hi', /hi/);
    q('RegExp', '/hi/', /hi/);
    q('RegExp', '/h\\/i/', /h\/i/);
    q('RegExp', '/hi/ig', /hi/ig);
    return q('RegExp', '/^(hi)|[a-zA-Z]*:there$/g', /^(hi)|[a-zA-Z]*:there$/g);
  });
  it('Array', function(){
    q('Array', '[1,2,3]', [1, 2, 3]);
    q('Array', '1,2,3', [1, 2, 3]);
    q('Array', '"hi"', ["hi"]);
    return q('Array', '[]', []);
  });
  it('Object', function(){
    q('Object', '{x: 2, y: hello}', {
      x: 2,
      y: 'hello'
    });
    q('Object', 'x: 2, y: hello', {
      x: 2,
      y: 'hello'
    });
    return q('Object', '{}', {});
  });
  it('JSON is valid Object', function(){
    var json;
    json = '{\n     "key": "object",\n     "empty": false,\n     "time_nano": 19608,\n     "validate": {"b": true},\n     "sizes": [1, 2, 3]\n}';
    return q('Object', json, {
      key: 'object',
      empty: false,
      time_nano: 19608,
      validate: {
        b: true
      },
      sizes: [1, 2, 3]
    });
  });
  it('String', function(){
    q('String', '2', '2');
    q('String', 'one two three', 'one two three');
    q('String', 'blah "hi \\" there:"', 'blah "hi \\" there:"');
    q('String', "blah 'hi \\' \\' there:'", "blah 'hi \\' \\' there:'");
    q('String', '[2]', '[2]');
    return q('String', '{2: [], ()}', '{2: [], ()}');
  });
  it('String using quotes', function(){
    q('String', "'one[two]three'", '\'one[two]three\'');
    q('String', '"before"after"', '"before"after"');
    q('String', '"hi"', '"hi"');
    return q('String', '"h\n\ni"', '"h\n\ni"');
  });
  it('multiple', function(){
    q('Number | String', '2', 2);
    return q('Number | String', 'str', 'str');
  });
  it('array', function(){
    it('regular', function(){
      return q('[Number]', '[1, 2, 3]', [1, 2, 3]);
    });
    it('children', function(){
      return q('[String]', '[1, hi, 3]', ['1', 'hi', '3']);
    });
    it('no delimiters', function(){
      return q('[Number]', '1, 2, 3', [1, 2, 3]);
    });
    it('space after comma with string content', function(){
      return q('[String]', 'node, browser', ['node', 'browser']);
    });
    it('trailing comma', function(){
      return q('[Number]', '[1, 2, 3,]', [1, 2, 3]);
    });
    it('empty', function(){
      return q('[Number]', '[]', []);
    });
    it('nested', function(){
      q('[[Number]]', '[[1, 2],[3,4],[5,6]]', [[1, 2], [3, 4], [5, 6]]);
      return q('[[Number]]', '[1,2],[3,4],[5,6]', [[1, 2], [3, 4], [5, 6]]);
    });
    return it('nope', function(){
      return throws(function(){
        return q('[Number]', '[hi, there]');
      }, /Value "hi" does not type check against/);
    });
  });
  it('tuple', function(){
    it('simple', function(){
      q('(Number, String)', '(2, hi)', [2, 'hi']);
      q('(Number, Boolean)', '(2, false)', [2, false]);
      return q('(Number, Null)', '(2, null)', [2, null]);
    });
    it('no delimiters', function(){
      return q('(Number, String)', '2, hi', [2, 'hi']);
    });
    it('trailing comma', function(){
      return q('(Number, String)', '(2, hi,)', [2, 'hi']);
    });
    it('nested', function(){
      return q('((Boolean, String), Number)', '(true, hi), 2', [[true, 'hi'], 2]);
    });
    it('attempt to cast non-array', function(){
      q('(Number, String) | Number', '(2, hi)', [2, 'hi']);
      return q('(Number, String) | Number', '2', 2);
    });
    it('maybe', function(){
      q('(Number, Maybe String)', '(2)', [2]);
      q('(Number, Maybe String)', '2', [2]);
      q('(Number, Maybe String)', '(2, undefined)', [2]);
      return q('(Number, Maybe String)', '2,undefined', [2]);
    });
    return it('nope', function(){
      throws(function(){
        return q('(Number, String)', '(hi, 2)');
      }, /Value "hi" does not type check against/);
      throws(function(){
        return q('(Number, String)', '(2)');
      }, /does not type check/);
      return throws(function(){
        return q('(Number, Number)', '(1,2,3)');
      }, /does not type check/);
    });
  });
  it('fields', function(){
    it('basic', function(){
      return q('{x: Number}', '{x: 2}', {
        x: 2
      });
    });
    it('no delimiters', function(){
      return q('{x: Number}', 'x: 2', {
        x: 2
      });
    });
    it('trailing comma', function(){
      return q('{x: Number}', '{x: 2,}', {
        x: 2
      });
    });
    it('multiple keys', function(){
      return q('{x: Number, y: String}', '{x: 2, y: yo}', {
        x: 2,
        y: 'yo'
      });
    });
    it('nested', function(){
      return q('{obj: {z: String}, x: {y: Boolean}}', 'obj: {z: hi}, x: {y: true}', {
        obj: {
          z: 'hi'
        },
        x: {
          y: true
        }
      });
    });
    it('etc', function(){
      return q('{x: Number, ...}', '{x: 2, y: hi}', {
        x: 2,
        y: 'hi'
      });
    });
    it('maybe', function(){
      return q('{x: Number, y: Maybe String}', '{x: 2}', {
        x: 2
      });
    });
    it('with type', function(){
      return q('RegExp{source: String}', '/[a-z]/g', /[a-z]/g);
    });
    return it('nope', function(){
      throws(function(){
        return q('{x: Number}', '{x: hi}');
      }, /Value "hi" does not type check against/);
      throws(function(){
        return q('{x: Number}', '{x: 2, y: hi}');
      }, /does not type check/);
      return throws(function(){
        return q('{x: Number, y: String}', '{x: 2}');
      }, /does not type check/);
    });
  });
  it('wildcard', function(){
    it('undefined', function(){
      return q('*', 'undefined', void 8);
    });
    it('null', function(){
      return q('*', 'null', null);
    });
    it('null', function(){
      return q('*', 'NaN', NaN);
    });
    it('bool', function(){
      q('*', 'true', true);
      return q('*', 'false', false);
    });
    it('number', function(){
      q('*', '0', 0);
      q('*', '1', 1);
      q('*', '-1', -1);
      q('*', '1.1', 1.1);
      return q('*', '-1.1', -1.1);
    });
    it('string', function(){
      q('*', 'hi', 'hi');
      q('*', '2011-11-11', '2011-11-11');
      q('*', '"\'"', "'");
      q('*', '"\\""', '"');
      return q('*', '"\\"\\""', '""');
    });
    it('quoted string', function(){
      q('*', '"hello there"', 'hello there');
      q('*', '"undefined"', 'undefined');
      q('*', '"void"', 'void');
      q('*', '"true"', 'true');
      q('*', '"false"', 'false');
      return q('*', '"2"', '2');
    });
    it('date', function(){
      q('*', '#2011-11-11#', new Date('2011-11-11'));
      return q('*', '#1320969600000#', new Date('2011-11-11'));
    });
    it('regex', function(){
      q('*', '/hi/', /hi/);
      q('*', '/hi/ig', /hi/ig);
      q('*', '/\\//', /\//);
      return q('*', '/^(hi) |[a-zA-Z]*:there$/g', /^(hi) |[a-zA-Z]*:there$/g);
    });
    it('array', function(){
      q('*', '[1,2,3]', [1, 2, 3]);
      return q('*', '[]', []);
    });
    it('tuple', function(){
      return q('*', '(1,2)', [1, 2]);
    });
    return it('object', function(){
      q('*', '{x: 2, y: hello}', {
        x: 2,
        y: 'hello'
      });
      return q('*', '{}', {});
    });
  });
  it('nested mixed', function(){
    it('array of tuples', function(){
      return q('[(Number, String)]', '[(1, hi),(3,"hello there"),(5,yo)]', [[1, 'hi'], [3, 'hello there'], [5, 'yo']]);
    });
    it('array of objects', function(){
      return q('[{x: Number}]', '[{x: 2}, {x: 3}]', [
        {
          x: 2
        }, {
          x: 3
        }
      ]);
    });
    return it('wildcard', function(){
      return q('*', '[hi,(null,[42]),{k: true}]', [
        'hi', [null, [42]], {
          k: true
        }
      ]);
    });
  });
  return it('options', function(){
    it('explicit', function(){
      q('Date | String', '2011-11-11', new Date('2011-11-11'), {
        explicit: false
      });
      q('Date | String', '2011-11-11', '2011-11-11', {
        explicit: true
      });
      q('RegExp', 're', /re/, {
        explicit: false
      });
      return throws(function(){
        return q('RegExp', 're', null, {
          explicit: true
        });
      }, /Value "re" does not type check/);
    });
    return it('custom-types', function(){
      var options;
      function Person(name, age){
        this.name = name;
        this.age = age;
      }
      options = {
        customTypes: {
          Even: {
            typeOf: 'Number',
            validate: function(it){
              return it % 2 === 0;
            },
            cast: function(it){
              return {
                type: 'Just',
                value: parseInt(it)
              };
            }
          },
          Person: {
            typeOf: 'Object',
            validate: (function(it){
              return it instanceof Person;
            }),
            cast: function(value, options, typesCast){
              var name, age;
              if (toString$.call(value).slice(8, -1) !== 'Object') {
                return {
                  type: 'Nothing'
                };
              }
              name = typesCast(value.name, [{
                type: 'String'
              }], options);
              age = typesCast(value.age, [{
                type: 'Number'
              }], options);
              return {
                type: 'Just',
                value: new Person(name, age)
              };
            }
          }
        }
      };
      q('Even', '2', 2, options);
      throws(function(){
        return q('Even', '3', null, options);
      }, /Value "3" does not type check/);
      q('Person', '{name: Arnold, age: 25}', new Person('Arnold', 25), options);
      throws(function(){
        return q('FAKE', '3', void 8, options);
      }, /Type not defined: FAKE/);
      return throws(function(){
        return q('FAKE', '3');
      }, /Type not defined: FAKE/);
    });
  });
});
