from __future__ import absolute_import, division, print_function

from dynd import nd
from collections import Iterator
from datashape import dshape, Record
from datashape.predicates import isunit, isdimension


def validate(schema, item):
    try:
        nd.array(item, dtype=str(schema))
        return True
    except:
        return False


def coerce(dshape, item):
    if isinstance(item, Iterator):
        item = list(item)
    return nd.as_py(nd.array(item, dtype=str(dshape)))


def coerce_to_ordered(ds, data):
    """ Coerce data with dicts into an ordered ND collection

    >>> from datashape import dshape

    >>> coerce_to_ordered('{x: int, y: int}', {'x': 1, 'y': 2})
    (1, 2)

    >>> coerce_to_ordered('var * {x: int, y: int}',
    ...                  [{'x': 1, 'y': 2}, {'x': 10, 'y': 20}])
    ((1, 2), (10, 20))

    Idempotent
    >>> coerce_to_ordered('var * {x: int, y: int}',
    ...                   ((1, 2), (10, 20)))
    ((1, 2), (10, 20))
    """
    if isinstance(ds, str):
        ds = dshape(ds)
    if isinstance(ds[0], Record):
        if isinstance(data, (list, tuple)):
            return data
        rec = ds[0]
        return tuple(coerce_to_ordered(rec[name], data[name])
                     for name in rec.names)
    if isdimension(ds[0]):
        return tuple(coerce_to_ordered(ds.subarray(1), row)
                     for row in data)
    return data


def coerce_record_to_row(schema, rec):
    """

    >>> from datashape import dshape

    >>> schema = dshape('{x: int, y: int}')
    >>> coerce_record_to_row(schema, {'x': 1, 'y': 2})
    [1, 2]

    Idempotent
    >>> coerce_record_to_row(schema, [1, 2])
    [1, 2]
    """
    if isinstance(rec, (tuple, list)):
        return rec
    return [rec[name] for name in schema[0].names]


def coerce_row_to_dict(schema, row):
    """

    >>> from datashape import dshape

    >>> schema = dshape('{x: int, y: int}')
    >>> coerce_row_to_dict(schema, (1, 2)) # doctest: +SKIP
    {'x': 1, 'y': 2}

    Idempotent
    >>> coerce_row_to_dict(schema, {'x': 1, 'y': 2}) # doctest: +SKIP
    {'x': 1, 'y': 2}
    """
    if isinstance(row, dict):
        return row
    return dict((name, item) for name, item in zip(schema[0].names, row))


def ordered_index(ind, ds):
    """ Transform a named index into an ordered one

    >>> ordered_index(1, '3 * int')
    1
    >>> ordered_index('name', '{name: string, amount: int}')
    0
    >>> ordered_index((0, 0), '3 * {x: int, y: int}')
    (0, 0)
    >>> ordered_index((0, 'x'), '3 * {x: int, y: int}')
    (0, 0)
    >>> ordered_index((0, ['x', 'y']), '3 * {x: int, y: int}')
    (0, [0, 1])
    """
    if isinstance(ds, str):
        ds = dshape(ds)
    if isinstance(ind, int):
        return ind
    if isinstance(ind, str) and isinstance(ds[0], Record):
        return ds[0].names.index(ind)
    if isdimension(ds[0]):
        return (ind[0],) + ordered_index(ind[1:], ds.subshape[ind[0]])
    if isinstance(ind, tuple) and len(ind) == 1:
        return (ordered_index(ind[0], ds),)
    if isinstance(ind, list) and isinstance(ds[0], Record):
        return list(map(ds[0].names.index, ind))
    raise NotImplementedError()

@@ -2516,51 +2516,268 @@
 dex(
-(0, 'x'), '3 * %7Bx: int, y: int%7D')%0A    (0, 0
+%5B0, 1%5D, '3 * %7Bx: int, y: int%7D')%0A    %5B0, 1%5D%0A    %3E%3E%3E ordered_index((%5B0, 1%5D, 'x'), '3 * %7Bx: int, y: int%7D')%0A    (%5B0, 1%5D, 0)%0A    %3E%3E%3E ordered_index((0, 'x'), '3 * %7Bx: int, y: int%7D')%0A    (0, 0)%0A    %3E%3E%3E ordered_index((0, %5B0, 1%5D), '3 * %7Bx: int, y: int%7D')%0A    (0, %5B0, 1%5D
 )%0A  
@@ -2936,19 +2936,28 @@
 ce(ind, 
+(
 int
+, slice)
 ):%0A     
@@ -2970,16 +2970,97 @@
 urn ind%0A
+    if isinstance(ind, list):%0A        return %5Bordered_index(i, ds) for i in ind%5D%0A
     if i
@@ -3202,16 +3202,24 @@
 %5B0%5D,) + 
+tupleit(
 ordered_
@@ -3249,15 +3249,11 @@
 ape%5B
-ind%5B
 0%5D
-%5D
+)
 )%0A  
@@ -3288,21 +3288,15 @@
 and 
-len(ind) == 1
+not ind
 :%0A  
@@ -3313,142 +3313,150 @@
 rn (
-ordered_index(ind%5B0%5D, ds),)%0A    if isinstance(ind, list) and isinstance(ds%5B0%5D, Record):%0A        return list(map(ds%5B0%5D.names.
+)%0A    if isinstance(ind, tuple):%0A        return ((ordered_index(ind%5B0%5D, ds),)%0A                + tupleit(ordered_
 index
-, 
+(
 ind
+%5B1:%5D, ds.subshape%5B0%5D)
 ))%0A 
@@ -3486,8 +3486,105 @@
 Error()%0A
+%0Adef tupleit(x):%0A    if not isinstance(x, tuple):%0A        return (x,)%0A    else:%0A        return x%0A

improve ordered_index