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

# Copyright 2008-2010 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.

from re import search, compile, S
from cl_utils import _toUNICODE
import cl_overriding
import cl_lang
import sys
tr = cl_lang.lang()
tr.setLocalDomain('cl_lib')
tr.setLanguage(sys.modules[__name__])

def prettyColumnStr(*cols):
    '''Функция преобразования строк в текстовые колонки. Если указанный текст
    не помещается в колонку, то строка переносится на следующую этой же колонки
    перенос текста идет по словам, и текст выравнивается по ширине колонки за
    счет дополнительных пробелов между словами. Если в строке используется
    перенос строки, то текст переносится не просто на следующую строку, а также
    на следующую строку колонки, причем если используется \r текст выравнива-
    ется по ширине, а если \n, то просто перевод строки.

    Параметры:
    cols     множестово пар: текст, ширина колонки, причем, если у последней
             колонки не указывать ширину, то она будет выведена вся.

    Возвращаемые параметры:
    строка, которую можно использовать для вывода на экран

    Пример: columnWrite( "Some text", 10, "Next column", 20 )
    '''
    # шаблон поиска переводов строк
    wherenr = compile( '[\n\r]', S )
    retstr = ""
    # перевести кортеж в список, т.к. изменяется
    cols = list(cols)
    # перевести текст в юникод, заодно перевести числа в строку
    noconvert = False
    space = u' '
    nospace = u''
    for i in xrange(0,len(cols),2):
        cols[i] = _toUNICODE(cols[i])
    # флаг "есть еще текст для вывода"
    repeat = True
    while repeat:
        # сбросить итератор на первый элемент
        q = 0
        repeat = False
        # пока не закончили перебирать параметры (перебираем по парам)
        while q < len(cols):
            # если это последний параметр, и для него не указана ширина
            if q == len(cols)-1:
                # выводим его полностью не смотря на ширину окна
                retstr += cols[q] + " "
                cols[q] = ''
            else:
                # вывести часть строки не больше указанной ширины колонки
                partstr = cols[q][:cols[q+1]]
                # искать перевод строки с полученной части
                brfind = wherenr.search(partstr)
                # если это не последняя колонка
                if q + 2 < len(cols):
                    # добавить разделитель между колонками
                    cellspacing = space
                else:
                    # разделитель не нужен
                    cellspacing = nospace

                # если перевод строки найден, то
                if brfind != None:
                    # для текущего вывода в колонку
                    # берем часть строки до перевода
                    partstr = partstr[:brfind.start()]
                    # остальная часть идет в остаток (без перевода)
                    cols[q] = cols[q][brfind.start()+1:]
#                    # если используется перевод каретки
#                    if brfind.group() == '\r':
#                        # то выравниваем по ширине колонки
#                        partstr = partstr.ljust(cols[q+1], ' ')
#                    else:
#                        # добавить отступы чтобы закончить колонку
                    partstr = partstr.ljust(cols[q+1], ' ')
                # если взята часть строки
                elif len(partstr) == cols[q+1] and partstr != cols[q]:
                    # если взята часть строки (разрыв в слове)
                    if cols[q][cols[q+1]] != ' ':
                        # ищем ближайший пробел справа
                        spacepos = partstr.rfind(' ')
                        # если пробел найти не удалось
                        if spacepos == -1:
                            # то на вывод идет часть строки равной ширине
                            cols[q] = cols[q][cols[q+1]:]
                        # если пробел найден
                        else:
                            # обрезаем строку до найденного пробела
                            partstr = partstr[:spacepos]
                            cols[q] = cols[q][spacepos+1:]
                    # если взята часть строки (разрыв на пробеле)
                    else:
                        # ислючить переносной пробел
                        cols[q] = cols[q][cols[q+1]+1:]
                    # выровнить текст по ширине колонки
                    partstr = partstr.ljust(cols[q+1], ' ')
                    #partstr = justify(partstr, cols[q+1])
                # остатки строки
                else:
                    # добавить отступы чтобы закончить колонку
                    partstr = partstr.ljust(cols[q+1], ' ')
                    cols[q] = ''

                retstr+= partstr + cellspacingS

                # остальную часть строки оставить на следующую итерацию
                # если от строки что то осаталось
                if len(cols[q]) > 0:
                    # отметить запуск еще одной итерации по параметрам
                    repeat = True
            # следующая пара
            q += 2
        # колонки отображены
        retstr += "\n"
    return retstr.encode('utf8')

def columnStr(*cols):
    '''Вывод данных по колонкам, причем, если данные не вмещаются в указнаную
    колонку, то они переносятся на следующую строку в нужную колонку. В строку.

    Параметры:
    cols     множестово пар: текст, ширина колонки, причем, если у последней
             колонки не указывать ширину, то она будет выведена вся.

    Возвращаемые параметры:
    строка, которую можно использовать для вывода на экран

    Пример: columnWrite( "Some text", 10, "Next column", 20 )
    '''
    retstr = ""
    # перевести кортеж в список, т.к. изменяется
    cols = list(cols)
    # перевести текст в юникод, заодно перевести числа в строку
    for i in xrange(0,len(cols),2):
        cols[i] = (str(cols[i])).decode('utf8')

    # флаг "есть еще текст для вывода"
    repeat = True
    while repeat:
        # сбросить итератор на первый элемент
        q = 0
        repeat = False
        # пока не закончили перебирать параметры (перебираем по парам)
        while q < len(cols):
            # если это последний параметр, и для него не указана ширина
            if q == len(cols)-1:
                # выводим его полностью не смотря на ширину окна
                retstr += cols[q] + " "
                cols[q] = ''
            else:
                # вывести часть строки не больше указанной ширины колонки
                retstr+=(cols[q][:cols[q+1]].ljust(cols[q+1])).encode('utf8') \
                    + " "
                # остальную часть строки оставить на следующую итерацию
                cols[q] = cols[q][cols[q+1]:]
                # если от строки что то осаталось
                if len(cols[q]) > 0:
                    # отметить запуск еще одной итерации по параметрам
                    repeat = True
            # следующая пара
            q += 2
        # колонки отображены
        retstr += "\n"
    return retstr

