import os, re, openpyxl
from openpyxl.cell import MergedCell
from openpyxl.utils import get_column_letter, column_index_from_string


def col_index_from_string(string):
    return column_index_from_string(string) - 1


def row_index_from_string(string):
    return int(string) - 1


class Excel:
    __readonly__ = None
    __autosave__ = None
    __filename__ = None
    __workbook__ = None
    __workshet__ = None

    def open(self, filename=None, sheet=None, read_only=False, auto_save=False):
        """打开Excel文档 | Open Excel document.
        :param filename:  将要打开的Excel文档的文件路径 | The file path of the Excel document that will be opened.
        :param sheet:     将要打开的工作表名称或索引，默认为当前活动工作表 | The name or index of the worksheet to open, defaults to the currently active worksheet.
        :param read_only: 只读模式 | Read-only mode.
        :param auto_save: 自动保存 | Auto save when modified.
        :return:
        """
        self.__readonly__ = read_only
        if self.__workbook__:
            raise Exception('文件已经打开 | File has been opened.')
        if filename:
            self.__filename__ = os.path.abspath(filename)
            self.__workbook__ = openpyxl.load_workbook(filename, read_only=False)
            self.__autosave__ = auto_save
            self.select(sheet)
        else:
            self.__workbook__ = openpyxl.Workbook()
            self.__readonly__ = False
            self.__autosave__ = False
            self.select(None)
            self.sheetRename('Sheet1')
        return self

    def select(self, sheet):
        """选择活动工作表 | Select active worksheet.
        :param sheet: 工作表名称或索引或工作表对象，默认为当前活动工作表 | Sheet name or index or sheet object, defaults to the currently active sheet.
        :return:
        """
        if isinstance(sheet, int):
            self.__workshet__ = self.__workbook__[self.sheetList()[sheet]]
        elif isinstance(sheet, str):
            self.__workshet__ = self.__workbook__[sheet]
        elif not sheet:
            self.__workshet__ = self.__workbook__.active
        else:
            self.__workshet__ = sheet
        if not self.__readonly__:
            self.__workbook__.active = self.__workshet__
        return self

    def sheetName(self):
        """返回当前工作表名称 | Returns the current sheet name.
        :return:
        """
        return self.__workshet__.title

    def sheetList(self):
        """返回当前工作簿的工作表名称列表 | Returns a list of sheet names for the current workbook.
        :return:
        """
        return self.__workbook__.sheetnames

    def sheetNrow(self):
        """获取当前工作表的最大有效行 | Get the maximum valid number of rows for the current worksheet.
        :return:
        """
        return self.__workshet__.max_row

    def sheetNcol(self):
        """获取当前工作表的最大有效列 | Get the maximum valid number of cols for the current worksheet.
        :return:
        """
        return self.__workshet__.max_column

    def sheetRename(self, name=None):
        if not name:
            name = 'Sheet'
        self.__workshet__.title = name
        self.saveAuto()
        return True

    def sheetCreate(self, name=None):
        if not name:
            currentList = self.sheetList()
            nextNo = len(currentList) + 1
            for i in range(0, 99):
                name = 'Sheet' + str(nextNo)
                if name in currentList:
                    nextNo = nextNo + 1
                    continue
                else:
                    break
        self.__workbook__.create_sheet(name)
        self.select(name)
        self.saveAuto()
        return self

    def sheetDelete(self):
        self.__workbook__.remove(self.__workshet__)
        self.select(0)
        self.saveAuto()
        return self

    def cellSelect(self, cell):
        """设置活动单元格 | Set active cell.
        :param cell:
        :return:
        """
        if not self.__readonly__:
            try:
                cell_letter = cell.column_letter + str(cell.row)
                self.__workshet__.sheet_view.selection[0].activeCell = cell_letter
                self.__workshet__.sheet_view.selection[0].sqref = cell_letter
            except:
                pass

    def cellGet(self, row: int = None, col: int = None, cell: str = None):
        if cell:
            cell_object = self.__workshet__[cell]
        else:
            if row < 0 or col < 0:
                raise Exception('行或列值必须大于等于0 | Row or column values must be at least 0.')
            cell_object = self.__workshet__.cell(row=row + 1, column=col + 1)
        cell_object = self._parseMergedCell(cell_object)
        self.cellSelect(cell=cell_object)
        return cell_object.value

    def cellPut(self, row: int = None, col: int = None, cell: str = None, value=None):
        if cell:
            cell_object = self.__workshet__[cell]
        else:
            if row < 0 or col < 0:
                raise Exception('行或列值必须大于等于0 | Row or column values must be at least 0.')
            cell_object = self.__workshet__.cell(row=row + 1, column=col + 1)
        cell_object = self._parseMergedCell(cell_object)
        self.cellSelect(cell=cell_object)
        cell_object.value = value
        self.saveAuto()
        return True

    def cellGetView(self, area='', from_col=0, from_row=0, to_col=0, to_row=0):
        cellList = self._viewGetOfCell(area=area, from_col=from_col, from_row=from_row, to_col=to_col, to_row=to_row)
        result = []
        for lineList in cellList:
            lineResult = []
            for cellObject in lineList:
                lineResult.append(cellObject.value)
            result.append(lineResult)
        return result

    def cellPutView(self, area='', from_col=0, from_row=0, to_col=0, to_row=0, value=None):
        if not value or not isinstance(value, list):
            return False
        values = self._viewGetOfCell(area=area, from_col=from_col, from_row=from_row, to_col=to_col, to_row=to_row)
        valuesLen = len(values)
        for i in range(0, valuesLen):
            online = values[i]
            onlineLen = len(online)
            for j in range(0, onlineLen):
                oncell = online[j]
                try:
                    oncell.value = value[i][j]
                except:
                    pass
        self.saveAuto()
        return True

    def save(self, filename=None):
        """保存 | Save.
        :param filename: 另存为的文件路径，默认为保存文件 | The file path to save as, the default is to save the file.
        :return:
        """
        if filename is None and self.__readonly__:
            raise Exception('只读模式 | Read-only')
        self.__workbook__.save(filename or self.__filename__)
        return True

    def saveAuto(self):
        if self.__autosave__:
            return self.save()

    def saveExit(self):
        """保存并退出 | Save and exit.
        :return:
        """
        self.save()
        self.exit()
        return True

    def exit(self):
        """退出 | Close the workbook and exit.
        :return:
        """
        self.__workbook__.close()
        self.__workshet__ = None
        self.__workbook__ = None
        self.__filename__ = None
        self.__autosave__ = None
        self.__readonly__ = None
        return True

    def _viewGetOfCell(self, area='', from_col=0, from_row=0, to_col=0, to_row=0):
        minCol = from_col
        minRow = from_row
        maxCol = to_col
        maxRow = to_row
        if area:
            area = area + ':'
            area = area.upper()
            area = area.split(':')
            minCell = area[0]
            maxCell = area[1]
            minColList = re.findall('([A-Z]+)', minCell)
            minRowList = re.findall('([0-9]+)', minCell)
            maxColList = re.findall('([A-Z]+)', maxCell)
            maxRowList = re.findall('([0-9]+)', maxCell)
            minColString = (minColList and minColList[0]) or ''
            minRowString = (minRowList and minRowList[0]) or ''
            maxColString = (maxColList and maxColList[0]) or ''
            maxRowString = (maxRowList and maxRowList[0]) or ''
            ncols = self.sheetNcol()
            nrows = self.sheetNrow()
            if minCell:
                minCol = minColString or 'A'
                minCol = column_index_from_string(minCol) - 1
                minRow = minRowString or '1'
                minRow = int(minRow) - 1
            if maxCell:
                if minColString and minRowString:
                    maxCol = maxColString or get_column_letter(ncols)
                    maxCol = column_index_from_string(maxCol) - 1
                    maxRow = maxRowString or nrows
                    maxRow = int(maxRow) - 1
                elif minColString and minRowString == '':
                    maxCol = column_index_from_string(maxColString) - 1
                    maxRow = nrows - 1
                elif minRowString and minColString == '':
                    maxCol = ncols - 1
                    maxRow = int(maxRowString) - 1
            else:
                if minColString and minRowString:
                    maxCol = minCol
                    maxRow = minRow
                elif minColString and minRowString == '':
                    maxCol = minCol
                    maxRow = nrows - 1
                elif minRowString and minColString == '':
                    maxCol = ncols - 1
                    maxRow = minRow
        cellList = self.__workshet__._cells_by_row(min_col=minCol + 1, min_row=minRow + 1,
                                                   max_col=maxCol + 1, max_row=maxRow + 1)
        result = []
        for lineList in cellList:
            lineResult = []
            for cellObject in lineList:
                lineResult.append(self._parseMergedCell(cellObject))
            result.append(lineResult)
        return result

    def _parseMergedCell(self, cell):
        if isinstance(cell, MergedCell):
            for mergeArea in self.__workshet__.merged_cells.ranges:
                if cell.coordinate in mergeArea:
                    cell = self.__workshet__.cell(row=mergeArea.min_row, column=mergeArea.min_col)
        return cell


