# Author:  mozman <mozman@gmx.at>
# Purpose: xfcell -- cell with convenient xf function
# Created: 04.12.2010
# Copyright (C) 2010, Manfred Moitzi
# License: BSD-style licence

"""
The XFCell() object contains the data for one cell.

WARNING: You don't call this class yourself. You access Cell objects
via methods of the Sheet object(s) that you found in the Book object that
was returned when you called xlrd.open_workbook("myfile.xls").

Cell objects have four attributes: `ctype` is an int, `value` (which depends
on `ctype`), `xf_index` and `sheet`, a reference to the containing sheet. If
**formatting_info** is not enabled when the workbook is opened, xf_index will
be **None**.

The following table describes the types of cells and how their values
are represented in Python.

=============== ===== ============ ==========================================
Type symbol     Const Python value Note
=============== ===== ============ ==========================================
XL_CELL_EMPTY   0     ""
XL_CELL_TEXT    1     str
XL_CELL_NUMBER  2     float
XL_CELL_DATE    3     float
XL_CELL_BOOLEAN 4     int          1 means TRUE, 0 means FALSE
XL_CELL_ERROR   5     int          representing internal Excel codes; for a
                                   text representation, refer to the supplied
                                   dictionary error_text_from_code
XL_CELL_BLANK   6     ""           this type will appear only when
                                   open_workbook(..., formatting_info=True)
                                   is used.
=============== ===== ============ ==========================================
"""

import datetime

from .xldate import xldate_as_tuple
from .biffh import XL_CELL_DATE, BaseObject

class XFCell(BaseObject):
    """ Extended Cell() class with convenient methods for easy access of cell
    properties.
    """
    __slots__ = ['sheet', 'ctype', 'value', 'xf']

    def __init__(self, ctype, value, xf_index=None, sheet=None):
        self.sheet = sheet
        self.ctype = ctype
        self.value = value

        if xf_index is not None:
            self.xf = self.book.xf_list[xf_index]
        else:
            self.xf = None

    @property
    def book(self):
        return self.sheet.book

    @property
    def has_xf(self):
        return (self.xf is not None)

    @property
    def xf_index(self):
        if self.has_xf:
            return self.xf.xf_index
        else:
            return None

    @property
    def parent_style(self):
        return self.book.xf_list[self.xf.parent_style_index]

    @property
    def is_datetime(self):
        return self.ctype == XL_CELL_DATE

    @property
    def has_date(self):
        if self.is_datetime:
            return self.value > 1.
        return False

    def get_color(self, index):
        return self.book.colour_map[index]

    def datetime(self):
        """ Returns a datetime.datetime object if cell type is XL_CELL_DATE
        else raises a TypeError, and raises ValueError if the the cell has
        not date value (only time value is present).
        """
        if self.is_datetime:
            if self.has_date:
                date = xldate_as_tuple(self.value, self.book.datemode)
                return datetime.datetime(*date)
            else:
                raise ValueError("Cell has no date value.")
        else:
            raise TypeError("Cell is not a XL_CELL_DATE.")

    def date(self):
        """ Returns a datetime.date object if cell type is XL_CELL_DATE
        else raises a **TypeError**. Raises **ValueError** if the cell
        doesn't have a date value (only time value is present).
        """
        dt = self.datetime()
        return dt.date()

    def time(self):
        """ Returns a datetime.time object if cell type is XL_CELL_DATE else
        raises a TypeError.
        """
        if self.is_datetime:
            date = xldate_as_tuple(self.value, self.book.datemode)
            return datetime.time(date[3], date[4], date[5])
        else:
            raise TypeError("Cell is not a XL_CELL_DATE.")

    #
    # access the XFBackground() class
    #

    @property
    def background(self):
        if self.xf.is_style and \
           self.xf._background_flag == 0:
            return self.xf.background
        elif self.xf._background_flag:
            return self.xf.background
        else:
            return self.parent_style.background

    def background_color(self):
        """ Get cell background-color as 3-tuple. """
        color_index = self.xf.background.background_colour_index
        return self.get_color(color_index)

    def fill_pattern(self):
        return self.xf.background.fill_pattern

    def pattern_color(self):
        color_index = self.xf.background.pattern_colour_index
        return self.get_color(color_index)

    #
    # access the Font() class
    #

    @property
    def font_index(self):
        if self.xf.is_style and \
           self.xf._font_flag == 0:
            return self.xf.font_index
        elif self.xf._font_flag:
            return self.xf.font_index
        else:
            return self.parent_style.font_index

    @property
    def font(self):
        """ Get the Font() class. """
        return self.book.font_list[self.xf.font_index]

    def font_color(self):
        """ Get cell foreground-color as 3-tuple. """
        return self.get_color(self.font.colour_index)

    #
    # access the Format() class
    #

    @property
    def format_key(self):
        if self.xf.is_style and \
           self.xf._format_flag == 0:
            return self.xf.format_key
        elif self.xf._format_flag:
            return self.xf.format_key
        else:
            return self.parent_style.format_key

    @property
    def format(self):
        """ Get the Format() class. """
        return self.book.format_map[self.format_key]

    def format_str(self):
        """ Get the associated 'format_str'. """
        return self.format.format_str

    #
    # access the XFAligment() class
    #

    @property
    def alignment(self):
        if self.xf.is_style and \
           self.xf._alignment_flag == 0:
            return self.xf.alignment
        elif self.xf._alignment_flag:
            return self.xf.alignment
        else:
            return self.parent_style.alignment

    #
    # access the XFBorder() class
    #

    @property
    def border(self):
        if self.xf.is_style and \
           self.xf._border_flag == 0:
            return self.xf.border
        elif self.xf._border_flag:
            return self.xf.border
        else:
            return self.parent_style.border

    def bordercolors(self):
        """ Get border color as dict of rgb-color-tuples. """
        border = self.border
        return {
            'top': self.get_color(border.top_colour_index),
            'bottom': self.get_color(border.bottom_colour_index),
            'left': self.get_color(border.left_colour_index),
            'right': self.get_color(border.right_colour_index),
            'diag': self.get_color(border.diag_colour_index),
        }

    def borderstyles(self):
        """ Get border styles as dict of ints. """
        border = self.border
        return {
            'top': border.top_line_style,
            'bottom': border.bottom_line_style,
            'left': border.left_line_style,
            'right': border.right_line_style,
            'diag': border.diag_line_style,
        }

    @property
    def has_up_diag(self):
        """ Draw a line across the cell from bottom left to top right. """
        return bool(self.border.diag_up)

    @property
    def has_down_diag(self):
        """ Draw a line across the cell from top left to bottom right. """
        return bool(self.border.diag_down)

    #
    # access the XFProtection() class
    #

    @property
    def protection(self):
        if self.xf.is_style and \
           self.xf._protection_flag == 0:
            return self.xf.protection
        elif self.xf._protection_flag:
            return self.xf.protection
        else:
            return self.parent_style.protection

    @property
    def is_cell_locked(self):
        return bool(self.protection.cell_locked)

    @property
    def is_formula_hidden(self):
        return bool(self.protection.cell_locked)
