"""
@package web_services.dialogs

@brief Dialogs for web services.

List of classes:
 - dialogs::WSDialogBase
 - dialogs::AddWSDialog
 - dialogs::WSPropertiesDialog
 - dialogs::SaveWMSLayerDialog

(C) 2009-2013 by the GRASS Development Team

This program is free software under the GNU General Public License
(>=v2). Read the file COPYING that comes with GRASS for details.

@author Martin Landa <landa.martin gmail.com>
@author Stepan Turek <stepan.turek seznam.cz>
"""

import wx

import os
import sys
import shutil

from copy      import deepcopy

import grass.script as grass

from core             import globalvar
from core.debug       import Debug
from core.gcmd        import GMessage, GWarning, GError, RunCommand
from core.utils       import GetSettingsPath, CmdToTuple, CmdTupleToList, _
from core.gconsole    import CmdThread, GStderr, EVT_CMD_DONE, EVT_CMD_OUTPUT

from gui_core.gselect import Select

from web_services.widgets import WSPanel, WSManageSettingsWidget

class WSDialogBase(wx.Dialog):
    """Base class for web service dialogs. 
    """
    def __init__(self, parent, id = wx.ID_ANY,
                 style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs):

        wx.Dialog.__init__(self, parent, id, style = style, **kwargs)

        self.parent = parent 

        # contains panel for every web service on server
        self.ws_panels =  {'WMS_1.1.1'  : {'panel' : None,
                                           'label' : 'WMS 1.1.1'},
                           'WMS_1.3.0' : {'panel' : None,
                                          'label' : 'WMS 1.3.0'},
                           'WMTS' : {'panel' : None,
                                     'label' : 'WMTS'},
                           'OnEarth' : {'panel' : None,
                                        'label' : 'OnEarth'},
                          }

        #TODO: should be in file
        self.default_servers = { 'OSM-WMS-EUROPE' : ['http://watzmann-geog.urz.uni-heidelberg.de/cached/osm', '', ''],
                                 'irs.gis-lab.info (OSM)' : ['http://irs.gis-lab.info', '', ''],
                                 'NASA OnEarth' : ['http://onearth.jpl.nasa.gov/wms.cgi', '', '']
                               }

        # holds reference to web service panel which is showed
        self.active_ws_panel = None

        # buttons which are disabled when the dialog is not connected
        self.run_btns = []

        # stores error messages for GError dialog showed when all web service connections were unsuccessful
        self.error_msgs = ''

        self._createWidgets()
        self._doLayout()

    def _createWidgets(self):

        settingsFile = os.path.join(GetSettingsPath(), 'wxWS')

        self.settsManager = WSManageSettingsWidget(parent=self,
                                                 settingsFile=settingsFile,
                                                 default_servers=self.default_servers)

        self.settingsBox = wx.StaticBox(parent = self, 
                                        id = wx.ID_ANY,
                                        label = _(" Server settings "))
        
        self.serverText = wx.StaticText(parent = self, 
                                        id = wx.ID_ANY, label = _("Server:"))
        self.server  = wx.TextCtrl(parent = self, id = wx.ID_ANY)

        self.btn_connect = wx.Button(parent = self, 
                                     id = wx.ID_ANY, label = _("&Connect"))
        self.btn_connect.SetToolTipString(_("Connect to the server"))
        if not self.server.GetValue():
            self.btn_connect.Enable(False)

        self.infoCollapseLabelExp = _('Show advanced connection settings')
        self.infoCollapseLabelCol = _('Hide advanced connection settings')

        self.adv_conn = wx.CollapsiblePane(parent = self,
                                           label = self.infoCollapseLabelExp,
                                           style = wx.CP_DEFAULT_STYLE |
                                                   wx.CP_NO_TLW_RESIZE | wx.EXPAND)

        self.MakeAdvConnPane(pane = self.adv_conn.GetPane())
        self.adv_conn.Collapse(True)
        self.Bind(wx.EVT_COLLAPSIBLEPANE_CHANGED, self.OnAdvConnPaneChanged, self.adv_conn) 

        self.reqDataPanel = wx.Panel(parent = self, id = wx.ID_ANY)

        self.layerNameBox = wx.StaticBox(parent = self.reqDataPanel, 
                                         id = wx.ID_ANY,
                                         label = _(" Layer Manager Settings "))

        self.layerNameText = wx.StaticText(parent = self.reqDataPanel, id = wx.ID_ANY, 
                                           label = _("Output layer name:"))
        self.layerName = wx.TextCtrl(parent = self.reqDataPanel, id = wx.ID_ANY)

        for ws in self.ws_panels.iterkeys():
            # set class WSPanel argument layerNameTxtCtrl
            self.ws_panels[ws]['panel'] =  WSPanel(parent = self.reqDataPanel,
                                                   web_service = ws)
            self.ws_panels[ws]['panel'].capParsed.connect(self.OnPanelCapParsed)
            self.ws_panels[ws]['panel'].layerSelected.connect(self.OnLayerSelected)

        # buttons
        self.btn_close = wx.Button(parent = self, id = wx.ID_CLOSE)
        self.btn_close.SetToolTipString(_("Close dialog"))
        
        # statusbar
        self.statusbar = wx.StatusBar(parent = self, id = wx.ID_ANY)

        # bindings
        self.btn_close.Bind(wx.EVT_BUTTON, self.OnClose)
        self.Bind(wx.EVT_CLOSE, self.OnClose)
        self.btn_connect.Bind(wx.EVT_BUTTON, self.OnConnect)

        self.server.Bind(wx.EVT_TEXT, self.OnServer)
        self.layerName.Bind(wx.EVT_TEXT, self.OnOutputLayerName)

        self.settsManager.settingsChanged.connect(self.OnSettingsChanged)
        self.settsManager.settingsSaving.connect(self.OnSettingsSaving)

    def OnLayerSelected(self, title):

        if not self.layerName.GetValue().strip():
            self.layerName.SetValue(title)

    def _doLayout(self):

        dialogSizer = wx.BoxSizer(wx.VERTICAL)

        dialogSizer.Add(item = self.settsManager, proportion = 0,
                        flag = wx.EXPAND | wx.TOP | wx.LEFT | wx.RIGHT, border = 5)
        
        # connectin settings
        settingsSizer = wx.StaticBoxSizer(self.settingsBox, wx.VERTICAL)
        
        serverSizer = wx.FlexGridSizer(cols = 3, vgap = 5, hgap = 5)

        serverSizer.Add(item = self.serverText,
                      flag = wx.ALIGN_CENTER_VERTICAL)
        serverSizer.AddGrowableCol(1)
        serverSizer.Add(item = self.server,
                      flag = wx.EXPAND | wx.ALL)

        serverSizer.Add(item = self.btn_connect)

        settingsSizer.Add(item = serverSizer, proportion = 0,
                          flag = wx.EXPAND | wx.LEFT | wx.RIGHT, border = 5)
        
        settingsSizer.Add(item = self.adv_conn,
                          flag = wx.ALL | wx.EXPAND, border = 5)

        dialogSizer.Add(item = settingsSizer, proportion = 0,
                        flag = wx.EXPAND | wx.TOP | wx.LEFT | wx.RIGHT, border = 5)

        # layer name, parsed capabilites

        reqDataSizer = wx.BoxSizer(wx.VERTICAL)

        layerNameSizer = wx.StaticBoxSizer(self.layerNameBox, wx.HORIZONTAL)

        layerNameSizer.Add(item = self.layerNameText,
                           flag = wx.ALIGN_CENTER_VERTICAL | wx.ALL, border = 5)

        layerNameSizer.Add(item = self.layerName, 
                           flag = wx.EXPAND, proportion = 1)
 
        reqDataSizer.Add(item = layerNameSizer,
                         flag = wx.TOP | wx.LEFT | wx.RIGHT | wx.EXPAND, border = 5)

        self.ch_ws_sizer = wx.BoxSizer(wx.VERTICAL)

        reqDataSizer.Add(item = self.ch_ws_sizer, proportion = 0,
                         flag = wx.TOP | wx.EXPAND, border = 5)

        for ws in self.ws_panels.iterkeys():
            reqDataSizer.Add(item = self.ws_panels[ws]['panel'], proportion = 1,
                             flag = wx.TOP | wx.LEFT | wx.RIGHT | wx.EXPAND, border = 5)
            self.ws_panels[ws]['panel'].Hide()

        dialogSizer.Add(item = self.reqDataPanel, proportion = 1,
                        flag = wx.EXPAND)

        self.reqDataPanel.SetSizer(reqDataSizer)
        self.reqDataPanel.Hide()

        # buttons
        self.btnsizer = wx.BoxSizer(orient = wx.HORIZONTAL)

        self.btnsizer.Add(item = self.btn_close, proportion = 0,
                          flag = wx.ALL | wx.ALIGN_CENTER,
                          border = 10)
        
        dialogSizer.Add(item = self.btnsizer, proportion = 0,
                        flag = wx.ALIGN_CENTER)

        # expand wxWidget wx.StatusBar
        statusbarSizer = wx.BoxSizer(wx.HORIZONTAL)
        statusbarSizer.Add(item=self.statusbar, proportion=1, flag=wx.EXPAND)
        dialogSizer.Add(item=statusbarSizer,
                        proportion=0,
                        flag=wx.EXPAND)

        self.SetSizer(dialogSizer)
        self.Layout()

        self.SetMinSize((550, -1))
        self.SetMaxSize((-1, self.GetBestSize()[1]))
        self.Fit()

    def MakeAdvConnPane(self, pane):
        """Create advanced connection settings pane
        """
        self.usernameText = wx.StaticText(parent = pane,
                                          id = wx.ID_ANY, label = _("Username:"))
        self.username  = wx.TextCtrl(parent = pane, id = wx.ID_ANY)

        self.passwText = wx.StaticText(parent = pane, 
                                        id = wx.ID_ANY, label = _("Password:"))
        self.password  = wx.TextCtrl(parent = pane, id = wx.ID_ANY,
                                     style = wx.TE_PASSWORD)

        # pane layout
        adv_conn_sizer = wx.BoxSizer(wx.VERTICAL)

        usernameSizer = wx.BoxSizer(wx.HORIZONTAL)

        usernameSizer.Add(item = self.usernameText,
                          flag = wx.ALIGN_CENTER_VERTICAL, border = 5)

        usernameSizer.Add(item = self.username, proportion = 1, 
                          flag = wx.EXPAND, border = 5)

        adv_conn_sizer.Add(item = usernameSizer,
                           flag = wx.ALL | wx.EXPAND, border = 5)

        passwSizer = wx.BoxSizer(wx.HORIZONTAL)

        passwSizer.Add(item = self.passwText,
                       flag = wx.ALIGN_CENTER_VERTICAL, border = 5)

        passwSizer.Add(item = self.password, proportion = 1, 
                       flag = wx.EXPAND, border = 5)

        adv_conn_sizer.Add(item = passwSizer,
                           flag = wx.ALL | wx.EXPAND, border = 5)
        
        pane.SetSizer(adv_conn_sizer)
        adv_conn_sizer.Fit(pane)

        pane.SetSizer(adv_conn_sizer)
        adv_conn_sizer.Fit(pane)

    def OnSettingsSaving(self, name):
        """Check if required data are filled before setting save is performed.
        """
        server = self.server.GetValue().strip()
        if not server:
            GMessage(parent = self,
                     message = _("No data source defined, settings are not saved."))
            return

        self.settsManager.SetDataToSave((server,
                                         self.username.GetValue(),
                                         self.password.GetValue()))
        self.settsManager.SaveSettings(name)

    def OnSettingsChanged(self, data):
        """Update widgets according to chosen settings"""
        # data list: [server, username, password]
        if len < 3:
            return

        self.server.SetValue(data[0])

        self.username.SetValue(data[1])
        self.password.SetValue(data[2])

        if data[1] or data[2]:
            self.adv_conn.Expand()
        else:
            self.adv_conn.Collapse(True)

        # clear content of the wxWidget wx.TextCtrl (Output layer
        # name:), based on changing default server selection in the
        # wxWidget wx.Choice
        if len(self.layerName.GetValue()) > 0:
            self.layerName.Clear()

    def OnClose(self, event):
        """Close the dialog
        """
        """Close dialog"""
        if not self.IsModal():
            self.Destroy()
        event.Skip()

    def _getCapFiles(self):
        ws_cap_files = {}
        for v in self.ws_panels.itervalues():
            ws_cap_files[v['panel'].GetWebService()] = v['panel'].GetCapFile()

        return ws_cap_files

    def OnServer(self, event):
        """Server settings edited
        """
        value = event.GetString()
        if value:
            self.btn_connect.Enable(True)
        else:
            self.btn_connect.Enable(False)
        
        # clear content of the wxWidget wx.TextCtrl (Output Layer
        # name:), based on changing content of the wxWidget
        # wx.TextCtrl (Server:)
        self.layerName.Clear()

    def OnOutputLayerName(self, event):
        """Update layer name to web service panel
        """
        lname = event.GetString()
        lname = lname.encode('ascii', 'replace')

        for v in self.ws_panels.itervalues():
            v['panel'].SetOutputLayerName(lname.strip())

    def OnConnect(self, event):
        """Connect to the server
        """
        server = self.server.GetValue().strip()

        self.ch_ws_sizer.Clear(deleteWindows = True)

        if self.active_ws_panel is not None:
            self.reqDataPanel.Hide()
            for btn in self.run_btns:
                btn.Enable(False)
            self.active_ws_panel = None

            self.Layout()
            self.Fit()

        self.statusbar.SetStatusText(_("Connecting to <%s>..." % self.server.GetValue().strip()))

        # number of panels already connected
        self.finished_panels_num = 0
        for ws in self.ws_panels.iterkeys():
            self.ws_panels[ws]['panel'].ConnectToServer(url = server,
                                                        username = self.username.GetValue(),
                                                        password = self.password.GetValue())
            self.ws_panels[ws]['panel'].Hide()
        
    def OnPanelCapParsed(self, error_msg):
        """Called when panel has downloaded and parsed capabilities file.
        """
        # how many web service panels are finished
        self.finished_panels_num +=  1

        if error_msg:
            self.error_msgs += '\n' + error_msg

        # if all are finished, show panels, which succeeded in connection
        if self.finished_panels_num == len(self.ws_panels):
            self.UpdateDialogAfterConnection()

            # show error dialog only if connections to all web services were unsuccessful
            if not self._getConnectedWS() and self.error_msgs:
                GError(self.error_msgs, parent = self)
            self.error_msgs = ''

            self.Layout()
            self.Fit()

    def _getConnectedWS(self):
        """
        :return: list of found web services on server (identified as keys in self.ws_panels) 
        """
        conn_ws = []
        for ws, data in self.ws_panels.iteritems():
            if data['panel'].IsConnected():
                conn_ws.append(ws)

        return conn_ws

    def UpdateDialogAfterConnection(self):
        """Update dialog after all web service panels downloaded and parsed capabilities data.
        """
        avail_ws = {}        
        conn_ws = self._getConnectedWS()

        for ws in conn_ws:
            avail_ws[ws] = self.ws_panels[ws]

        self.web_service_sel = []
        self.rb_choices = []

        # at least one web service found on server
        if len(avail_ws) > 0:
            self.reqDataPanel.Show()
            self.rb_order = ['WMS_1.1.1', 'WMS_1.3.0', 'WMTS', 'OnEarth']

            for ws in self.rb_order:

                if ws in avail_ws:
                    self.web_service_sel.append(ws)
                    self.rb_choices.append(avail_ws[ws]['label'])

            self.choose_ws_rb = wx.RadioBox(parent = self.reqDataPanel, id = wx.ID_ANY, 
                                            label = _("Available web services"), 
                                            pos = wx.DefaultPosition, choices = self.rb_choices, 
                                            majorDimension = 1, style = wx.RA_SPECIFY_ROWS)
        
            self.Bind(wx.EVT_RADIOBOX, self.OnChooseWs, self.choose_ws_rb)
            self.ch_ws_sizer.Add(item = self.choose_ws_rb,
                                 flag = wx.TOP | wx.LEFT | wx.RIGHT | wx.EXPAND, border = 5)
            self._showWsPanel(self.web_service_sel[self.choose_ws_rb.GetSelection()])
            self.statusbar.SetStatusText(_("Connected to <%s>" % self.server.GetValue().strip()))
            for btn in self.run_btns:
                btn.Enable(True)
        # no web service found on server
        else:
            self.statusbar.SetStatusText(_("Unable to connect to <%s>" % self.server.GetValue().strip()))
            for btn in self.run_btns:
                btn.Enable(False)
            self.reqDataPanel.Hide()
            self.active_ws_panel = None

    def OnChooseWs(self, event):
        """Show panel corresponding to selected web service.
        """
        choosen_r = event.GetInt() 
        self._showWsPanel(self.web_service_sel[choosen_r])

    def _showWsPanel(self, ws):
        """Helper function
        """
        if self.active_ws_panel is not None:
            self.active_ws_panel.Hide()

        self.active_ws_panel = self.ws_panels[ws]['panel']
        self.active_ws_panel.Show()
        self.SetMaxSize((-1, -1))
        self.active_ws_panel.GetContainingSizer().Layout()

    def OnAdvConnPaneChanged(self, event):
        """Collapse search module box
        """
        if self.adv_conn.IsExpanded():
            self.adv_conn.SetLabel(self.infoCollapseLabelCol)
        else:
            self.adv_conn.SetLabel(self.infoCollapseLabelExp)

        self.Layout()
        self.SetMaxSize((-1, self.GetBestSize()[1]))
        self.SendSizeEvent()
        self.Fit()

