# Copyright (c) 2001, 2003, 2004 by Intevation GmbH
# Authors:
# Martin Mueller <mmueller@intevation.de>
# Bernhard Herzog <bh@intevation.de>
#
# This program is free software under the GPL (>=v2)
# Read the file COPYING coming with Thuban for details.


"""Dialogs to manage database connections"""

import sys, traceback

from wxPython.wx import *

try:
    import ogr
except ImportError:
    ogr = None

from Thuban import _
from Thuban.Model.table import FIELDTYPE_INT
from Extensions.ogr import ogrshapes


ID_DBCHOOSE_RETRIEVE = 9201
ID_DBCHOOSE_OK       = 9202
ID_DBCHOOSE_CANCEL   = 9203
ID_LB_DCLICK         = 9204


class ChooseFileFormat(wxDialog):
    """This dialog lists all available drivers.
    """
    def __init__(self, parent, session):
        """Initialize the dialog.
        """
        wxDialog.__init__(self, parent, -1, _("Choose file format"),
                          style = wxDIALOG_MODAL|wxCAPTION)
        self.session = session
        self.tables = []

        #
        # Build the dialog
        #

        # Sizer for the entire dialog
        top = wxFlexGridSizer(2, 1, 0, 0)

        # Sizer for the main part with the list box
        main_sizer = wxBoxSizer(wxHORIZONTAL)
        top.Add(main_sizer, 1, wxEXPAND, 0)

        # The list box with the drivers
        static_box = wxStaticBoxSizer(wxStaticBox(self, -1, "File formats"),
                                   wxHORIZONTAL)
        self.lb_drivers = wxListBox(self, -1)
        static_box.Add(self.lb_drivers, 0, wxEXPAND, 0)
        main_sizer.Add(static_box, 1, wxEXPAND, 0)

        for i in range(ogr.GetDriverCount()):
            self.lb_drivers.Append(ogr.GetDriver(i).GetName())
        if self.lb_drivers.GetCount() > 0:
            self.lb_drivers.SetSelection(0, True)

        # The standard button box at the bottom of the dialog
        buttons = wxFlexGridSizer(1, 2, 0, 0)
        ok_button = wxButton(self, ID_DBCHOOSE_OK, _("OK"))
        EVT_BUTTON(self, ID_DBCHOOSE_OK, self.OnOK)
        buttons.Add(ok_button, 0, wxALL|wxALIGN_RIGHT, 4)
        cancel_button = wxButton(self, ID_DBCHOOSE_CANCEL, _("Cancel"))
        EVT_BUTTON(self, ID_DBCHOOSE_CANCEL, self.OnCancel)
        buttons.Add(cancel_button, 0, wxALL, 4)
        top.Add(buttons, 1, wxALL|wxALIGN_CENTER_HORIZONTAL, 4)

        # Autosizing
        self.SetAutoLayout(1)
        self.SetSizer(top)
        top.Fit(self)
        top.SetSizeHints(self)
        self.Layout()


    def GetTable(self):
        """no functionality
        """
        return None

    def OnLBDClick(self, event):
        """Close dialog.
        """
        if self.lb_tables.GetSelection() >= 0:
            self.EndModal(wxID_OK)
            self.Show(False)

    def OnOK(self, event):
        """Close dialog.
        """
        self.EndModal(wxID_OK)
        self.Show(False)

    def OnCancel(self, event):
        """Close dialog.
        """
        self.EndModal(wxID_CANCEL)
        self.Show(False)


