# -*- coding: iso-8859-1 -*-
# -----------------------------------------------------------------------
# info_area.py - An info area for the Freevo skin
# -----------------------------------------------------------------------
# $Id: info_area.py 10454 2008-02-28 19:29:22Z duncan $
#
# Notes:
# Todo:
#
# -----------------------------------------------------------------------
# Freevo - A Home Theater PC framework
# Copyright (C) 2002 Krister Lagerstrom, et al.
# Please see the file freevo/Docs/CREDITS for a complete list of authors.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MER-
# CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# -----------------------------------------------------------------------


import copy
import util
import re

from area import Skin_Area
from skin_utils import *
import xml_skin
from area import Geometry

import traceback

# function calls to get more info from the skin
function_calls = { 'comingup': util.comingup }

class Info_Area(Skin_Area):
    """
    this call defines the view area
    """

    def __init__(self):
        Skin_Area.__init__(self, 'info')
        self.last_item = None
        self.content = None
        self.layout_content = None
        self.list = None
        self.updated = 0
        self.sellist = None
        self.i18n_re = re.compile('^( ?)(.*?)([:,]?)( ?)$')
        self.do_i18n_re = re.compile('\w{2}')

    def update_content_needed(self):
        """
        check if the content needs an update
        """
        update = 0

        if self.layout_content is not self.layout.content:
            return True

        if self.last_item != self.infoitem:
            return True

        update += self.set_content()    # set self.content
        update += self.set_list(update) # set self.list

        try:
            list = self.eval_expressions(self.list)
        except:
            print "skin error: unable to parse expression in info_area"
            traceback.print_exc()
            return 0

        if self.sellist  != list:
            self.sellist = list
            update += 1

        if update:
            self.updated = 1
        return update


    def update_content(self):
        """
        update the info area
        """
        if not self.updated: # entered a menu for the first time
            self.set_list(self.set_content())
            try:
                self.sellist = self.eval_expressions(self.list)
            except:
                print "skin error: unable to parse expression in info_area"
                traceback.print_exc()
                return 0

        self.last_item = self.infoitem
        if not self.list: # nothing to draw
            self.updated = 0
            return

        # get items to be draw
        list = self.return_formatedtext(self.sellist)

        for i in list:
            if isinstance(i, xml_skin.FormatText):
                if i.y + i.height > self.content.height:
                    break
                self.drawstring(i.text,
                                 i.font, self.content,
                                 (self.content.x + i.x), (self.content.y + i.y),
                                 i.width , i.height,
                                 align_v = i.valign, align_h = i.align,
                                 mode = i.mode,
                                 ellipses = i.ellipses,
                                 dim = i.dim)

            elif isinstance(i, xml_skin.FormatImg):
                if i.src:
                    tmp = (self.content.x + i.x, self.content.y + i.y,
                            i.width, i.height)
                    self.drawimage(i.src, tmp)
                else:
                    print String(_("ERROR")) + ": " + String(_("missing 'src' attribute in skin tag!"))


        self.last_item = self.infoitem

        # always set this to 0 because we don't call update_content
        # when everything changes
        self.updated = 0


    def set_content(self):
        """
        set self.content and self.layout_content if they need to be set (return 1)
        or does nothing (return 0)
        """
        update=0
        if self.content and self.area_val and \
               (self.content.width != self.area_val.width or \
                self.content.height != self.area_val.height or \
                self.content.x != self.area_val.x or \
                self.content.y != self.area_val.y):
            update=1

        if self.layout_content is not self.layout.content or update:
            types = self.layout.content.types
            self.content = self.calc_geometry(self.layout.content, copy_object=True)
            # backup types, which have the previously calculated fcontent
            self.content.types = types
            self.layout_content = self.layout.content
            return 1
        return 0


    def set_list(self, force = 0):
        """
        set self.list if need (return 1) or does nothing (return 0)
        """
        if force or self.infoitem is not self.last_item or self.infoitem != self.last_item:
            key = 'default'
            if hasattr(self.infoitem, 'info_type'):
                key = self.infoitem.info_type or key

            elif hasattr(self.infoitem, 'type'):
                key = self.infoitem.type or key

            if self.content.types.has_key(key):
                val = self.content.types[ key ]
            else:
                val = self.content.types[ 'default' ]

            if not hasattr(val, 'fcontent'):
                self.list = None
                return 1

            self.list = val.fcontent
            return 1

        return 0



    def get_expression(self, expression):
        """
        create the python expression
        """
        exp = ''
        if expression.startswith('python:'):
            return expression.replace('python:', '', 1)

        for b in expression.split(' '):
            if b in ('and', 'or', 'not', '==', '!='):
                # valid operator
                exp += ' %s' % (b)

            elif b.startswith('\'') and b.endswith('\''):
                # string
                exp += ' %s' % (b)

            elif b.startswith('function:'):
                exp += ' %s()' % b[9:]

            elif b[ :4 ] == 'len(' and b.find(')') > 0 and \
                     len(b) - b.find(')') < 5:
                # lenght of something
                exp += ' attr("%s") %s' % (b[ : (b.find(')') + 1) ], b[ (b.find(')') + 1) : ])
            else:
                # an attribute
                exp += ' attr("%s")' % b

        return exp.strip()




    def eval_expressions(self, list, index=[ ]):
        """
        traverse the list evaluating the expressions,

        @returns: a flat list with valid elements indexes only (false 'if' expressions
        eliminated).

        Also, text elements are in the list too in a tuple: (index, 'text value') so
        you can check if it changed just comparing two lists (useful in music player,
        to update 'elapsed')
        """
        item = self.infoitem
        ret_list = [ ]

        if not list:
            return

        rg = range(len(list))
        for i in rg:
            if isinstance(list[ i ], xml_skin.FormatIf):
                if list[ i ].expression_analized == 0:
                    list[ i ].expression_analized = 1
                    exp = self.get_expression(list[ i ].expression)
                    list[ i ].expression = exp
                else:
                    exp = list[ i ].expression

                # Evaluate the expression:
                if exp and eval(exp, {'attr': item.getattr}, function_calls):
                    # It's true, we should recurse into children
                    ret_list += self.eval_expressions(list[ i ].content, index + [ i ])
                continue

            elif isinstance(list[ i ], xml_skin.FormatText):
                exp = None
                if list[ i ].expression:
                    if list[ i ].expression_analized == 0:
                        list[ i ].expression_analized = 1
                        exp = self.get_expression(list[ i ].expression)
                        list[ i ].expression = exp
                    else:
                        exp = list[ i ].expression
                    # evaluate the expression:
                    if exp:
                        exp = eval(exp, {'attr': item.getattr}, function_calls)
                        if not isstring(exp):
                            exp = str(exp)
                        if exp:
                            list[ i ].text = exp
                else:
                    # translate the text in the FormatText
                    if list[ i ].expression_analized == 0:
                        # not translated yet
                        list[ i ].expression_analized = 1
                        # translate if contains some alphanumerics
                        if self.do_i18n_re.match(list[i].text):
                            m = self.i18n_re.match(list[i].text).groups()
                            list[i].text = m[0] + _(m[1]) + m[2] + m[3]
                # I add a tuple here to be able to compare lists and know if we need to
                # update, this is useful in the mp3 player
                ret_list += [ index + [ (i, list[ i ].text) ] ]
            else:
                ret_list += [ index + [ i ] ]

        return ret_list





    def return_formatedtext(self, sel_list):
        """
        receives a list of indexex of elements to be used and
        returns a array with FormatText ready to print (with its elements
        x, y, width and height already calculated)
        """
        x, y = 0, 0

        item = self.infoitem

        list = self.list
        ret_list = [ ]
        last_newline = 0 # index of the last line
        for i in sel_list:
            newline = 0

            # find the element
            element = self.list
            for j in range(len(i) - 1):
                element = element[ i[ j ] ].content
            if isinstance(i[ -1 ], tuple):
                element = element[ i[ -1 ][ 0 ] ]
            else:
                element = element[ i[ -1 ] ]

            #
            # Tag: <goto_pos>
            #
            if isinstance(element, xml_skin.FormatGotopos):
                # move to pos
                if element.mode == 'absolute':
                    if element.x != None:
                        x = element.x
                    if element.y != None:
                        y = element.y
                else: # relative
                    if element.x != None:
                        x += element.x
                    if element.y != None:
                        y = y + element.y
            #
            # Tag: <img>
            #
            elif isinstance(element, xml_skin.FormatImg):
                element = copy.copy(element)
                # Image is a float object
                if element.x == None:
                    element.x = x

                if element.y == None:
                    element.y = y
                else:
                    my_y = y

                if element.width == None or element.height == None:
                    image = osd.loadbitmap(element.src, True)
                    size = image.get_size()

                    if element.width == None:
                        element.width = size[ 0 ]

                    if element.height == None:
                        element.height = size[ 1 ]

                ret_list += [ element ]

            #
            # Tag: <newline>
            #
            elif isinstance(element, xml_skin.FormatNewline):
                newline = 1 # newline height will be added later
                x = 0

            #
            # Tag: <text>
            #
            elif isinstance(element, xml_skin.FormatText):
                element = copy.copy(element)
                # text position is the current position:
                element.x = x
                element.y = y

                # Calculate the geometry
                r = Geometry(x, y, element.width, element.height)
                r = self.get_item_rectangle(r, self.content.width - x,
                                            self.content.height - y)[ 2 ]
                if element.height > 0:
                    height = min(r.height, element.height)
                else:
                    height = -1

                size = osd.drawstringframed(element.text, 0, 0,
                                             r.width, r.height,
                                             element.font, None, None,
                                             element.align, element.valign,
                                             element.mode, ellipses=element.ellipses,
                                             dim=element.dim, layer='')[ 1 ]
                m_width  = size[ 2 ] - size[ 0 ]
                m_height = size[ 3 ] - size[ 1 ]

                if isinstance(element.width, int):
                    if element.width <= 0:
                        element.width = min(m_width, r.width)
                elif element.align == 'left':
                    element.width = min(m_width, r.width)
                else:
                    element.width = r.width

                if isinstance(element.height, int) or element.height == 'line_height':
                    if element.height <= 0 or element.height == 'line_height':
                        element.height = m_height
                else:
                    element.height = min(m_height, r.height)

                x += element.width
                ret_list.append(element)


            # We should shrink the width and go next line (overflow)
            if x > self.content.width:
                x = 0
                newline = 1
                element.width = self.content.width - element.x


            # Need to recalculate line height?
            if newline and ret_list:
                newline_height = 0
                # find the tallest string
                new_last_newline = len(ret_list)
                last_line = ret_list[ last_newline : new_last_newline ]
                for j in last_line:
                    if isinstance(j, xml_skin.FormatText):
                        font = j.font
                        if j.text and j.height > newline_height:
                            newline_height = j.height

                y = y + newline_height
                last_newline = new_last_newline
                # update the height of the elements in this line,
                # so vertical alignment will be respected
                for j in last_line:
                    j.height = newline_height


        return ret_list
