from __future__ import unicode_literals
"""
Format String 2D array
2d array for compositing term-formated strings


-autoexpanding vertically
-interesting get_item behavior (renders fmtstrs)
-caching behavior eventually


>>> a = FSArray(10, 14)
>>> a.shape
(10, 14)
>>> a[1] = 'i'
>>> a[3:4, :] = ['i' * 14]
>>> a[16:17, :] = ['j' * 14]
>>> a.shape, a[16, 0]
((17, 14), ['j'])
>>> a[200, 1] = ['i']
>>> a[200, 1]
['i']
"""

import sys
import logging
import unittest

from .formatstring import fmtstr
from .formatstring import normalize_slice
from .formatstring import FmtStr

logger = logging.getLogger(__name__)

#TODO check that strings used in arrays don't have tabs or spaces in them!

def slicesize(s):
    return int((s.stop - s.start) / (s.step if s.step else 1))

def fsarray(strings, *args, **kwargs):
    """fsarray(list_of_FmtStrs_or_strings, width=None) -> FSArray

    Returns a new FSArray of width of the maximum size of the provided
    strings, or width provided, and height of the number of strings provided.
    If a width is provided, raises a ValueError if any of the strings
    are of length greater than this width"""

    strings = list(strings)
    if 'width' in kwargs:
        width = kwargs['width']
        del kwargs['width']
        if strings and max(len(s) for s in strings) > width:
            raise ValueError("Those strings won't fit for width %d" % width)
    else:
        width = max(len(s) for s in strings) if strings else 0
    fstrings = [s if isinstance(s, FmtStr) else fmtstr(s, *args, **kwargs) for s in strings]
    arr = FSArray(len(fstrings), width, *args, **kwargs)
    rows = [fs.setslice_with_length(0, len(s), s, width) for fs, s in zip(arr.rows, fstrings)]
    arr.rows = rows
    return arr

class FSArray(object):
    """A 2D array of colored text.

    Internally represented by a list of FmtStrs of identical size."""

    #TODO add constructor that takes fmtstrs instead of dims
    def __init__(self, num_rows, num_columns, *args, **kwargs):
        self.saved_args, self.saved_kwargs = args, kwargs
        self.rows = [fmtstr('', *args, **kwargs) for _ in range(num_rows)]
        self.num_columns = num_columns

    def __getitem__(self, slicetuple):
        if isinstance(slicetuple, int):
            if slicetuple < 0:
                slicetuple = len(self.rows) - slicetuple
            if slicetuple < 0 or slicetuple >= len(self.rows):
                raise IndexError('out of bounds')
            return self.rows[slicetuple]
        if isinstance(slicetuple, slice):
            rowslice = normalize_slice(len(self.rows), slicetuple)
            return self.rows[rowslice]
        rowslice, colslice = slicetuple
        rowslice = normalize_slice(len(self.rows), rowslice)
        colslice = normalize_slice(self.num_columns, colslice)
        #TODO clean up slices
        return [fs[colslice] for fs in self.rows[rowslice]]

    def __len__(self):
        return len(self.rows)

    @property
    def shape(self):
        """tuple of (len(rows, len(num_columns)) numpy-style shape"""
        return len(self.rows), self.num_columns