#-*- coding: utf-8 -*-

# Copyright 2008-2013 Calculate Ltd. http://www.calculate-linux.org
#
#  Licensed under the Apache License, Version 2.0 (the "License");
#  you may not use this file except in compliance with the License.
#  You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
#  limitations under the License.

import types
from calculate.lib import  cl_overriding
import re
import sys
from itertools import *

from calculate.lib.cl_lang import setLocalTranslate
setLocalTranslate('cl_lib3',sys.modules[__name__])

def columnMatrix(*cols):
    """
    Split columns text to matrix
    """
    def splitter(s,wid):
        """Split string by width and \\n"""
        for part in unicode(s).split('\n'):
            if wid > 0:
                while part:
                    p, part = part[:wid],part[wid:]
                    yield p
            else:
                yield part
    return izip_longest(
           *starmap(splitter,
           izip_longest(islice(cols,0,None,2),
                        islice(cols,1,None,2))),
           fillvalue="")

def columnStr(*cols):
    '''Get string for write, data by columns

    Parameters:
    cols     list(tuple(text,width))

    Return:
    string for display

    Example: columnStr( "Some text", 10, "Next column", 20 )
    '''

    def generateStrings(*cols):
        colWidth = list(islice(cols,1,None,2))
        for line in map(lambda x:izip_longest(colWidth,x),columnMatrix(*cols)):
            yield " ".join(("{0:%d}"%(width or 1)).format(s) for \
                            width,s in line)
    return "\n".join(generateStrings(*cols))

def columnWrite(*cols):
    '''Display data by columns

    Parameters:
    cols     list(tuple(text,width))

    Example: columnWrite( "Some text", 10, "Next column", 20 )
    '''
    colWidth = list(islice(cols,1,None,2))
    for line in map(lambda x:izip_longest(colWidth,x),columnMatrix(*cols)):
        for width,s in line:
            cl_overriding.printSUCCESS(("{0:%d} "%(width or 1)).format(s),
                printBR=False)
        cl_overriding.printSUCCESS("",printBR=True)

def justify(s,width):
    '''Выровнить текст по ширине

    Параметры:
    s      выводимая строка
    width  ширина на которую надо выровнить строку

    Возвращаямые параметры:
    Выровненная строка
    '''
    # если подана строка без пробелов - прекратить обработку
    if s.find(' ') == -1:
        return s
    pos = 0
    # переводим в юникод для правильного вычисления длины
    try:
        s = s.decode( 'utf-8' )
    # пропуск если это не utf-8
    except UnicodeEncodeError:
        pass
    # пока длина строки меньше указанной
    while len(s) < width:
        # находим очередной пробел
        pos = s.find( ' ', pos )
        # если не найден искать сначала
        if pos == -1:
            pos = s.find(' ')
        # вставить в позицию еще один пробел
        s = s[:pos] +' ' +s[pos:]
        # оставить удвоенный пробел
        pos += 3
    # вернуть строку в utf8 если она пришла в utf8
    return s.encode('utf-8')

