# 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: 1.14 $"
# $Source: /thubanrepository/thuban/Thuban/UI/join.py,v $
# $Id: join.py,v 1.14 2003/12/23 11:37:04 bh Exp $


import sys
from wxPython.wx import *

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(wxDialog):

    """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):
        wxDialog.__init__(self, parent, -1, title,
                          style = wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER)
        self.layer = layer

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

        self.choice_right_table = wxChoice(self, ID_RIGHT_TABLE)
        width, height = self.choice_right_table.GetSizeTuple()
        self.choice_right_table.SetSize(wxSize(CHOICE_WIDTH, height))

        self.choice_left_field = wxChoice(self, -1)
        width, height = self.choice_left_field.GetSizeTuple()
        self.choice_left_field.SetSize(wxSize(CHOICE_WIDTH, height))
        self.choice_right_field = wxChoice(self, -1)
        width, height = self.choice_right_field.GetSizeTuple()
        self.choice_right_field.SetSize(wxSize(CHOICE_WIDTH, height))

        self.button_join = wxButton(self, wxID_OK, _("Join"))
        self.button_join.SetDefault()
        self.button_close = wxButton(self, wxID_CANCEL, _("Close"))

        EVT_BUTTON(self, wxID_OK, self.OnJoin)

        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:
            EVT_CHOICE(self, ID_LEFT_TABLE, self.OnLeftTable)
        EVT_CHOICE(self, ID_RIGHT_TABLE, self.OnRightTable)

        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 = wxBoxSizer(wxVERTICAL)

        sizer = wxFlexGridSizer(2, 4)
        sizer.Add(wxStaticText(self, -1, _("Table:")), 0, wxALL, 4)
        if self.choice_left_table is not None:
            sizer.Add(self.choice_left_table, 1,
                      wxEXPAND|wxALL|wxALIGN_CENTER_VERTICAL, 4)
        else:
            sizer.Add(wxStaticText(self, -1, self.left_table.Title()), 0,
                      wxALL, 4)

        sizer.Add(wxStaticText(self, -1, _("Table:")), 0, wxALL, 4)
        sizer.Add(self.choice_right_table, 1,
                  wxEXPAND|wxALL|wxALIGN_CENTER_VERTICAL, 4)
        sizer.Add(wxStaticText(self, -1, _("Field:")), 0, wxALL, 4)
        sizer.Add(self.choice_left_field, 1,
                  wxEXPAND|wxALL|wxALIGN_CENTER_VERTICAL, 4)
        sizer.Add(wxStaticText(self, -1, _("Field:")), 0, wxALL, 4)
        sizer.Add(self.choice_right_field, 1,
                  wxEXPAND|wxALL|wxALIGN_CENTER_VERTICAL, 4)

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

        topBox.Add(sizer, 0, wxEXPAND|wxALL, 4)

        sizer = wxBoxSizer(wxHORIZONTAL)
        self.check_outer_join = wxCheckBox(self,-1,
                                _("Outer Join (preserves left table records)"))
        sizer.Add(self.check_outer_join, 1, wxALL,4)
        topBox.Add(sizer, 0, wxALIGN_LEFT|wxALIGN_TOP, 4)

        sizer = wxBoxSizer(wxHORIZONTAL)
        sizer.Add(self.button_join, 0, wxALIGN_RIGHT|wxALIGN_BOTTOM|wxRIGHT,
                  10)
        sizer.Add(self.button_close, 0, wxALIGN_RIGHT|wxALIGN_BOTTOM|wxRIGHT,
                  10)

        topBox.Add(sizer, 1, wxALIGN_RIGHT|wxALIGN_BOTTOM|wxBOTTOM|wxTOP, 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 = wxMessageDialog(None,
                                      _('Join failed:\n  %s') % sys.exc_info()[1],
                                      _('Info'), wxOK|wxICON_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 = wxMessageDialog(None, msg, _('Join Failed'),
                                      wxOK|wxICON_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)
