#-*- coding: utf-8 -*-
#!/usr/bin/python

import xlrd
import time
import sys
import logging

from six import string_types

from collections import OrderedDict

from tqdm import tqdm
from tqdm import trange


logger = logging.getLogger(__name__)


class Nimbus(object):

    def __init__(self, file, sheet, *args, **kwargs):
        super(Nimbus, self).__init__()
        self.args = args
        self.kwargs = kwargs
        self.file = file
        self.sheet = sheet
        self.config = "Configs"
        self.tip = ''
        self.mapping_typo_rules = OrderedDict((
            ('\n', ';'),(',', ';'),('"', ''),("'", ""),('\\xa0', ' ')
        ))
        self.mapping_header_rules = (
            ('OEM PN', 'PPN')
        )
        self.sequence_data = self._sequence_data()
        self.coordinate = self._coordinate(data=self.sequence_data)

    @property
    def seq_data(self):
        return self.sequence_data

    @property
    def row_data(self):
        all_data = xlrd.open_workbook(self.file, encoding_override='utf8')
        data = all_data.sheet_by_name(self.sheet)
        return data

    def _sequence_data(self, Column=None):
        data = self.row_data
        if Column is None:
            self.Column = []
        else:
            self.Column = Column
        for i in trange(data.nrows, desc='seq rows in memory'):
            Rows = {}
            for cell in trange(data.ncols, desc='seq cols in memory', leave=False):
                Rows[cell] = data.row_values(i)[cell]
            self.Column.append(Rows)
        return self.Column

    def _coordinate(self, data):
        """return Build Matrix coordinate
        parm:
            row_start_point, int
            col_title_length, int
            sku_start_point, int
            sku_end_point, int
            sku_col_start_point, int
        """
        Init_row, row_site, col_site, sku_row_site = 0, 0, 0, 0
        Init_col = True
        #data = self.sequence_data
        for row in range(len(data)):
            if Init_col:
                for col in range(len(data[row])):
                    if str(data[row][col]).upper() == str(self.config).upper():
                        sku_row_site, col_site = row, col
                        Init_col = False
                        break
            if isinstance(data[row][0], string_types):
                if len(data[row][0]) != 0:
                    Init_row = Init_row + 1 
                if Init_row == 3: 
                    row_site = row
                    break
        coordinate_json = {
            'row_start_point': int(row_site),
            'col_title_length': int(col_site)+1,
            'sku_start_point': int(sku_row_site),
            'sku_end_point': int(row_site)-1,
            'sku_col_start_point': int(col_site)
        }
        return coordinate_json

    @property
    def matrix_data(self, Column=None):
        data = self.sequence_data
        header = self._header_matrix_mapping(column=data)
        coordinate = self.coordinate
        row_start_point = coordinate['row_start_point']
        col_title_length = coordinate['col_title_length']
        if Column is None:
            self.Column = []
        else:
            self.Column = Column
        
        title, comp = '', ''
        for row in trange(len(data), desc='matrix rows in memory'):
            if row > row_start_point:
                if data[row][header[self.item]] == None or data[row][header[self.item]] == '':
                    Rows, config = {}, ''
                    for cell in trange(len(data[row]), desc='matrix cols in memory', leave=False):
                        if cell < col_title_length:
                            Rows[cell] = data[row][cell]
                        if cell >= col_title_length and str(data[row][cell]).lower() != self.tip:
                            config += data[coordinate['row_start_point']][cell] + ';'
                        # title
                        if cell == header[self.item]:
                            Rows[cell] = title
                        # component
                        if cell == header[self.component]:
                            if data[row][header[self.component]] == None or data[row][header[self.component]] == '':
                                Rows[cell] = comp
                            else:
                                comp = data[row][header[self.component]]
                    Rows[col_title_length] = config
                    self.Column.append(Rows)
                else:
                    title = data[row][header[self.item]]
        return {'title': header, 'data': self.Column}

    @property
    def config_data(self, Column=None, configColumn=None):
        data = self.sequence_data
        coordinate = self.coordinate
        sku_start_point = coordinate['sku_start_point'] #config 標頭開始
        sku_end_point = coordinate['sku_end_point'] #config 標頭結束
        sku_col_start_point = coordinate['sku_col_start_point'] #config 每列開始
        if Column is None:
            self.Column = []
        else:
            self.Column = Column
        if configColumn is None:
            self.configColumn = []
        else:
            self.configColumn = configColumn
        
        for i in tqdm(range(len(data))): # config 資料每一行去存
            if i >= sku_start_point and i <= sku_end_point:
                rows = OrderedDict()
                for k, v in dict(data[i]).items():
                    if k >= sku_col_start_point:
                        rows.setdefault(k, v)
                self.Column.append(rows)

        for i in trange(sku_col_start_point+1, sku_col_start_point+len(self.Column[0]), desc='config rows in memory'):
            rows = OrderedDict()
            for row in trange(len(self.Column), desc='config cols in memory', leave=False):
                for k, v in dict(self.Column[row]).items():
                    if k == i:
                        rows.setdefault(row, str(v).upper())
            self.configColumn.append(rows)
        return {'column': self.Column, 'configcolumn': self.configColumn}

    def replace_data(self, data, rules):
        for k, v in rules.items(): #根據 self.mapping_rules 取代值 
            data = data.replace(k, v)
        return data

    def _insert_rows(self, row, rules, data=None):
        if data is None:
            self.data = []
        else:
            self.data = data
        for k, v in row.items():
            v = self.replace_data(data=repr(str(v)), rules=rules) #取代值
            self.data.append(v)
        return self.data

    def _matrix_output(self, output):
        matrix = self.matrix_data
        title = matrix['title']
        data = matrix['data']
        coordinate = self.coordinate
        col_title_length = coordinate['col_title_length']

        with open(output, 'w') as fp:
            for k, v in title.items(): #title
                if str(k).upper() == 'OEM PN':
                    fp.write('PPN,')
                else:
                    if v < col_title_length:
                        k = self.replace_data(data=k, rules=self.mapping_typo_rules) #取代值
                        fp.write(str(k) + ',')
                    elif v == col_title_length:
                        fp.write('CONFIG,\n')
            
            for row in tqdm(range(len(data))):
                insert_row = self._insert_rows(row=data[row], rules=self.mapping_typo_rules)
                fp.write(','.join(insert_row) + '\n')
        return 'matrix data output Success.'        

    def _config_output(self, output):
        config_data = self.config_data
        column = config_data['column']
        title = self._header_config_mapping(column=column)
        data = config_data['configcolumn']
        
        with open(output, 'w') as fp:
            insert_row = self._insert_rows(row=title, rules=self.mapping_typo_rules) #title
            fp.write(','.join(insert_row) + '\n')
            
            for row in tqdm(range(len(data))): #輸出內容
                insert_row = self._insert_rows(row=data[row], rules=self.mapping_typo_rules)
                fp.write(','.join(insert_row) + '\n')
        return 'config data output Success.'

    def _config_list_output(self, output):
        config_data = self.config_data
        data = config_data['configcolumn']
        configs = []
        for row in tqdm(range(len(data))):
            for k, v in dict(data[row]).items(): 
                if k == 0 and v not in configs: 
                    configs.append(v)
        with open(output, 'w') as fp2: #輸出所有 config
            fp2.write(';'.join(configs))
        return 'config list data output Success.'

    def _header_matrix_mapping(self, column, data=None):
        self.column = column
        coordinate = self.coordinate
        row_start_point = coordinate['row_start_point']
        if data is None:
            self.data = OrderedDict()
        else:
            self.data = data
        
        for k, v in dict(self.column[row_start_point]).items(): 
            count = 1
            while v in self.data.keys(): # 處理重複的值
                v = v + '_' + str(count)
                count += 1
            self.data.setdefault(str(v).upper(), k)
        return self.data

    def _header_config_mapping(self, column, data=None):
        self.column = column
        coordinate = self.coordinate
        sku_col_start_point = coordinate['sku_col_start_point']
        if data is None:
            self.data = OrderedDict()
        else:
            self.data = data

        for row in tqdm(range(len(self.column))):
            for k, v in dict(self.column[row]).items():
                if k == sku_col_start_point:
                    self.data.setdefault(row, str(v).upper())
        return self.data

    def __del__(self):
        pass


