# Flexinode handler -- a flexible, rule-agnostic, character node
#
# Copyright (C) 2009 David Vrabel
#

from core import *
from flexi_parser import flexiexpr_parser, flexiexpr_error

class flexinode_handler(node_handler):
    def __init__(self, xml_dom, tree_node):
        node_handler.__init__(self, xml_dom, tree_node)

        self.expressions = []
        self.action = None

        self.load_children()

    def load_children(self):
        children = self.master_dom._get_childNodes()
        for c in children:
            if c.tagName == "expression":
                expr = flexiexpr(self, c)
                self.add_expression(expr)
            elif c.tagName == "action":
                if not self.action:
                    self.action = flexiaction(self, c)
            else:
                self.tree.load_xml(c, self.mytree_node);
        if not self.action:
            self.action = flexiaction(self)

    def add_expression(self, expr):
        self.expressions.append(expr)

    def del_expression(self, expr):
        self.expressions.remove(expr)
        expr.delete()

    def find_expr(self, var):
        return self.__find_expr(var, self)

    def __find_expr(self, var, requester):
        expr = None
        for e in self.expressions:
            if e.variable == var:
                expr = e
                break;
        if not expr:
            e = None
            c = self.tree.GetFirstChild(self.mytree_node)[0]
            while c.IsOk():
                n = self.tree.GetPyData(c)
                if isinstance(n, flexinode_handler) and n != requester:
                    expr = n.__find_expr(var, self)
                    if expr:
                        break
                c = self.tree.GetNextSibling(c)
            if not expr:
                parent = self.parent()
                if parent and parent != requester:
                    expr = parent.__find_expr(var, self)
        return expr

    def name(self):
        return self.master_dom.getAttribute("name")

    def path(self):
        if self.parent() == None:
            return self.name()
        return self.parent().path() + " / " + self.name()

    def parent(self):
        """Return the parent flexinode or None.
        """
        parent_node = self.tree.GetItemParent(self.mytree_node)
        parent = self.tree.GetPyData(parent_node)
        if isinstance(parent, flexinode_handler):
            return parent
        return None

    def on_use(self, evt):
        if self.action.text != "":
            try:
                action_text = self.action.parse()
                if action_text[0] == ":":
                    text = action_text[1:]
                else:
                    text = "<b>" + self.name() + ":</b> " + action_text
                    self.chat.ParsePost(text, True, True)
            except flexiexpr_error, e:
                text = "<i>Flexinode error: expression " + e.str + "</i>"
                self.chat.ParsePost(text, False, True)

    def on_design(self, evt):
        if not self.myeditor or self.myeditor.destroyed:
            self.myeditor = flexinode_frame(self)
        self.myeditor.Show()
        self.myeditor.Raise()

class flexiexpr(object):
    """Docs
    """
    def __init__(self, fnode, node=None):
        self.fnode = fnode
        if not node:
            self.expr_node = fnode.tree.xml_doc.createElement("expression")
            self.expr_node.setAttribute("variable", "")
            self.fnode.master_dom.appendChild(self.expr_node)
        else:
            self.expr_node = node
        self.value_node = safe_get_text_node(self.expr_node)
        self.parser = flexiexpr_parser(self)

    def __repr__ (self):
        return self.variable + " = " + self.value

    def __get_var(self):
        return self.expr_node.getAttribute("variable")

    def __set_var(self, var):
        self.expr_node.setAttribute("variable", var)

    variable = property(__get_var, __set_var)

    def __get_val(self):
        return self.value_node.data

    def __set_val(self, v):
        self.value_node.data = v

    value = property(__get_val, __set_val)

    def eval(self):
        return self.parser.parse(self.value)

    def delete(self):
        self.fnode.master_dom.removeChild(self.expr_node)

class flexiaction(object):
    def __init__(self, fnode, action_node = None):
        self.fnode = fnode
        if not action_node:
            action_node = fnode.tree.xml_doc.createElement("action")
            self.fnode.master_dom.appendChild(action_node)
        self.text_node = action_node._get_firstChild()
        if self.text_node == None:
            self.text_node = minidom.Text("")
            self.text_node = action_node.appendChild(self.text_node)

        self.dice_re = re.compile('\[([^]]+)\]')

    def __get_text(self):
        return self.text_node.data

    def __set_text(self, text):
        self.text_node.data = text

    text = property(__get_text, __set_text)

    def parse(self):
        return self.dice_re.sub(flexirollparser(self.fnode), self.text)

class flexirollparser:
    def __init__(self, fnode):
        self.fnode = fnode
        self.var_re = re.compile('([^a-zA-Z0-9]|^\s*)([a-zA-Z_][a-zA-Z0-9_]*)')

    def __call__(self, match):
        roll = match.group(1)
        roll = self.var_re.sub(flexigetvar(self.fnode), roll)
        return "[" + roll + "]"