class AddWSDialog(WSDialogBase):
    """Dialog for adding web service layer."""
    def __init__(self, parent, giface, id = wx.ID_ANY,
                 style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs):

        WSDialogBase.__init__(self, parent, id = wx.ID_ANY,
                              style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs)

        self.SetTitle(_("Add web service layer"))

        self.parent = parent
        self.giface = giface
        self.btn_connect.SetDefault()

    def _createWidgets(self):

        WSDialogBase._createWidgets(self)

        self.btn_add = wx.Button(parent = self, id = wx.ID_ANY, label = _("&Add layer"))
        self.btn_add.SetToolTipString(_("Add selected web service layers as map layer into layer tree"))        
        self.btn_add.Enable(False)
        
        self.run_btns.append(self.btn_add)

    def _doLayout(self):

        WSDialogBase._doLayout(self)

        self.btnsizer.Add(item = self.btn_add, proportion = 0,
                          flag = wx.ALL | wx.ALIGN_CENTER,
                          border = 10)

        # bindings
        self.btn_add.Bind(wx.EVT_BUTTON, self.OnAddLayer)

    def UpdateDialogAfterConnection(self):
        """Connect to the server
        """
        WSDialogBase.UpdateDialogAfterConnection(self)

        if self._getConnectedWS():
            self.btn_add.SetDefault()
        else:
            self.btn_connect.SetDefault()

    def OnAddLayer(self, event):
        """Add web service layer.
        """
        # add layer
        if self.active_ws_panel is None:
            return 

        lcmd = self.active_ws_panel.CreateCmd()
        if not lcmd:
            return None

        # TODO: It is not clear how to do GetOptData in giface
        # knowing what GetOptData is doing might help
        # (maybe Get... is not the right name)
        # please fix giface if you know
        # tree -> giface
        # GetLayerTree -> GetLayerList
        # AddLayer -> AddLayer (but tree ones returns some layer,
        # giface ones nothing)
        # GetLayerInfo -> Layer object can by used instead
        # GetOptData -> unknown
        ltree = self.giface.GetLayerTree()

        active_ws = self.active_ws_panel.GetWebService()
        if 'WMS' not in active_ws:
            cap_file =  self.active_ws_panel.GetCapFile()
            cmd_cap_file = grass.tempfile()
            shutil.copyfile(cap_file, cmd_cap_file)
            lcmd.append('capfile=' + cmd_cap_file)

        layer = ltree.AddLayer(ltype = 'wms',
                               lname = self.active_ws_panel.GetOutputLayerName(), 
                               lchecked = True, lcmd = lcmd)


        ws_cap_files = self._getCapFiles()
        # create properties dialog
        cmd_list = ltree.GetLayerInfo(layer,'cmd')
        cmd = CmdToTuple(cmd_list)

        prop_win = WSPropertiesDialog(parent = self.parent,
                                      giface = self.giface,
                                      id = wx.ID_ANY,
                                      layer = layer,
                                      ws_cap_files = ws_cap_files,
                                      cmd = cmd)

        prop_win.Hide()
        ltree.GetOptData(dcmd = None, layer = layer, 
                         params = None, propwin = prop_win)