class NimbusFATP(Nimbus):

    def __init__(self, file, sheet, *args, **kwargs):
        super(NimbusFATP, self).__init__(file, sheet)
        self.args = args
        self.kwargs = kwargs
        self.file = file
        self.sheet = sheet
        self.item = 'ITEM'
        self.component = 'COMPONENT'

    def matrix_output(self, output='MatrixFATP.csv'):
        return self._matrix_output(output=output)

    def config_output(self, output='MatrixFATP_config.csv'):
        return self._config_output(output=output)

    def config_list_output(self, output='MatrixFATP_config_list.csv'):
        return self._config_list_output(output=output)

    def __del__(self):
        pass


class NimbusMLB(Nimbus):

    def __init__(self, file, sheet, *args, **kwargs):
        super(NimbusMLB, self).__init__(file, sheet)
        self.args = args
        self.kwargs = kwargs
        self.file = file
        self.sheet = sheet
        self.item = 'ITEM'
        self.component = 'REF DES'

    def matrix_output(self, output='MatrixMLB.csv'):
        return self._matrix_output(output=output)

    def config_output(self, output='MatrixMLB_config.csv'):
        return self._config_output(output=output)

    def config_list_output(self, output='MatrixMLB_config_list.csv'):
        return self._config_list_output(output=output)

    def __del__(self):
        pass