class flexigetvar:
    def __init__(self, fnode):
        self.fnode = fnode

    def __call__(self, match):
        var = match.group(2)
        expr = self.fnode.find_expr(var)
        if expr:
            return match.group(1) + str(expr.eval())
        return var

class flexinode_frame(wx.Frame):
    def __init__(self, fnode):
        wx.Frame.__init__(self, fnode.tree, 0, fnode.path())
        self.panel = flexinode_panel(self, fnode)

class flexinode_panel(wx.Panel):
    def __init__(self, parent, fnode):
        wx.Panel.__init__(self, parent)

        self.flexinode = fnode
        
        self.vbox = wx.BoxSizer(wx.VERTICAL)

        action_label = wx.StaticText(self, 0, "Action")
        self.action_text = wx.TextCtrl(self, 0, self.flexinode.action.text)

        self.expr_sizer = flexinode_expr_sizer(self, fnode)
        self.vbox.Add(self.expr_sizer, 0, wx.EXPAND)

        hbox = wx.BoxSizer(wx.HORIZONTAL)
        hbox.Add(action_label, 0, wx.ALIGN_CENTER_VERTICAL)
        hbox.Add(self.action_text, 1, wx.EXPAND)

        self.vbox.Add(hbox, 0, wx.EXPAND)

        self.action_text.Bind(wx.EVT_TEXT, self.on_action_text)

        self.vbox.SetMinSize((400, 0))
        self.SetSizer(self.vbox)
        self.adjust_size()

    def adjust_size(self):
        self.vbox.Layout()
        self.vbox.SetSizeHints(self.GetParent())
        self.GetParent().Fit()

    def on_action_text(self, evt):
        self.flexinode.action.text = self.action_text.GetValue()


class flexinode_expr_sizer(wx.StaticBoxSizer):
    def __init__(self, panel, fnode):
        wx.StaticBoxSizer.__init__(self, wx.StaticBox(panel, label="Expressions"), wx.VERTICAL)

        self.panel = panel
        self.flexinode = fnode
        self.num_expressions = 0

        hbox = wx.BoxSizer(wx.HORIZONTAL)
        self.plus_btn = wx.Button(self.panel, 0, '+', style=wx.BU_EXACTFIT)
        hbox.Add((0,0), 1, wx.EXPAND)
        hbox.Add(self.plus_btn, 0)
        self.Add(hbox, 0, wx.EXPAND)

        for expr in self.flexinode.expressions:
            self.add_expr(expr)

        self.plus_btn.Bind(wx.EVT_BUTTON, self.on_plus_button)

    def add_expr(self, expr):
        expr_box = flexinode_expr_box(self.panel, expr)
        self.Insert(self.num_expressions, expr_box, 0, wx.EXPAND)
        self.num_expressions = self.num_expressions + 1
        expr_box.val_text.MoveBeforeInTabOrder(self.panel.action_text)
        expr_box.var_text.MoveBeforeInTabOrder(expr_box.val_text)
        expr_box.minus_btn.MoveBeforeInTabOrder(self.plus_btn)

    def del_expr(self, expr_box):
        expr_box.Clear(True)
        self.Remove(expr_box)
        self.panel.adjust_size()
        self.num_expressions = self.num_expressions - 1

    def on_plus_button(self, evt):
        expr = flexiexpr(self.flexinode)
        self.flexinode.add_expression(expr)
        self.add_expr(expr)
        self.panel.adjust_size()


class flexinode_expr_box(wx.BoxSizer):
    def __init__(self, window, expr):
        wx.BoxSizer.__init__(self, wx.HORIZONTAL)

        self.window = window
        self.expr = expr

        self.var_text = wx.TextCtrl(window, 0, self.expr.variable)
        self.eq_label = wx.StaticText(window, 0, "=")
        self.val_text = wx.TextCtrl(window, 0, self.expr.value)
        self.minus_btn = wx.Button(window, 0, u'\u2212', style=wx.BU_EXACTFIT) # minus

        self.Add(self.var_text, 1, wx.EXPAND)
        self.Add(self.eq_label, 0, wx.ALIGN_CENTRE_VERTICAL)
        self.Add(self.val_text, 3, wx.EXPAND)
        self.Add(self.minus_btn, 0, wx.ALIGN_CENTRE_VERTICAL)

        self.minus_btn.Bind(wx.EVT_BUTTON, self.on_minus_button)
        self.var_text.Bind(wx.EVT_TEXT, self.on_variable_text)
        self.val_text.Bind(wx.EVT_TEXT, self.on_value_text)

    def on_minus_button(self, evt):
        self.window.flexinode.del_expression(self.expr)
        self.window.expr_sizer.del_expr(self)

    def on_variable_text(self, evt):
        self.expr.variable = self.var_text.GetValue()

    def on_value_text(self, evt):
        self.expr.value = self.val_text.GetValue()
