#!/usr/bin/env python3

from typing import Any, Union

from .easymain import BaseObject
from .easydoc import LODocument


class LOCalcRange(object):
    CELL = 'ScCellObj'

    def __init__(self, obj):
        self._obj = obj
        self._is_cell = obj.ImplementationName == self.CELL

    def __getitem__(self, index):
        return LOCalcRange(self.obj[index])

    def __iter__(self):
        self._r = 0
        self._c = 0
        return self

    def __next__(self):
        try:
            rango = self[self._r, self._c]
        except Exception as e:
            raise StopIteration
        self._c += 1
        if self._c == self.obj.Columns.Count:
            self._c = 0
            self._r +=1
        return rango

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_value, traceback):
        pass

    def __len__(self):
        ra = self.range_address
        rows = ra.EndRow - ra.StartRow + 1
        cols = ra.EndColumn - ra.StartColumn + 1
        return rows, cols

    def __str__(self):
        s = f'Range: {self.name}'
        if self.is_cell:
            s = f'Cell: {self.name}'
        return s

    @property
    def obj(self):
        return self._obj

    @property
    def is_cell(self):
        return self._is_cell

    @property
    def name(self):
        return self.obj.AbsoluteName

    @property
    def address(self):
        return self.obj.CellAddress

    @property
    def range_address(self):
        return self.obj.RangeAddress

    @property
    def sheet(self):
        return LOCalcSheet(self.obj.Spreadsheet)

    @property
    def doc(self):
        doc = self.obj.Spreadsheet.DrawPage.Forms.Parent
        return LODocCalc(doc)

    @property
    def cursor(self):
        cursor = self.obj.Spreadsheet.createCursorByRange(self.obj)
        return cursor

    def offset(self, rows=0, cols=1):
        ra = self.range_address
        col = ra.EndColumn + cols
        row = ra.EndRow + rows
        return LOCalcRange(self.sheet[row, col].obj)

    @property
    def style(self):
        return self.obj.CellStyle
    @style.setter
    def style(self, value):
        self.obj.CellStyle = value

    @property
    def string(self):
        return self.obj.String
    @string.setter
    def string(self, value):
        self.obj.setString(value)

    @property
    def data(self):
        return self.obj.getDataArray()
    @data.setter
    def data(self, values):
        if self._is_cell:
            self.to_size(len(values[0]), len(values)).data = values
        else:
            self.obj.setDataArray(values)

    @property
    def current_region(self):
        cursor = self.cursor
        cursor.collapseToCurrentRegion()
        rango = self.obj.Spreadsheet[cursor.AbsoluteName]
        return LOCalcRange(rango)

    def to_size(self, cols: int, rows: int):
        cursor = self.cursor
        cursor.collapseToSize(cols, rows)
        rango = self.obj.Spreadsheet[cursor.AbsoluteName]
        return LOCalcRange(rango)


class LOCalcSheet(BaseObject):

    def __init__(self, obj):
        super().__init__(obj)

    def __getitem__(self, index):
        return LOCalcRange(self.obj[index])

    def __exit__(self, exc_type, exc_value, traceback):
        pass

    def __str__(self):
        return f'Sheet: {self.name}'

    @property
    def doc(self):
        return LODocCalc(self.obj.DrawPage.Forms.Parent)

    @property
    def name(self):
        return self._obj.Name
    @name.setter
    def name(self, value):
        self._obj.Name = value

    @property
    def code_name(self):
        return self._obj.CodeName
    @code_name.setter
    def code_name(self, value):
        self._obj.CodeName = value

    @property
    def visible(self):
        return self._obj.IsVisible
    @visible.setter
    def visible(self, value):
        self._obj.IsVisible = value

    @property
    def color(self):
        return self._obj.TabColor
    @color.setter
    def color(self, value):
        self._obj.TabColor = Color()(value)

    @property
    def events(self):
        return LOEvents(self.obj.Events)

    @property
    def used_area(self):
        cursor = self.create_cursor()
        cursor.gotoEndOfUsedArea(True)
        return self[cursor.AbsoluteName]

    @property
    def is_protected(self):
        return self._obj.isProtected()

    @property
    def password(self):
        return ''
    @password.setter
    def password(self, value):
        self.obj.protect(value)

    def unprotect(self, value):
        try:
            self.obj.unprotect(value)
            return True
        except:
            pass
        return False

    def move(self, pos: int=-1):
        index = pos
        if pos < 0:
            index = len(self.doc)
        self.doc.move(self.name, index)
        return

    def remove(self):
        self.doc.remove(self.name)
        return

    def copy(self, new_name: str='', pos: int=-1):
        index = pos
        if pos < 0:
            index = len(self.doc)
        new_sheet = self.doc.copy_sheet(self.name, new_name, index)
        return new_sheet

    def copy_to(self, doc: Any, target: str='', pos: int=-1):
        index = pos
        if pos < 0:
            index = len(doc)

        new_name = target or self.name
        sheet = doc.copy_from(self.doc, self.name, new_name, index)
        return sheet

    def activate(self):
        self.doc.activate(self.obj)
        return

    def create_cursor(self, rango: Any=None):
        if rango is None:
            cursor = self.obj.createCursor()
        else:
            obj = rango
            if hasattr(rango, 'obj'):
                obj = rango.obj
            cursor = self.obj.createCursorByRange(obj)
        return cursor


class LOCalc(LODocument):
    """Classe for Calc module"""
    TYPE_RANGES = ('ScCellObj', 'ScCellRangeObj')
    RANGES = 'ScCellRangesObj'
    SHAPE = 'com.sun.star.drawing.SvxShapeCollection'
    _type = 'calc'

    def __init__(self, obj):
        super().__init__(obj)
        self._sheets = obj.Sheets
        self._listener_range_selection = None

    def __getitem__(self, index):
        """Index access"""
        return LOCalcSheet(self._sheets[index])

    def insert(self, name: Union[str, list, tuple]):
        """Insert new sheet

        :param name: Name new sheet, or iterable with names.
        :type name: str, list or tuple
        :return: New last instance sheet.
        :rtype: LOCalcSheet
        """
        names = name
        if isinstance(name, str):
            names = (name,)
        for n in names:
            self._sheets[n] = self._create_instance('com.sun.star.sheet.Spreadsheet')
        return LOCalcSheet(self._sheets[n])