class tableReport(object):
    """Display data in table"""
    def __init__(self, title, headerList, dataList,colSpan=1):

        # title
        self.title = title
        # elements list for first table row
        self.headerList = headerList
        # row list (each row is list)
        self.dataList = map(lambda x:map(lambda y:str(y).expandtabs(),
                                     map(lambda y:y or "",x)),
                        dataList)
        # calculate columns list
        self.columnsWidth = self.getColumsnWidth()
        self.vertChar = colSpan*" "+"|"+colSpan*" "
        self.crossChar = colSpan*"-"+"+"+colSpan*"-"
        self.colSpan = colSpan
        self.align = "<"+"^"*(len(headerList)-2)+"<"

    def setAutosize(self,colNum=-1,maxSize=None,forceMax=False):
        """
        Set width for specified column
        """
        if maxSize is None:
            maxSize = getTerminalSize()[1]
            if not maxSize:
                maxSize = 80
        spanSize = self.colSpan*2+1
        if colNum < 0:
            colNum = len(self.columnsWidth)+colNum
        currentSize = sum(map(lambda x:x+spanSize,self.columnsWidth))+1
        if currentSize < maxSize and not forceMax:
            return
        excludeSize = sum(map(lambda x:x[1]+spanSize,
                      filter(lambda x:x[0]!=colNum,
                      enumerate(self.columnsWidth))))+spanSize+1
        if maxSize - excludeSize > 5:
            self.columnsWidth[colNum] = maxSize - excludeSize

    def getColumsnWidth(self):
        """Находит максимальную ширину каждой колонки

        результат список ширин колонок
        """
        columnsWidth = []
        for s in self.headerList:
            columnsWidth.append(len(_toUNICODE(s)))
        lenCol = len(self.headerList)
        maxLenCol = lenCol
        # Вычисляем максимальное количество столбцов
        for s in self.dataList:
            lenS = len(s)
            if maxLenCol < lenS:
                maxLenCol = lenS
        if maxLenCol > lenCol:
            appCol = maxLenCol - lenCol
            # Добавляем элементы в список ширин
            for i in range(appCol):
                columnsWidth.append(0)
        # Вычисляем ширину столбцов
        for e in self.dataList:
            i = 0
            for s in e:
                lenS = len(_toUNICODE(s))
                if columnsWidth[i] < lenS:
                    columnsWidth[i] = lenS
                i += 1
        return columnsWidth

    def createFormatStr(self, listStr):
        """Создает список (текст, ширина ...)"""
        return chain(*izip_longest(listStr,self.columnsWidth,fillvalue=""))


    def printFunc(self, s):
        """
        Overriding print
        """
        cl_overriding.printSUCCESS(s, printBR=False)

    def line_printer(self, s):
        """
        Вывод используется для отображения "линий" таблицы
        """
        self.printFunc(s)

    def head_printer(self, s):
        """
        Вывод используется для отображения текста в шапке
        """
        self.printFunc(s)

    def body_printer(self, s):
        """
        Вывод используется для отображения текста в таблице
        """
        self.printFunc(s)

    def default_printer(self, s):
        self.printFunc(s)

    def printReport(self,printRows=True):
        """Напечатать данные в табличном виде"""
        if self.title:
            self.printFunc(self.title)
        listStrSep = []
        for lenCol in self.columnsWidth:
            listStrSep.append("-"*lenCol)
        printData = []
        printData.append([self.crossChar, self.line_printer,
                          columnMatrix(*self.createFormatStr(listStrSep))])
        printData.append([self.vertChar, self.head_printer,
                          columnMatrix(*self.createFormatStr(self.headerList))])
        printData.append([self.crossChar, self.line_printer,
                          columnMatrix(*self.createFormatStr(listStrSep))])
        for s in self.dataList:
            printData.append([self.vertChar, self.body_printer,
                              columnMatrix(*self.createFormatStr(s))])
        printData.append([self.crossChar, self.line_printer,
                          columnMatrix(*self.createFormatStr(listStrSep))])
        last_col = len(self.headerList) - 1

        for char, _print, row in printData:
            for line in row:
                self.line_printer(char[self.colSpan:])
                for i, align, width, txt in izip(count(), self.align,
                                                 self.columnsWidth,
                                                 line):
                    _print(("{0:%s%d}" % (align, width)).format(txt))
                    if i < last_col:
                        self.line_printer(char)
                self.line_printer(char[:-self.colSpan+1])
                self.default_printer('\n')
        if printRows:
            self.printFunc("(%s %s)"%(len(self.dataList), _("rows")))

class MultiReplace:
    """MultiReplace function object
    
    Usage:
    replacer = MultiReplace({'str':'efg','in':'asd'})
    s = replacer("string")
    """
    def __init__(self, repl_dict):
        # string to string mapping; use a regular expression
        keys = repl_dict.keys()
        keys.sort(reverse=True) # lexical order
        pattern = u"|".join([re.escape(key) for key in keys])
        self.pattern = re.compile(pattern)
        self.dict = repl_dict

    def replace(self, s):
        # apply replacement dictionary to string
        def repl(match, get=self.dict.get):
            item = match.group(0)
            return get(item, item)
        return self.pattern.sub(repl, s)
    __call__ = replace