def columnWrite(*cols):
    '''Вывод данных по колонкам, причем, если данные не вмещаются в указнаную
    колонку, то они переносятся на следующую строку в нужную колонку.

    Параметры:
    cols     множестово пар: текст, ширина колонки, причем, если у последней
             колонки не указывать ширину, то она будет выведена вся.

    Пример: columnWrite( "Some text", 10, "Next column", 20 )
    '''
    # перевести кортеж в список, т.к. изменяется
    cols = list(cols)
    # перевести текст в юникод, заодно перевести числа в строку
    for i in xrange(0,len(cols),2):
        cols[i] = (str(cols[i])).decode('utf8')

    # флаг "есть еще текст для вывода"
    repeat = True
    while repeat:
        # сбросить итератор на первый элемент
        q = 0
        repeat = False
        # пока не закончили перебирать параметры (перебираем по парам)
        while q < len(cols):
            # если это последний параметр, и для него не указана ширина
            if q == len(cols)-1:
                # выводим его полностью несмотря на ширину окна
                cl_overriding.printSUCCESS(cols[q].encode('utf8'),
                                           printBR=False)
                cols[q] = ''
            else:
                # вывести часть строки не больше указанной ширины колонки
                cl_overriding.printSUCCESS(\
                    (cols[q][:cols[q+1]].ljust(cols[q+1])).encode('utf8') + " ",
                    printBR=False)
                # остальную часть строки оставить на следующую итерацию
                cols[q] = cols[q][cols[q+1]:]
                # если от строки что то осаталось
                if len(cols[q]) > 0:
                    # отметить запуск еще одной итерации по параметрам
                    repeat = True
            # следующая пара
            q += 2
        # колонки отображены
        cl_overriding.printSUCCESS('')

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:
    """Класс для выдачи данных в табличном виде"""
    def __init__(self, title, headerList, dataList):
        # Заголовок
        self.title = title
        # Cписок элементов первой строки таблицы
        self.headerList = headerList
        # Список строк (каждая строка - список элементов)
        self.dataList = dataList
        # Список ширин колонок
        self.columnsWidth = self.getColumsnWidth()

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

        результат список ширин колонок
        """
        columnsWidth = []
        lenCol = 0 
        for s in self.headerList:
            lenCol += 1
            columnsWidth.append(len(_toUNICODE(s)))
        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):
        """Создает список (текст, ширина ...)"""
        strList = []
        lenStr = len(listStr)
        for i in range(len(self.columnsWidth)):
            if lenStr > i:
                strList.append(listStr[i])
            else:
                strList.append("")
            strList.append(self.columnsWidth[i])
        return strList

    def printReport(self,printRows=True):
        """Напечатать данные в табличном виде"""
        if self.title:
            cl_overriding.printSUCCESS(self.title)
        listStrSep = []
        for lenCol in self.columnsWidth:
            listStrSep.append("-"*lenCol)
        printData = ""
        printData += columnStr(*self.createFormatStr(listStrSep))
        printData += columnStr(*self.createFormatStr(self.headerList))
        printData += columnStr(*self.createFormatStr(listStrSep))
        for s in self.dataList:
            printData += columnStr(*self.createFormatStr(s))
        printData += columnStr(*self.createFormatStr(listStrSep))
        if printData[-1] == "\n":
            printData = printData[:-1]
        lines = printData.splitlines()
        lenCols = map(lambda x: len(x), lines[0].strip().split(" "))
        convLines = []
        lenLines = len(lines)
        for i in range(lenLines):
            char = " | "
            if i == 0 or i == 2 or i == lenLines-1:
                char ="-+-"
            convLines.append(self._insertStrChar(lines[i], lenCols, char))
        cl_overriding.printSUCCESS("\n".join(convLines))
        if printRows:
            cl_overriding.printSUCCESS("(%s %s)"%(len(self.dataList), _("rows")))

    def _insertStrChar(self, line, lenCols, char):
        """Вставляет несколько символов char в указанные позиции

        сначала строки будет вставлено char[1:] в конце строки char[:-1]
        """
        lineUnicode = _toUNICODE(line)
        prevPos = 0
        convLine = char[1:]
        lenLenCols = len(lenCols)
        for i in range(lenLenCols):
            pos = lenCols[i] + prevPos
            if i == lenLenCols-1:
                insertChar = char[:-1]
            else:
                insertChar = char
            convLine += lineUnicode[prevPos:pos] + insertChar
            prevPos = pos + 1
        return convLine.encode("UTF-8")