class ChooseLayer(wxDialog):
    """This dialog lists all the layers contained in the given datasource.

    One layer can be chosen, which is then opened.
    """

    def __init__(self, parent, filename):
        """Initialize the dialog.
        """
        wxDialog.__init__(self, parent, -1, _("Choose layer"),
                          style = wxDIALOG_MODAL|wxCAPTION)
        self.tables = []

        #
        # Build the dialog
        #

        # Sizer for the entire dialog
        top = wxFlexGridSizer(2, 1, 0, 0)

        # Sizer for the main part with the list boxes
        main_sizer = wxBoxSizer(wxHORIZONTAL)
        top.Add(main_sizer, 1, wxEXPAND, 0)

        # The list box with the drivers
        static_box = wxStaticBoxSizer(wxStaticBox(self, -1, _("Layers")),
                                   wxHORIZONTAL)
        self.lb_drivers = wxListBox(self, -1)
        static_box.Add(self.lb_drivers, 0, wxEXPAND, 0)
        main_sizer.Add(static_box, 1, wxEXPAND, 0)

        datasource = ogr.Open(filename)
        self.layer = []
        for i in range(datasource.GetLayerCount()):
            self.layer.append(datasource.GetLayer(i))
            self.lb_drivers.Append(datasource.GetLayer(i).GetName())
        if self.lb_drivers.GetCount() > 0:
            self.lb_drivers.SetSelection(0, True)

        # The standard button box at the bottom of the dialog
        buttons = wxFlexGridSizer(1, 2, 0, 0)
        ok_button = wxButton(self, ID_DBCHOOSE_OK, _("OK"))
        EVT_BUTTON(self, ID_DBCHOOSE_OK, self.OnOK)
        buttons.Add(ok_button, 0, wxALL|wxALIGN_RIGHT, 4)
        cancel_button = wxButton(self, ID_DBCHOOSE_CANCEL, _("Cancel"))
        EVT_BUTTON(self, ID_DBCHOOSE_CANCEL, self.OnCancel)
        buttons.Add(cancel_button, 0, wxALL, 4)
        top.Add(buttons, 1, wxALL|wxALIGN_CENTER_HORIZONTAL, 4)

        # Autosizing
        self.SetAutoLayout(1)
        self.SetSizer(top)
        top.Fit(self)
        top.SetSizeHints(self)
        self.Layout()

    def end_dialog(self, result):
        """If the dialog is closed with OK, set chosen layer as layer 
        to be opened.
        """
        self.result = result
        if result is not None:
            self.EndModal(wxID_OK)
        else:
            self.EndModal(wxID_CANCEL)
        self.Show(False)

    def OnOK(self, event):
        """Dialog closed with OK button.
        """
        self.end_dialog(self.lb_drivers.GetSelection())

    def OnCancel(self, event):
        """Dialog closed with Cancel.
        """
        self.end_dialog(None)

    def GetLayer(self):
        """Return the selected layer."""
        return self.layer[self.lb_drivers.GetSelection()].GetName()