if __name__ == '__main__':
    # Example.
    # 创建新的Excel文件
    # excel = Excel().open()
    # 打开现有Excel文件
    # excel = Excel().open(filename='./example.xlsx')
    excel = Excel().open(filename='./example.xlsx', read_only=False, auto_save=False)
    print(excel.sheetList())
    excel.select(0)
    excel.select('Sheet1')
    print(excel.cellGet(cell='A1'))
    print(excel.cellPut(cell='A1', value='标题'))
    print(excel.cellGetView(area='A1:F5'))
    print(excel.cellGetView(area='A:C'))
    print(excel.cellGetView(area='1:5'))
    print(excel.cellGetView(area='A1'))
    print(excel.cellGetView(area='A'))
    print(excel.cellGetView(area='1'))
    print(excel.cellPutView(area='A1:Z256', value=
    [
        ['1', '2', '3', '4', '5'],
        ['A', 'B', 'C', 'D', 'E'],
        ['A', 'B', 'C', 'D', 'E'],
        ['A', 'B', 'C', 'D', 'E']
    ]
                            ))
    print(excel.sheetRename('表一'))
    print(excel.sheetCreate('表二'))
    print(excel.sheetCreate('表三'))
    print(excel.sheetCreate('表四'))
    print(excel.sheetCreate('表五'))
    print(excel.sheetCreate('测试'))
    print(excel.select('测试').sheetDelete())
    print(excel.sheetList())
    print(excel.sheetName())
    print(excel.sheetNrow())
    print(excel.sheetNcol())
    print(excel.save())
    print(excel.save('./example_backup.xlsx'))
    print(excel.exit())
