# Copyright (C) 2003-2005 by Intevation GmbH
# Authors:
# Jan-Oliver Wagner <jan@intevation.de>
#
# This program is free software under the GPL (>=v2)
# Read the file COPYING coming with Thuban for details.

"""
Classes for ArcView Objects as in '.apr'-files.

The classes are only added to this module if they
are considered to be complete and whenever possible
accompanied by unit tests (see tests/).
Experimental classes should remain in importAPR.py.
"""

__version__ = "$Revision: 2629 $"

from math import ceil

from Thuban.Model.color import Color, Transparent, Black
from Thuban.Model.range import Range
from Thuban.Model.classification import ClassGroupProperties

from odb import ODBBaseObject

class APR_BLnSym(ODBBaseObject):
    """Line symbol object.
    Always references a color object TClr via 'Color'.

    The stroke width 'Width' is always given, but the scale is
    unclear so far (e.g. which width is actually meant with value '0.1'?).
    Meanwhile, the ceiling of 'Width' is applied to be stroke width
    in pixels.

    Finally, there seems always to be a 'Pattern'-List consisting of
    float values. No idea so far, how to interpret this (Thuban does
    not support pattern yet anyway).
    """
    _obj_refs = [ 'Color' ]
    _values = [ 'Width', 'Pattern' ]

    def GetThubanProp(self):
        """Create a Thuban ClassGroupProperty from this object and
        return it.
        """
        prop = ClassGroupProperties()
        prop.SetLineColor(self.Get('Color').GetThubanColor())
        prop.SetLineWidth(int(ceil(float(self.Get('Width')))))
        return prop

class APR_BMkSym(ODBBaseObject):
    """Point symbol object.
    Always references a Color and a Background Color via 'Color', 'BgColor'.
    For Thuban, Color is interpreted as line color and BGColor is
    interpreted as fill color.

    Next, there is always a 'Font' reference. Probably this defines
    the font for the label. This is not interpreted for Thuban.

    There is always a Size element. It is not clear how 'size'
    defined in ArcView.

    There is always a Angle element, but I don't know how this is
    defined. I only sighted the value of 360 so far.


    Finally, there seems always to be a 'Pattern'-List consisting of
    float values. No idea so far, how to interpret this (Thuban does
    not support pattern yet anyway).
    """

    _obj_refs = [ 'Color', 'BgColor', 'Font' ]
    _values = [ 'Angle', 'Size', 'Pattern' ]

    def GetThubanProp(self):
        """Create a Thuban ClassGroupProperty from this object and
        return it.

        In Thuban, the points have all the same size,
        but we can vary the width of the line.
        """
        prop = ClassGroupProperties()
        prop.SetSize(int(ceil(float(self.Get('Size')))))
        prop.SetLineColor(self.Get('Color').GetThubanColor())
        prop.SetFill(self.Get('BgColor').GetThubanColor())
        return prop

class APR_BShSym(ODBBaseObject):
    """Polygon symbol object, either filled with a single color or
    with a pattern.
    .
    Always references TClr objects via 'Color', 'OutlineColor' and 'BgColor'.
    Always has attributes 'OutlineWidth' and 'Outline'.

    OutlineColor is interpreted to be the Thuban line color, OutlineWidth
    as the Thuban line width.
    'Color' is interpreted to be the Thuban fill color.
    'BgColor' is not interpreted and probably has something to do with
    patterns (Stripple).
    'Stripple' ist not interpreted in Thuban. It is a pattern definition
    based on a bitpattern. Thuban has no Patterns yet.

    It is unclear what 'Outline' defines and thus is not used for Tuban.
    """
    _obj_refs = [ 'Color', 'OutlineColor', 'BgColor', 'Stripple' ]
    _values = [ 'OutlineWidth', 'Outline' ]

    def GetThubanProp(self):
        """Create a Thuban ClassGroupProperty from this object and
        return it.
        """
        prop = ClassGroupProperties()
        prop.SetLineWidth(int(ceil(float(self.Get('OutlineWidth')))))
        prop.SetLineColor(self.Get('OutlineColor').GetThubanColor())
        prop.SetFill(self.Get('Color').GetThubanColor())
        return prop