class ChooseOGRDBTableDialog(wxDialog):
    """This dialog opens a datasource from an existing database connection.

    A list of all available database connections is offered. If one connection
    is selected and the button "Retrieve" is clicked, all layers are listed.
    One of these layers can be chosen to be opened.
    An ID column can be chosen, too."""

    def __init__(self, parent, session):
        """Initialize the dialog.
        """
        wxDialog.__init__(self, parent, -1, _("Choose layer from database"),
                          style = wxDIALOG_MODAL|wxCAPTION)
        self.session = session
        self.dbconns = self.session.DBConnections()
        self.tables = []

        #
        # Build the dialog
        #

        # Sizer for the entire dialog
        top = wxFlexGridSizer(2, 1, 0, 0)

        # Sizer for the main part with the list boxes
        main_sizer = wxBoxSizer(wxHORIZONTAL)
        top.Add(main_sizer, 1, wxEXPAND, 0)

        # The list box with the connections
        static_box = wxStaticBoxSizer(wxStaticBox(self, -1, _("Databases")),
                                   wxHORIZONTAL)
        self.lb_connections = wxListBox(self, -1)
        static_box.Add(self.lb_connections, 0, wxEXPAND, 0)
        main_sizer.Add(static_box, 1, wxEXPAND, 0)

        for i in range(len(self.dbconns)):
            self.lb_connections.Append(self.dbconns[i].BriefDescription())
        if self.lb_connections.GetCount() > 0:
            self.lb_connections.SetSelection(0, True)

        # The button box between the connections list box and the table
        # list box
        buttons = wxFlexGridSizer(3, 1, 0, 0)
        buttons.Add(20, 80, 0, wxEXPAND, 0)
        retrieve_button = wxButton(self, ID_DBCHOOSE_RETRIEVE, _("Retrieve"))
        EVT_BUTTON(self, ID_DBCHOOSE_RETRIEVE, self.OnRetrieve)
        buttons.Add(retrieve_button, 0, wxALL
                    |wxALIGN_CENTER_HORIZONTAL|wxALIGN_CENTER_VERTICAL, 4)
        buttons.Add(20, 80, 0, wxEXPAND, 0)
        main_sizer.Add(buttons, 0, wxEXPAND, 0)

        # The list box with the tables
        static_box = wxStaticBoxSizer(wxStaticBox(self, -1, _("Tables")),
                                   wxHORIZONTAL)
        self.lb_tables = wxListBox(self, ID_LB_DCLICK)
        EVT_LISTBOX(self, ID_LB_DCLICK, self.OnTableSelect)
        EVT_LISTBOX_DCLICK(self, ID_LB_DCLICK, self.OnLBDClick)
        static_box.Add(self.lb_tables, 0, wxEXPAND, 0)
        main_sizer.Add(static_box, 1, wxEXPAND, 0)

        # id column and geometry column selection
        box = wxBoxSizer(wxVERTICAL)
        box.Add(wxStaticText(self, -1, _("ID Column")), 0,
                wxALL|wxALIGN_CENTER_VERTICAL, 4)
        self.text_id_column = wxComboBox(self, -1, "")
        box.Add(self.text_id_column, 0,
                wxALL|wxALIGN_CENTER_VERTICAL|wxEXPAND, 4)
        main_sizer.Add(box, 1, wxEXPAND, 0)

        # The standard button box at the bottom of the dialog
        buttons = wxFlexGridSizer(1, 2, 0, 0)
        ok_button = wxButton(self, ID_DBCHOOSE_OK, _("OK"))
        EVT_BUTTON(self, ID_DBCHOOSE_OK, self.OnOK)
        buttons.Add(ok_button, 0, wxALL|wxALIGN_RIGHT, 4)
        cancel_button = wxButton(self, ID_DBCHOOSE_CANCEL, _("Cancel"))
        EVT_BUTTON(self, ID_DBCHOOSE_CANCEL, self.OnCancel)
        buttons.Add(cancel_button, 0, wxALL, 4)
        top.Add(buttons, 1, wxALL|wxALIGN_CENTER_HORIZONTAL, 4)

        # Autosizing
        self.SetAutoLayout(1)
        self.SetSizer(top)
        top.Fit(self)
        top.SetSizeHints(self)
        self.Layout()


    def GetTable(self):
        """Return the chosen DB connection, the table to be opened, 
        the connection string for ogr and the ID column.
        """
        i = self.lb_tables.GetSelection()
        if i >= 0:
            return (self.selected_conn, self.connString, self.tables[i],
                    self.text_id_column.GetValue())
        return None

    def OnRetrieve(self, event):
        """Provide a list of all available layers in the selected datasource.
        """
        i = self.lb_connections.GetSelection()
        if i >= 0:
            self.selected_conn = self.dbconns[i]
            self.connString = "PG: dbname=%s" %self.selected_conn.dbname
            if self.selected_conn.host is not "":
                self.connString = (self.connString + " host=%s"
                                    %self.selected_conn.host)
            if self.selected_conn.user is not "":
                self.connString = (self.connString + " user=%s"
                                    %self.selected_conn.user)
            if self.selected_conn.password is not "":
                self.connString = (self.connString + " password=%s"
                                    %self.selected_conn.password)
            if self.selected_conn.port is not "":
                self.connString = (self.connString + " port= %s"
                                    %self.selected_conn.port)
            ds = ogr.Open(self.connString)
            if ds:
                for i in range(ds.GetLayerCount()):
                    self.tables.append(ds.GetLayer(i).GetName())
                self.lb_tables.Set(self.tables)

    def OnTableSelect(self, event):
        """If a table is selected, list all possible ID columns.
        """
        i = self.lb_tables.GetSelection()
        self.text_id_column.Clear()
        if i >= 0:
            for name, typ in self.selected_conn.table_columns(self.tables[i]):
                if typ == FIELDTYPE_INT:
                    self.text_id_column.Append(name)

    def OnLBDClick(self, event):
        """Close dialog.
        """
        if self.lb_tables.GetSelection() >= 0:
            self.EndModal(wxID_OK)
            self.Show(False)

    def OnOK(self, event):
        """Dialog closed with OK button.
        """
        self.EndModal(wxID_OK)
        self.Show(False)

    def OnCancel(self, event):
        """Dialog closed with Cancel.
        """
        self.EndModal(wxID_CANCEL)
        self.Show(False)