class WSPropertiesDialog(WSDialogBase):
    """Dialog for editing web service properties."""
    def __init__(self, parent, giface, layer, ws_cap_files, cmd, id = wx.ID_ANY,
                 style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs):
        """
        :param giface: grass interface
        :param layer: layer tree item
        :param ws_cap_files: dict web service('WMS_1.1.1', 'WMS_1.3.0',
        'WMTS', 'OnEarth') : cap file path cap files, which will be parsed
        :param cmd: cmd to which dialog widgets will be initialized if
        it is possible (cmp parameters exists in parsed web service cap_file)
        """

        WSDialogBase.__init__(self, parent, id = wx.ID_ANY,
                               style = wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER, **kwargs)

        self.SetTitle(_("Web service layer properties"))

        self.layer = layer
        self.giface = giface

        # after web service panels are connected, set dialog widgets
        # according to cmd in this variable (if it is not None) 
        self.cmd_to_set = None

        # store data needed for reverting
        self.revert_ws_cap_files = {}
        self.revert_cmd = cmd

        ws_cap = self._getWSfromCmd(cmd)
        for ws in self.ws_panels.iterkeys():
            # cap file used in cmd will be deleted, thnaks to the dialogs destructor
            if ws == ws_cap and cmd[1].has_key('capfile'):
                self.revert_ws_cap_files[ws] = cmd[1]['capfile']
                del ws_cap_files[ws]
            else:
                self.revert_ws_cap_files[ws] = grass.tempfile()

        self._setRevertCapFiles(ws_cap_files)

        self.LoadCapFiles(ws_cap_files = self.revert_ws_cap_files, cmd = cmd)
        self.btn_ok.SetDefault()

    def __del__(self):
        for f in self.revert_ws_cap_files.itervalues():
            grass.try_remove(f)

    def _setRevertCapFiles(self, ws_cap_files):

        for ws, f in ws_cap_files.iteritems():
            if os.path.isfile(ws_cap_files[ws]):
                shutil.copyfile(f, self.revert_ws_cap_files[ws])
            else:
                # delete file content
                f_o = open(f, 'w')
                f_o.close()

    def _createWidgets(self):

        WSDialogBase._createWidgets(self)

        self.btn_apply = wx.Button(parent = self, id = wx.ID_ANY, label = _("&Apply"))
        self.btn_apply.SetToolTipString(_("Apply changes"))        
        self.btn_apply.Enable(False)
        self.run_btns.append(self.btn_apply)

        self.btn_ok = wx.Button(parent = self, id = wx.ID_ANY, label = _("&OK"))
        self.btn_ok.SetToolTipString(_("Apply changes and close dialog"))
        self.btn_ok.Enable(False)
        self.run_btns.append(self.btn_ok)

    def _doLayout(self):

        WSDialogBase._doLayout(self)

        self.btnsizer.Add(item = self.btn_apply, proportion = 0,
                          flag = wx.ALL | wx.ALIGN_CENTER,
                          border = 10)

        self.btnsizer.Add(item = self.btn_ok, proportion = 0,
                          flag = wx.ALL | wx.ALIGN_CENTER,
                          border = 10)

        # bindings
        self.btn_apply.Bind(wx.EVT_BUTTON, self.OnApply)
        self.btn_ok.Bind(wx.EVT_BUTTON, self.OnSave)

    def LoadCapFiles(self, ws_cap_files, cmd):
        """Parse cap files and update dialog.

        For parameters description, see the constructor.
        """
        self.ch_ws_sizer.Clear(deleteWindows = True)

        self.cmd_to_set = cmd

        self.finished_panels_num = 0

        conn = self._getServerConnFromCmd(cmd)

        self.server.SetValue(conn['url'])
        self.password.SetValue(conn['password'])
        self.username.SetValue(conn['username'])

        self.layerName.SetValue(cmd[1]['map'])

        for ws, data in self.ws_panels.iteritems():
            cap_file = None

            if ws_cap_files.has_key(ws):
                cap_file = ws_cap_files[ws]

            data['panel'].ParseCapFile(url = conn['url'], 
                                       username = conn['password'], 
                                       password = conn['username'], 
                                       cap_file = cap_file)

    def _getServerConnFromCmd(self, cmd):
        """Get url/server/passwod from cmd tuple 
        """
        conn = { 'url' : '', 'username' : '', 'password' : ''}
        
        for k in conn.iterkeys():
            if cmd[1].has_key(k):
                conn[k] = cmd[1][k]
        return conn

    def _apply(self):
        """Apply chosen values from widgets to web service layer."""
        lcmd = self.active_ws_panel.CreateCmd()
        if not lcmd:
            return

        active_ws = self.active_ws_panel.GetWebService()
        if 'WMS' not in active_ws:
            lcmd.append('capfile=' + self.revert_ws_cap_files[active_ws])

        self.giface.GetLayerTree().GetOptData(dcmd = lcmd, 
                                              layer = self.layer, 
                                              params = None,
                                              propwin = self)

        #TODO use just list or tuple
        cmd = CmdToTuple(lcmd)
        self.revert_cmd = cmd
        self._setRevertCapFiles(self._getCapFiles())

        self.giface.updateMap.emit()

    def UpdateDialogAfterConnection(self):
        """Connect to the server
        """
        WSDialogBase.UpdateDialogAfterConnection(self)
        if self._getConnectedWS():
            self.btn_ok.SetDefault()
        else:
            self.btn_connect.SetDefault()

    def OnApply(self, event):   
        self._apply()

    def OnSave(self, event):
        self._apply()
        self._close()

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

    def _close(self):
        """Hide dialog"""
        self.Hide()
        self.LoadCapFiles(cmd = self.revert_cmd,
                          ws_cap_files = self.revert_ws_cap_files)

    def OnPanelCapParsed(self, error_msg):
        """Called when panel has downloaded and parsed capabilities file.
        """
        WSDialogBase.OnPanelCapParsed(self, error_msg)

        if self.finished_panels_num == len(self.ws_panels):
            if self.cmd_to_set:
                self._updateWsPanelWidgetsByCmd(self.cmd_to_set)
                self.cmd_to_set = None

    def _updateWsPanelWidgetsByCmd(self, cmd):
        """Set values of  widgets according to parameters in cmd.
        """

        ws = self._getWSfromCmd(cmd)
        if self.ws_panels[ws]['panel'].IsConnected():
            self.ws_panels[ws]['panel'].UpdateWidgetsByCmd(cmd)
            self.choose_ws_rb.SetStringSelection(self.ws_panels[ws]['label'])
            self._showWsPanel(ws)

    def _getWSfromCmd(self, cmd):
        driver = cmd[1]['driver']
        ws = driver.split('_')[0]
        
        if ws == 'WMS':
            ws += '_' + cmd[1]['wms_version']
        return ws