def str2dict(s):
    """Convert string to dictionary:

    String format:
    {'key1':'value1','key2':'val\'ue2'}
    """
    value = r'(?:\\\\|\\\'|[^\'])'
    pair = r"""
        \s*'           # begin key 
        (%(v)s*)
        '              # end key 
        \s*:\s*        # delimeter key/value
        '              # begin value
        (%(v)s*)
        '\s*           # end value
        """ % {"v":value}
    reDict = re.compile(pair, re.X)
    reMatchDict = re.compile("""
            ^{          # begin dict
            ((%(v)s,)*  # many pair with comma at end
            %(v)s)?     # pair without comma
            }$          # end dict
            """ % {'v':pair}, re.X)
    if reMatchDict.match(s.strip()):
        d = dict(reDict.findall(s))
        replaceSlash = MultiReplace({'\\\\':'\\','\\\'':'\''})
        for i in d.keys():
            d[i] = replaceSlash(d[i])
        return d
    else:
        cl_overriding.printERROR(_("wrong dict value: %s"%s))
        cl_overriding.exit(1)

def str2list(s):
    """Convert string to list:

    String format:
    ['value1','val\'ue2']
    """
    value = r'(?:\\\\|\\\'|[^\'])'
    element = r"""
        \s*'           # begin value 
        (%(v)s*)
        '\s*           # end value
        """ % {"v":value}
    reList = re.compile(element, re.X)
    reMatchList = re.compile("""
            ^\[          # begin dict
            ((%(v)s,)*  # many elements with comma at end
            %(v)s)?     # element without comma
            \]$          # end dict
            """ % {'v':element}, re.X)
    if reMatchList.match(s.strip()):
        replaceSlash = MultiReplace({'\\\\':'\\','\\\'':'\''})
        return [replaceSlash(i) for i in reList.findall(s)]
    else:
        cl_overriding.printERROR(_("wrong list value: %s"%s))
        cl_overriding.exit(1)

def list2str(list):
    """Convert list to string
    
    Return string with escaped \ and '.
    """
    replaceSlash = MultiReplace({'\\':'\\\\','\'':'\\\''})
    return "[%s]" % ','.join(["'%s'"%replaceSlash(str(i))
                                for i in list ])

def dict2str(dict):
    """Convert dictionry to string
    
    Return string with escaped \ and '.
    """
    replaceSlash = MultiReplace({'\\':'\\\\','\'':'\\\''})
    return '{%s}' % ','.join(["'%s':'%s'" % (str(k),replaceSlash(str(v))) \
        for (k,v) in dict.items()])

def convertStrListDict(val):
    """Convert data between string, list, dict"""
    # if val is list
    if type(val) == types.ListType:
        return list2str(val)
    # if val is dictionary
    elif type(val) == types.DictType:
        return dict2str(val)
    # else it is string
    else:
        # detect dictionary
        if re.match("^{.*}$",val):
            return str2dict(val)
        # detect list
        elif re.match("^\[.*\]$",val):
            return str2list(val)
        # else is simple string
        else:
            return val

def _toUNICODE(val):
    """Convert text to unicode"""
    if type(val) == types.UnicodeType:
        return val
    else:
        return str(val).decode('UTF-8')

def formatListOr(lst):
    """Convert list to string like this: [1,2,3] -> 1,2 or 3"""
    lststr = map(lambda x:str(x),lst)
    return (" %s "%_("or")).join(filter(lambda x:x,
                                 [", ".join(map(lambda x:x or "''",lststr[:-1])),
                                 "".join(lststr[-1:])]))

def getTerminalSize():
    """
    Get terminal size
    """
    import struct
    try:
        import fcntl
        import termios
    except ImportError:
        return (None, None)

    try:
        data = fcntl.ioctl(sys.stdout.fileno(), termios.TIOCGWINSZ, 4 * '00')
        height, width = struct.unpack('4H', data)[:2]
        return (height, width)
    except Exception:
        return (None, None)


def simplify_profiles(profiles, drop_words=("amd64", "x86")):
    """
    Упростить названия профилей (удалить одинаковое начало и конец в строках)
    :param profiles: список названий профилей
    :param drop_words: список слов из профилей, которые можно отбросить
        если профиль один
    :return: список упрощенных названий профилей
    """
    if len(profiles) < 2:
        if len(profiles) == 1:
            return ["/".join(filter(lambda x: x not in drop_words,
                                    profiles[0].split('/')))]
        return profiles
    matrix = [x.split('/') for x in profiles]
    # удаляем одинаковые начальные и конечные поля
    matrix = zip(*reversed(list(dropwhile(lambda x: x.count(x[0]) == len(x),
                (reversed(list(dropwhile(lambda x: x.count(x[0]) == len(x),
                 izip_longest(*matrix, fillvalue="")))))))))
    return ["/".join(filter(None, x)) for x in matrix]
