var assert, throws, ref$, c, parsedTypeCheck;
assert = require('assert'), throws = assert.throws;
ref$ = require('../..'), c = ref$.typeCheck, parsedTypeCheck = ref$.parsedTypeCheck;
describe('check', function(){
  it('Undefined', function(){
    assert(c('Undefined', void 8));
    assert(c('Undefined', Math.FAKE));
    assert(!c('Undefined', null));
    return assert(!c('Undefined', false));
  });
  it('Undefined in field', function(){
    assert(c('{a: Undefined}', {}));
    assert(c('{a: Undefined}', {
      a: void 8
    }));
    return assert(!c('{a: Undefined}', {
      a: 1
    }));
  });
  it('Undefined in tuple', function(){
    assert(c('(Undefined, Number)', [void 8, 2]));
    return assert(!c('(Undefined, Number)', [1, 2]));
  });
  it('Null', function(){
    assert(c('Null', null));
    assert(!c('Null', void 8));
    return assert(!c('Null', false));
  });
  it('Boolean', function(){
    assert(c('Boolean', true));
    assert(c('Boolean', false));
    assert(c('Boolean', new Boolean(false)));
    return assert(!c('Boolean', 1));
  });
  it('String', function(){
    assert(c('String', 'hi'));
    assert(c('String', new String('hi')));
    return assert(!c('String', 2));
  });
  it('Number', function(){
    assert(c('Number', 2));
    assert(c('Number', new Number(2)));
    return assert(!c('Number', 'hi'));
  });
  it('NaN', function(){
    assert(c('NaN', NaN));
    return assert(!c('NaN', 1));
  });
  it('Int', function(){
    assert(c('Int', 1));
    assert(c('Int', 1.0));
    return assert(!c('Int', 1.1));
  });
  it('Float', function(){
    assert(c('Float', 1));
    assert(c('Float', 1.0));
    return assert(c('Float', 1.1));
  });
  it('Date', function(){
    assert(c('Date', new Date('2011-11-11')));
    return assert(!c('Date', new Date('2011-1111')));
  });
  it('Function', function(){
    return assert(c('Function', function(){}));
  });
  it('wildcard', function(){
    assert(c('*', void 8));
    assert(c('*', null));
    assert(c('*', 2));
    assert(c('*', {}));
    assert(c('*', new Error));
    return assert(c('[*]', [
      1, null, void 8, 'hi', {
        x: 22
      }
    ]));
  });
  it('multiple', function(){
    assert(c('Number | String', 'hi'));
    return assert(!c('Date | Number', 'hi'));
  });
  it('Array', function(){
    it('bare', function(){
      assert(c('Array', [1, 2, 3]));
      assert(c('Array', [1, 'hi']));
      return assert(!c('Array', true));
    });
    it('simple', function(){
      return assert(c('[Number]', [1, 2, 3]));
    });
    it('incorrect type', function(){
      return assert(!c('[Number]', true));
    });
    return it('incorrect element type', function(){
      return assert(!c('[Number]', [1, 'hi']));
    });
  });
  it('Tuple', function(){
    it('simple', function(){
      return assert(c('(String, Number)', ['hi', 2]));
    });
    it('too long', function(){
      return assert(!c('(String, Number)', ['hi', 2, 1]));
    });
    it('too short', function(){
      return assert(!c('(String, Number)', ['hi']));
    });
    it('incorrect type', function(){
      return assert(!c('(String, Number)', {}));
    });
    return it('incorrect element type', function(){
      return assert(!c('(String, Number)', ['hi', 'bye']));
    });
  });
  it('bare Object', function(){
    assert(c('Object', {}));
    assert(c('Object', {
      a: 1,
      length: 1
    }));
    return assert(!c('Object', new Date));
  });
  it('Maybe', function(){
    it('simple', function(){
      assert(c('Maybe Number', 1));
      assert(c('Maybe Number', null));
      return assert(!c('Maybe Number', 'string'));
    });
    it('with multiple', function(){
      var type;
      type = 'Maybe Number | String';
      assert(c(type, 2));
      assert(c(type, null));
      return assert(c(type, 'hi'));
    });
    it('in fields', function(){
      var type;
      type = '{a: Maybe String}';
      assert(c(type, {
        a: 'string'
      }));
      assert(c(type, {
        a: null
      }));
      assert(c(type, {}));
      return assert(!c(type, {
        a: 2
      }));
    });
    it('in tuple', function(){
      var type;
      type = '(Number, Maybe String)';
      assert(c(type, [1, 'hi']));
      assert(c(type, [1, null]));
      assert(c(type, [1]));
      return assert(!c('(Maybe String, Number)', [2]));
    });
    return it('in array', function(){
      assert(c('[Maybe Number]', [1, null, 2, void 8, 23]));
      return assert(c('Object[Maybe String]', {
        0: 'a',
        2: null,
        5: 'b',
        length: 6
      }));
    });
  });
  it('duck typing', function(){
    it('basic', function(){
      return assert(c('{a: String}', {
        a: 'hi'
      }));
    });
    it('property must by appropriate type', function(){
      return assert(!c('{a: String}', {
        a: 2
      }));
    });
    it('key must be the same', function(){
      return assert(!c('{a: String}', {
        b: 'hi'
      }));
    });
    it('not an object - fails', function(){
      return assert(!c('{a: String}', 2));
    });
    it('non-enumerable properties', function(){
      return assert(c('{parse: Function, stringify: Function}', JSON));
    });
    it('enumerable and non-enumerable properties', function(){
      return assert(c('{0: Number, 1: Number, length: Number}', [1, 2]));
    });
    return it('using spread operator to check only a subset of the properties', function(){
      return assert(c('{length: Number, ...}', [1, 2]));
    });
  });
  it('structures with types', function(){
    it('fields with Object', function(){
      assert(c('Object{a: String}', {
        a: 'hi'
      }));
      return assert(!c('Object{a: String}', {
        a: 2
      }));
    });
    it('fields with Array', function(){
      assert(c('Array{0:Number, 1:Number, 2:Number}', [1, 2, 3]));
      assert(c('Array{0:Number, 1:Number, 2:Number}', [1, 2, 3]));
      assert(c('Array{0:Number, 1:String}', [1, 'hi']));
      return assert(!c('Array{0:Number, 1:String}', [1]));
    });
    it('fields with JSON', function(){
      assert(c('JSON{parse: Function, stringify: Function}', JSON));
      return assert(!c('JSON{parse: Function, stringify: Function}', {
        parse: function(){},
        stringify: function(){}
      }));
    });
    it('fields with Math (using subset)', function(){
      return assert(c('Math{PI: Float, sqrt: Function, ...}', Math));
    });
    it('array structure with Array', function(){
      return assert(c('Array[Number]', [1, 2]));
    });
    it('array structure with Object', function(){
      return assert(c('Object[Number]', {
        0: 1,
        1: 2,
        length: 2
      }));
    });
    it('tuple structure with Array', function(){
      return assert(c('Array(Number, String)', [1, 'two']));
    });
    return it('tuple structure with Object', function(){
      return assert(c('Object(Number, String)', {
        0: 1,
        1: 'two',
        length: 2
      }));
    });
  });
  it('custom types', function(){
    it('simple', function(){
      var o;
      o = {
        customTypes: {
          Even: {
            typeOf: 'Number',
            validate: function(it){
              return it % 2 === 0;
            }
          }
        }
      };
      assert(c('Even', 2, o));
      return assert(!c('Even', 1, o));
    });
    return it('overwrite current', function(){
      var o;
      o = {
        customTypes: {
          Undefined: {
            typeOf: 'String',
            validate: function(it){
              return it === 'bananas';
            }
          }
        }
      };
      assert(c('Undefined', 'bananas', o));
      return assert(!c('Undefined', void 8, o));
    });
  });
  it('nested', function(){
    var type;
    type = '{a: (String, [Number], {x: {a: Maybe Number}, y: Array, ...}), b: Error{message: String, ...}}';
    assert(c(type, {
      a: [
        'hi', [1, 2, 3], {
          x: {
            a: 42
          },
          y: [1, 'bye']
        }
      ],
      b: new Error('message')
    }));
    return assert(c(type, {
      a: [
        'moo', [3], {
          x: {},
          y: [],
          z: 999
        }
      ],
      b: new Error('23')
    }));
  });
  return it('errors', function(){
    it('no type defined', function(){
      return throws(function(){
        return parsedTypeCheck([{}], true);
      }, /No type defined\. Input: true/);
    });
    return it('types must be in array', function(){
      return throws(function(){
        return parsedTypeCheck({}, true);
      }, /Types must be in an array\. Input: true/);
    });
  });
});
