import numpy as np
import sys

from datashape.discovery import (discover, null, unite_identical, unite_base,
        unite_merge_dimensions, do_one, lowest_common_dshape)
from datashape.coretypes import *
from datashape.internal_utils import raises
from datashape import dshape
from datetime import date, time, datetime
from datashape.py2help import xfail

def test_simple():
    assert discover(3) == int64
    assert discover(3.0) == float64
    assert discover(3.0 + 1j) == complex128
    assert discover('Hello') == string
    assert discover(True) == bool_
    assert discover(None) == null


def test_long():
    if sys.version_info[0] == 2:
        assert eval('discover(3L)') == int64


def test_list():
    print(discover([1, 2, 3]))
    assert discover([1, 2, 3]) == 3 * discover(1)
    assert discover([1.0, 2.0, 3.0]) == 3 * discover(1.0)


def test_heterogeneous_ordered_container():
    print(discover(('Hello', 1)))
    assert discover(('Hello', 1)) == Tuple([discover('Hello'), discover(1)])



def test_string():
    assert discover('1') == discover(1)
    assert discover('1.0') == discover(1.0)
    assert discover('True') == discover(True)
    assert discover('true') == discover(True)


def test_record():
    assert discover({'name': 'Alice', 'amount': 100}) == \
            Record([['amount', discover(100)],
                    ['name', discover('Alice')]])


def test_datetime():
    inputs = ["1991-02-03 04:05:06",
              "11/12/1822 06:47:26.00",
              "1822-11-12T06:47:26",
              "Fri Dec 19 15:10:11 1997",
              "Friday, November 11, 2005 17:56:21",
              "1982-2-20 5:02:00",
              "20030331 05:59:59.9",
              "Jul  6 2030  5:55PM",
              "1994-10-20 T 11:15",
              "2013-03-04T14:38:05.123",
              datetime(2014, 1, 1, 12, 1, 1),
              # "15MAR1985:14:15:22",
              # "201303041438"
              ]
    for dt in inputs:
        assert discover(dt) == datetime_


def test_date():
    assert discover('2014-01-01') == date_
    assert discover(date(2014, 1, 1)) == date_


def test_time():
    assert discover(time(12, 0, 1)) == time_


@xfail
def test_time_string():
    assert discover('12:00:01') == time_


def test_integrative():
    data = [{'name': 'Alice', 'amount': '100'},
            {'name': 'Bob', 'amount': '200'},
            {'name': 'Charlie', 'amount': '300'}]

    assert dshape(discover(data)) == \
            dshape('3 * {amount: int64, name: string}')


def test_numpy_scalars():
    assert discover(np.int32(1)) == int32
    assert discover(np.float64(1)) == float64


def test_numpy_array():
    assert discover(np.ones((3, 2), dtype=np.int32)) == dshape('3 * 2 * int32')


unite = do_one([unite_identical,
                unite_merge_dimensions,
                unite_base])

def test_unite():
    assert unite([int32, int32, int32]) == 3 * int32
    assert unite([3 * int32, 2 * int32]) == 2 * (var * int32)
    assert unite([2 * int32, 2 * int32]) == 2 * (2 * int32)
    assert unite([3 * (2 * int32), 2 * (2 * int32)]) == 2 * (var * (2 * int32))


def test_unite_missing_values():
    assert unite([int32, null, int32]) == 3 * Option(int32)
    assert unite([string, null, int32])


def test_unite_tuples():
    assert discover([[1, 1, 'hello'],
                     [1, '', ''],
                     [1, 1, 'hello']]) == \
                    3 * Tuple([int64, Option(int64), Option(string)])

    assert discover([[1, 1, 'hello', 1],
                     [1, '', '', 1],
                     [1, 1, 'hello', 1]]) == \
                    3 * Tuple([int64, Option(int64), Option(string), int64])


def test_unite_records():
    assert discover([{'name': 'Alice', 'balance': 100},
                     {'name': 'Bob', 'balance': ''}]) == \
                    2 * Record([['balance', Option(int64)], ['name', string]])

    assert discover([{'name': 'Alice', 's': 'foo'},
                     {'name': 'Bob', 's': None}]) == \
                    2 * Record([['name', string], ['s', Option(string)]])

    assert discover([{'name': 'Alice', 's': 'foo', 'f': 1.0},
                     {'name': 'Bob', 's': None, 'f': None}]) == \
                    2 * Record([['f', Option(float64)],
                                ['name', string],
                                ['s', Option(string)]])

    # assert unite((Record([['name', string], ['balance', int32]]),
    #               Record([['name', string]]))) == \
    #                 Record([['name', string], ['balance', Option(int32)]])


def test_dshape_missing_data():
    assert discover([[1, 2, '', 3],
                     [1, 2, '', 3],
                     [1, 2, '', 3]]) == \
                3 * Tuple([int64, int64, null, int64])


def test_discover_mixed():
    i = discover(1)
    f = discover(1.0)
    assert dshape(discover([[1, 2, 1.0, 2.0]] * 10)) == \
            10 * Tuple([i, i, f, f])

    print(dshape(discover([[1, 2, 1.0, 2.0], [1.0, 2.0, 1, 2]] * 5)))
    assert dshape(discover([[1, 2, 1.0, 2.0], [1.0, 2.0, 1, 2]] * 5)) == \
            10 * (4 * f)


def test_test():
    print(discover([['Alice', 100], ['Bob', 200]]))
    print(2 * Tuple([string, int64]))
    assert discover([['Alice', 100], ['Bob', 200]]) == \
            2 * Tuple([string, int64])


def test_discover_appropriate():
    assert discover((1, 1.0)) == Tuple([int64, real])
    print(discover([(1, 1.0), (1, 1.0), (1, 1)]))
    assert discover([(1, 1.0), (1, 1.0), (1, 1)]) == \
            3 * Tuple([int64, real])


def test_big_discover():
    data = [['1'] + ['hello']*20] * 10
    assert discover(data) == 10 * Tuple([int64] + [string]*20)


def test_unite_base():
    assert unite_base([date_, datetime_]) == 2 * datetime_


def test_list_of_dicts_no_difference():
    data = [{'name': 'Alice', 'amount': 100},
            {'name': 'Bob'}]
    result = discover(data)
    expected = dshape('2 * {amount: ?int64, name: string}')
    assert result == expected


def test_list_of_dicts_difference():
    data = [{'name': 'Alice', 'amount': 100},
            {'name': 'Bob', 'house_color': 'blue'}]
    result = discover(data)
    s = '2 * {amount: ?int64, house_color: ?string, name: string}'
    expected = dshape(s)
    assert result == expected


def test_unite_base_on_records():
    dshapes = [dshape('{name: string, amount: int32}'),
               dshape('{name: string, amount: int32}')]
    assert unite_base(dshapes) == \
            dshape('2 * {name: string, amount: int32}')

    dshapes = [Null(), dshape('{name: string, amount: int32}')]
    assert unite_base(dshapes) == \
            dshape('2 * ?{name: string, amount: int32}')

    dshapes = [dshape('{name: string, amount: int32}'),
               dshape('{name: string, amount: int64}')]
    assert unite_base(dshapes) == \
            dshape('2 * {name: string, amount: int64}')