class OGRConnectionDialog(wxDialog):
    """A string can be enteres, which is directly passed to ogr to open a
    datasource.
    """
    def __init__(self, parent, session):
        """Initialize the dialog.
        """
        wxDialog.__init__(self, parent, -1, "Enter string for OGRConnection",
                          style = wxDIALOG_MODAL|wxCAPTION)
        self.session = session

        # Sizer for the entire dialog
        top = wxBoxSizer(wxVERTICAL)

        # The list box with the drivers
        box = wxBoxSizer(wxHORIZONTAL)#wxBox(self, -1, _("OGRConnection")),
                            #       wxHORIZONTAL)
        box.Add(wxStaticText(self, -1, _("URL:")), 0,
                wxALL|wxALIGN_CENTER_VERTICAL, 4)
        self.text_string = wxTextCtrl(self, -1, "")
        box.Add(self.text_string, 0, wxEXPAND, 0)
        top.Add(box, 0, wxEXPAND)

        # The standard button box at the bottom of the dialog
        buttons = wxFlexGridSizer(1, 2, 0, 0)
        ok_button = wxButton(self, ID_DBCHOOSE_OK, _("OK"))
        EVT_BUTTON(self, ID_DBCHOOSE_OK, self.OnOK)
        buttons.Add(ok_button, 0, wxALL|wxALIGN_RIGHT, 4)
        cancel_button = wxButton(self, ID_DBCHOOSE_CANCEL, _("Cancel"))
        EVT_BUTTON(self, ID_DBCHOOSE_CANCEL, self.OnCancel)
        buttons.Add(cancel_button, 0, wxALL, 4)
        top.Add(buttons, 1, wxALL|wxALIGN_CENTER_HORIZONTAL, 4)

        # Autosizing
        self.SetAutoLayout(1)
        self.SetSizer(top)
        top.Fit(self)
        top.SetSizeHints(self)
        self.Layout()

    def RunDialog(self):
        """Run dialog
        """
        self.ShowModal()
        self.Destroy()
        return self.result

    def end_dialog(self, result):
        """Close dialog
        """
        self.result = result
        if result is not None:
            self.EndModal(wxID_OK)
        else:
            self.EndModal(wxID_CANCEL)
        self.Show(False)

    def OnOK(self, event):
        """Dialog closed with OK
        """
        result = {}
        result["string"] = getattr(self, "text_string").GetValue()
        self.end_dialog(result)

    def OnCancel(self, event):
        """Dialog closed with Cancel.
        """
        self.end_dialog(None)

    def GetDatasourceName(self):
        """Return the string to be used for opening the database
        """
        return self.result["string"]