class SaveWMSLayerDialog(wx.Dialog):
    """Dialog for saving web service layer into GRASS vector/raster layer.

    .. todo::
        Implement saving data in region of map display.
    """
    def __init__(self, parent, layer, giface):
        
        wx.Dialog.__init__(self, parent = parent, title = ("Save web service layer as raster map"), id = wx.ID_ANY)

        self.layer = layer
        self._giface = giface

        self.cmd = self.layer.GetCmd()

        self.thread = CmdThread(self)
        self.cmdStdErr = GStderr(self)

        self._createWidgets()

    def _createWidgets(self):

        self.labels = {}
        self.params = {}

        self.labels['output'] = wx.StaticText(parent = self, id = wx.ID_ANY, label = _("Name for output raster map:"))

        self.params['output'] = Select(parent = self, type = 'raster', mapsets = [grass.gisenv()['MAPSET']],
                                       size = globalvar.DIALOG_GSELECT_SIZE)

        self.regionStBoxLabel = wx.StaticBox(parent = self, id = wx.ID_ANY,
                                             label = " %s " % _("Export region"))

        self.region_types_order = ['display', 'comp', 'named']
        self.region_types =  {}
        self.region_types['display'] = wx.RadioButton(parent=self, label=_("Map display"),
                                                      style=wx.RB_GROUP)
        self.region_types['comp'] = wx.RadioButton(parent=self, label=_("Computational region"))
        self.region_types['named'] = wx.RadioButton(parent=self, label=_("Named region"))
        self.region_types['display'].SetToolTipString(_("Extent and resolution"
                                                        " are based on Map Display geometry."))
        self.region_types['comp'].SetToolTipString(_("Extent and resolution"
                                                     " are based on computational region."))
        self.region_types['named'].SetToolTipString(_("Extent and resolution"
                                                      " are based on named region."))
        self.region_types['display'].SetValue(True)  # set default as map display

        self.overwrite  = wx.CheckBox(parent = self, id = wx.ID_ANY,
                                      label = _("Overwrite existing raster map"))

        self.named_reg_panel = wx.Panel(parent = self, id = wx.ID_ANY)
        self.labels['region'] = wx.StaticText(parent = self.named_reg_panel, id = wx.ID_ANY, 
                                             label = _("Choose named region:"))

        self.params['region'] = Select(parent = self.named_reg_panel, type = 'region',
                                       size = globalvar.DIALOG_GSELECT_SIZE)

        # buttons
        self.btn_close = wx.Button(parent = self, id = wx.ID_CLOSE)
        self.btn_close.SetToolTipString(_("Close dialog"))

        self.btn_ok = wx.Button(parent=self, id=wx.ID_OK, label=_("&Save layer"))
        self.btn_ok.SetToolTipString(_("Save web service layer as raster map"))

        # statusbar
        self.statusbar = wx.StatusBar(parent = self, id = wx.ID_ANY)

        self._layout()

    def _layout(self):

        self._border = wx.BoxSizer(wx.VERTICAL)
        dialogSizer = wx.BoxSizer(wx.VERTICAL)

        regionSizer = wx.BoxSizer(wx.HORIZONTAL)

        dialogSizer.Add(item = self._addSelectSizer(title = self.labels['output'], 
                                                    sel = self.params['output']))

        regionSizer = wx.StaticBoxSizer(self.regionStBoxLabel, wx.VERTICAL)

        regionTypeSizer = wx.BoxSizer(wx.HORIZONTAL)
        for r_type in self.region_types_order:
            regionTypeSizer.Add(item=self.region_types[r_type], flag=wx.RIGHT, border=8)

        regionSizer.Add(item = regionTypeSizer)

        self.named_reg_panel.SetSizer(self._addSelectSizer(title = self.labels['region'],
                                                            sel = self.params['region']))
        regionSizer.Add(item = self.named_reg_panel)
        self.named_reg_panel.Hide()

        dialogSizer.Add(item = regionSizer, flag = wx.EXPAND)

        dialogSizer.Add(item=self.overwrite, flag=wx.TOP, border=10)

        # buttons
        self.btnsizer = wx.BoxSizer(orient = wx.HORIZONTAL)

        self.btnsizer.Add(item = self.btn_close, proportion = 0,
                          flag = wx.ALL | wx.ALIGN_CENTER,
                          border = 10)
        
        self.btnsizer.Add(item = self.btn_ok, proportion = 0,
                          flag = wx.ALL | wx.ALIGN_CENTER,
                          border = 10)

        dialogSizer.Add(item = self.btnsizer, proportion = 0,
                        flag = wx.ALIGN_CENTER)

        self._border.Add(item=dialogSizer, proportion=0,
                         flag=wx.ALL, border=5)

        self._border.Add(item=self.statusbar, proportion=0)

        self.SetSizer(self._border)
        self.Layout()
        self.Fit()

        # bindings
        self.btn_close.Bind(wx.EVT_BUTTON, self.OnClose)
        self.btn_ok.Bind(wx.EVT_BUTTON, self.OnSave)

        self.Bind(EVT_CMD_DONE,   self.OnCmdDone)
        self.Bind(EVT_CMD_OUTPUT, self.OnCmdOutput)

        for r_type in self.region_types_order:
            self.Bind(wx.EVT_RADIOBUTTON, self.OnRegionType, self.region_types[r_type])

    def _addSelectSizer(self, title, sel): 
        """Helper layout function.
        """
        selSizer = wx.BoxSizer(orient = wx.VERTICAL)

        selTitleSizer = wx.BoxSizer(wx.HORIZONTAL)
        selTitleSizer.Add(item = title, proportion = 1,
                          flag = wx.LEFT | wx.TOP | wx.EXPAND, border = 5)

        selSizer.Add(item = selTitleSizer, proportion = 0,
                     flag = wx.EXPAND)

        selSizer.Add(item = sel, proportion = 1,
                     flag = wx.EXPAND | wx.ALL| wx.ALIGN_CENTER_VERTICAL,
                     border = 5)

        return selSizer

    def OnClose(self, event):
        """Close dialog
        """
        if not self.IsModal():
            self.Destroy()
        event.Skip()

    def OnRegionType(self, event):
        selected = event.GetEventObject()
        if selected == self.region_types['named']:
            self.named_reg_panel.Show()
        else:
            self.named_reg_panel.Hide()

        self._border.Layout()
        self.Fit()

    def OnSave(self, event):
        """Import WMS raster data into GRASS as raster layer.
        """
        self.thread.abort(abortall = True)
        currmapset = grass.gisenv()['MAPSET']
        
        self.output = self.params['output'].GetValue().strip()
        l_spl = self.output.strip().split("@")

        # check output layer
        msg = None
        if not self.output:
            msg = _('Missing output raster.')

        elif len(l_spl) > 1 and \
             l_spl[1] != currmapset:
                msg = _('Output map can be added only to current mapset.')

        elif not self.overwrite.IsChecked() and\
            grass.find_file(self.output, 'cell', '.')['fullname']:
            msg = _('Output map <%s> already exists' % self.output)

        if msg:
            GMessage(parent = self,
                     message = msg)
            return

        self.output = l_spl[0]


        # check region
        region = self.params['region'].GetValue().strip()
        reg_spl = region.strip().split("@")

        reg_mapset = '.'
        if len(reg_spl) > 1:
            reg_mapset = reg_spl[1]

        if self.region_types['named'].GetValue():
            if not grass.find_file(reg_spl[0], 'windows', reg_mapset)['fullname']:
                msg = _('Region <%s> does not exist.' % self.params['region'].GetValue())
                GWarning(parent=self,
                         message=msg)
                return

        # create r.in.wms command
        cmd = ('r.in.wms', deepcopy(self.cmd[1]))

        if cmd[1].has_key('map'):
            del cmd[1]['map']

        cmd[1]['output'] = self.output

        if self.overwrite.IsChecked():
            cmd[1]['overwrite'] = True

        env = os.environ.copy()
        if self.region_types['named'].GetValue():
            cmd[1]['region'] = region
        elif self.region_types['display'].GetValue():
            region = self._giface.GetMapWindow().GetMap().SetRegion()
            env['GRASS_REGION'] = region

        cmdList = CmdTupleToList(cmd)
        self.currentPid = self.thread.GetId()

        self.thread.RunCmd(cmdList, env=env, stderr=self.cmdStdErr)

        self.statusbar.SetStatusText(_("Downloading data..."))

    def OnCmdDone(self, event):
        """When data are fetched.
        """
        if event.pid != self.currentPid:
            return

        self._addLayer()
        self.statusbar.SetStatusText("")

    def _addLayer(self):
        """Add layer into layer tree.
        """
        llist = self._giface.GetLayerList()
        if len(llist.GetLayersByName(self.output)) == 0:
            cmd = ['d.rast', 'map=' + self.output]
            llist.AddLayer(ltype='raster',
                           name=self.output,
                           cmd=cmd,
                           checked=True)

    def OnCmdOutput(self, event):
        """Handle cmd output according to debug level.
        """
        if Debug.GetLevel() == 0:
            if event.type == 'error':
                msg = _('Unable to fetch data.\n')
                msg += event.text
                GWarning(parent = self,
                         message = msg)
        else:
            Debug.msg(1, event.text)
