# Copyright (c) 2003 by Intevation GmbH
# Authors:
# Jonathan Coles <jonathan@intevation.de>
#
# This program is free software under the GPL (>=v2)
# Read the file COPYING coming with Thuban for details.

"""The layer and table join dialog"""

__version__ = "$Revision: 2700 $"
# $Source$
# $Id: join.py 2700 2006-09-18 14:27:02Z dpinte $


import sys
import wx

from Thuban import _

from Thuban.Model.transientdb import TransientJoinedTable
from Thuban.Model.data import DerivedShapeStore
from Thuban.UI.tableview import QueryTableFrame

from common import ThubanBeginBusyCursor, ThubanEndBusyCursor

ID_LEFT_TABLE = 4001
ID_RIGHT_TABLE = 4002

CHOICE_WIDTH = 150

class JoinDialog(wx.Dialog):

    """Table join dialog.

    Join two tables (left/right) based on the user selected fields.
    Opens a QueryTableFrame and registers the new table with the session.
    """

    def __init__(self, parent, title, session, layer = None):
        wx.Dialog.__init__(self, parent, -1, title,
                          style = wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
        self.layer = layer

        if not layer:
            self.choice_left_table = wx.Choice(self, ID_LEFT_TABLE)
            width, height = self.choice_left_table.GetSizeTuple()
            self.choice_left_table.SetSize(wx.Size(CHOICE_WIDTH, height))
            self.left_table = None
        else:
            self.choice_left_table = None
            self.left_table = layer.ShapeStore().Table()

        self.choice_right_table = wx.Choice(self, ID_RIGHT_TABLE)
        width, height = self.choice_right_table.GetSizeTuple()
        self.choice_right_table.SetSize(wx.Size(CHOICE_WIDTH, height))

        self.choice_left_field = wx.Choice(self, -1)
        width, height = self.choice_left_field.GetSizeTuple()
        self.choice_left_field.SetSize(wx.Size(CHOICE_WIDTH, height))
        self.choice_right_field = wx.Choice(self, -1)
        width, height = self.choice_right_field.GetSizeTuple()
        self.choice_right_field.SetSize(wx.Size(CHOICE_WIDTH, height))

        self.button_join = wx.Button(self, wx.ID_OK, _("Join"))
        self.button_join.SetDefault()
        self.button_close = wx.Button(self, wx.ID_CANCEL, _("Close"))

        self.Bind(wx.EVT_BUTTON, self.OnJoin, id=wx.ID_OK)

        if self.choice_left_table is not None:
            self.choice_left_table.Append(_('Select...'), None)
        self.choice_right_table.Append(_('Select...'), None)

        for t in session.Tables():
            if self.choice_left_table is not None:
                self.choice_left_table.Append(t.Title(), t)

            # If there is no choice_left_table then self.left_table will
            # be the keft table so we can simply leave it out on the
            # right side.
            if t is not self.left_table:
                self.choice_right_table.Append(t.Title(), t)

        if self.choice_left_table is None:
            for col in self.left_table.Columns():
                self.choice_left_field.Append(col.name, col)

        if self.choice_left_table is not None:
            self.Bind(wx.EVT_CHOICE, self.OnLeftTable, id=ID_LEFT_TABLE)
        self.Bind(wx.EVT_CHOICE, self.OnRightTable, id=ID_RIGHT_TABLE)

        if self.choice_left_table is not None:
            self.choice_left_table.SetSelection(0)
        self.choice_right_table.SetSelection(0)
        self.button_join.Enable(False)

        topBox = wx.BoxSizer(wx.VERTICAL)

        sizer = wx.FlexGridSizer(2, 4)
        sizer.Add(wx.StaticText(self, -1, _("Table:")), 0, wx.ALL, 4)
        if self.choice_left_table is not None:
            sizer.Add(self.choice_left_table, 1,
                      wx.EXPAND|wx.ALL|wx.ALIGN_CENTER_VERTICAL, 4)
        else:
            sizer.Add(wx.StaticText(self, -1, self.left_table.Title()), 0,
                      wx.ALL, 4)

        sizer.Add(wx.StaticText(self, -1, _("Table:")), 0, wx.ALL, 4)
        sizer.Add(self.choice_right_table, 1,
                  wx.EXPAND|wx.ALL|wx.ALIGN_CENTER_VERTICAL, 4)
        sizer.Add(wx.StaticText(self, -1, _("Field:")), 0, wx.ALL, 4)
        sizer.Add(self.choice_left_field, 1,
                  wx.EXPAND|wx.ALL|wx.ALIGN_CENTER_VERTICAL, 4)
        sizer.Add(wx.StaticText(self, -1, _("Field:")), 0, wx.ALL, 4)
        sizer.Add(self.choice_right_field, 1,
                  wx.EXPAND|wx.ALL|wx.ALIGN_CENTER_VERTICAL, 4)

        sizer.AddGrowableCol(1)
        sizer.AddGrowableCol(3)

        topBox.Add(sizer, 0, wx.EXPAND|wx.ALL, 4)

        sizer = wx.BoxSizer(wx.HORIZONTAL)
        self.check_outer_join = wx.CheckBox(self,-1,
                                _("Outer Join (preserves left table records)"))
        sizer.Add(self.check_outer_join, 1, wx.ALL,4)
        topBox.Add(sizer, 0, wx.ALIGN_LEFT|wx.ALIGN_TOP, 4)

        sizer = wx.BoxSizer(wx.HORIZONTAL)
        sizer.Add(self.button_join, 0, wx.ALIGN_RIGHT|wx.ALIGN_BOTTOM|wx.RIGHT,
                  10)
        sizer.Add(self.button_close, 0, wx.ALIGN_RIGHT|wx.ALIGN_BOTTOM|wx.RIGHT,
                  10)

        topBox.Add(sizer, 1, wx.ALIGN_RIGHT|wx.ALIGN_BOTTOM|wx.BOTTOM|wx.TOP, 10)

        self.SetAutoLayout(True)
        self.SetSizer(topBox)
        topBox.Fit(self)
        topBox.SetSizeHints(self)

        # Save same parameters for later use.
        self.parent = parent
        self.session = session

    def OnJoin(self, event):
        """Get the table and field selections and perform the join."""
        ThubanBeginBusyCursor()
        try:
            # left
            if self.choice_left_table is not None:
                i = self.choice_left_table.GetSelection()
                left_table = self.choice_left_table.GetClientData(i)
            else:
                left_table = self.left_table
            i = self.choice_left_field.GetSelection()
            left_field  = self.choice_left_field.GetString(i)
            # right
            i = self.choice_right_table.GetSelection()
            right_table = self.choice_right_table.GetClientData(i)
            i = self.choice_right_field.GetSelection()
            right_field  = self.choice_right_field.GetString(i)

            outer_join = self.check_outer_join.IsChecked()

            try:
                joined_table = TransientJoinedTable(self.session.TransientDB(),
                                                    left_table, left_field,
                                                    right_table, right_field,
                                                    outer_join)
            except:
                dlg = wx.MessageDialog(None,
                                      _('Join failed:\n  %s') % sys.exc_info()[1],
                                      _('Info'), wx.OK|wx.ICON_ERROR)
                dlg.ShowModal()
                dlg.Destroy()
                return

            joined_table = self.session.AddTable(joined_table)

            if self.layer is not None:
                needed_rows = self.layer.ShapeStore().Table().NumRows()
                joined_rows = joined_table.NumRows()
                if needed_rows == joined_rows:
                    store = DerivedShapeStore(self.layer.ShapeStore(),
                                          joined_table)
                    self.session.AddShapeStore(store)
                    self.layer.SetShapeStore(store)

        finally:
            ThubanEndBusyCursor()

        if self.layer is not None:
            if needed_rows != joined_rows:
                msg = _("The joined table has %(joined)d rows but"
                        " it must have %(needed)d rows"
                        " to be used with the selected layer") \
                        % {"joined": joined_rows,
                           "needed": needed_rows}
                dlg = wx.MessageDialog(None, msg, _('Join Failed'),
                                      wx.OK|wx.ICON_ERROR)
                dlg.ShowModal()
                dlg.Destroy()
                return

        else:
            name = joined_table.tablename
            dialog = QueryTableFrame(self.parent, name,
                                     _('Table: %s') % joined_table.Title(),
                                     joined_table)
            self.parent.add_dialog(name, dialog)
            dialog.Show(True)

        self.Close()

    def OnClose(self, event):
        """Close the dialog."""
        self.Close()

    def OnLeftTable(self, event):
        """Get the selected table and fill the field choice."""
        i = self.choice_left_table.GetSelection()
        table = self.choice_left_table.GetClientData(i)
        self.choice_left_field.Clear()
        if table is not None:
            for col in table.Columns():
                self.choice_left_field.Append(col.name, col)
        self.update_sensitivity()

    def OnRightTable(self, event):
        """Get the selected table and fill the field choice."""
        i = self.choice_right_table.GetSelection()
        table = self.choice_right_table.GetClientData(i)
        self.choice_right_field.Clear()
        if table is not None:
            for col in table.Columns():
                self.choice_right_field.Append(col.name, col)
        self.update_sensitivity()

    def update_sensitivity(self):
        if self.choice_left_table is not None:
            lsel = self.choice_left_table.GetSelection()
        else:
            lsel = sys.maxint
        sel = self.choice_right_table.GetSelection()
        self.button_join.Enable(sel > 0 and lsel > 0 and sel != lsel)