class APR_LClass(ODBBaseObject):
    """This object describes the range and label of a class
    within a legend.

    'IsText' determines whether 'MinStr'/'MaxStr' are given, else
    'MinNum'/'MaxNum' should be there.
    So far, only String-Ranges with identical 'MinStr'/'MaxStr' have
    been sighted.

    'MinNum' may not be there. In this case assume it to be -oo.

    There are objects with 'IsNoData' set to 1. Not yet sure how to
    treat them.

    However, objects have been sighted that only have 'IsText' and
    'Precision': We assume an empty label and a full range.

    No referenced objects.
    """
    _obj_refs = [ ]
    _values = [ 'IsText', 'MinStr', 'MaxStr', 'MinNum', 'MaxNum', 'Label',
                'Precision', 'IsNoData' ]

    def GetThubanRange(self):
        """Return a Thuban range that corresponds to this object.

        The returned object is a
        - Range-Object in case of a numerical range.
        - String-Object in case of a text range (assuming that
          text objects occur only with 'MinStr' == 'MaxStr'.
        """
        if hasattr(self, 'IsText'):
            if hasattr(self, 'MinStr'):
                return self.MinStr
            else:
                return ''

        # build range
        if hasattr(self, 'MinNum'):
            range_str = ']' + self.MinNum + ';'
        else:
            range_str = ']-oo;'
        if hasattr(self, 'MaxNum'):
            range_str = range_str + self.MaxNum + ']'
        else:
            range_str = None

        if hasattr(self, 'MinNum') and hasattr(self, 'MaxNum'):
            if self.MinNum == self.MaxNum:
                range_str = '[' + self.MinNum + ';' + self.MaxNum + ']'
        return Range(range_str)

    def GetLabel(self):
        """Return the label string.
        Return an  empty string if there is no 'Label'.
        """
        if hasattr(self, 'Label'):
            return self.Label
        else:
            return ''


class APR_TClr(ODBBaseObject):
    """Color object. Appears in 3 styles:
    1. no attributes: (so far assumed as black)
    2. only 'Name': a string that describes the color.
       Seen only "Transparent".
    3. 'Red', 'Green', 'Blue': RGB code. Each value in '0xffff' style.
       3.1 Only one or two of the colors are defined. It is assumed
           that in this case the rest is equal to 0x0000
       3.2 Some hex-codes are incomplete (eg. 0xff). It is assumed
           that the missing digits are "0".

    No referenced objects.
    """
    _obj_refs = [ ]
    _values = [ 'Name', 'Red', 'Green', 'Blue' ]

    def GetThubanColor(self):
        """Return a Thuban Color object; returns None if a problem
        occured.
        """
        if hasattr(self, 'Red') or hasattr(self, 'Green') or \
            hasattr(self, 'Blue'):
            rgb = { 'Red': 0, 'Green': 0, 'Blue': 0 } # default for missing
                                                      # parts: 0x0000

            for color in [ 'Red', 'Green', 'Blue' ]:
                if hasattr(self, color):
                    s = getattr(self, color)
                    # It seems that ArcView sometimes uses only
                    # 2 bytes for a color definition, eg. 0xff.
                    # It is assumed that this is the same as
                    # 0x00ff (and not the same as 0xff00). At
                    # least color comparison shows this.
                    # Thus we do not need to append "00" if length
                    # of s is < 6. The following conversion does is
                    # right even for the short strings.
                    rgb[color] = int(s, 16)/float(int('0xffff', 16))
            return Color(rgb['Red'], rgb['Green'], rgb['Blue'])
        elif hasattr(self, 'Name'):
            if self.Name == 'Transparent':
                return Transparent
            else:
                return None
        else:
            return Black
