# Copyright (C) 2003, 2004 by Intevation GmbH
# Authors:
# Jan-Oliver Wagner <jan@intevation.de>
#
# This program is free software under the GPL (>=v2)
# Read the file COPYING coming with Thuban for details.

"""
Provide layers via OGC WMS.

This extension is in a very experimental stage!
It just demonstrates how to add a special
layer into Thuban via an extension.
Some things are not wired, so be prepared for Exceptions
everywhere.

You will need PyOGCLib 0.1.0, see
  http://pyogclib.sourceforge.net/
Set the PYTHONPATH to the PyOGCLib directory before
starting Thuban.
"""

__version__ = "$Revision: 1.2 $"

import os, sys
import xml.dom.minidom
import tempfile

from wxPython.wx import *

from ogclib.WMSClient import WMSClient

from Thuban.Model.layer import BaseLayer
from Thuban.Model.proj import Projection
from Thuban.Model.extension import Extension
from Thuban.Model.resource import get_system_proj_file, EPSG_PROJ_FILE, \
     EPSG_DEPRECATED_PROJ_FILE
from Thuban.UI.command import registry, Command
import Thuban.UI.mainwindow
from Thuban import _
import Thuban.UI.baserenderer


def epsg_code_to_projection(epsg):
    """Find the projection for the given epsg code.

    epsg -- EPSG code as string
    """
    proj_file, warnings = get_system_proj_file(EPSG_PROJ_FILE)

    for proj in proj_file.GetProjections():
        if proj.EPSGCode() == epsg:
            return proj
    proj_file, warnings = get_system_proj_file(EPSG_DEPRECATED_PROJ_FILE)
    for proj in proj_file.GetProjections():
        if proj.EPSGCode() == epsg:
            return proj
    return None

class WMSExtension(Extension):
    def TreeInfo(self):
        return (_("Extension: %s") % self.title,
                [ object.TreeInfo() for object in self.objects ])

class WMSLayer(BaseLayer, WMSClient):

    def __init__(self, title, url):
        """Initializes the WMSLayer.

        title -- Title of this layer.
        url -- URL of the WMS-Server wich must contain '?'

        If an error occured, self.error_msg is a string describing
        the problem(s). Else, self.error_msg is None.
        """
        BaseLayer.__init__(self, title, visible = True, projection = None)
        self.url = url
        self.bbox = None
        self.latlonbbox = None
        self.error_msg = None
        self.layer_name = None

        wms_response = self.getCapabilities(self.url, '1.0')
        self._capa_dom = xml.dom.minidom.parseString(wms_response)

        root = self._capa_dom.documentElement
        cap = root.getElementsByTagName('Capability')[0]
        layer = cap.getElementsByTagName('Layer')[0]

        # get projected bounding box and latlon bounding box
        for node in layer.childNodes:
            if node.nodeName == 'BoundingBox':
                minx = node.attributes.get('minx').nodeValue
                miny = node.attributes.get('miny').nodeValue
                maxx = node.attributes.get('maxx').nodeValue
                maxy = node.attributes.get('maxy').nodeValue
                self.bbox = (float(minx), float(miny), float(maxx), float(maxy))
        bbox = layer.getElementsByTagName('LatLonBoundingBox')[0]
        self.layer_name = layer.getElementsByTagName('Name')[0].childNodes[0].data
        minx = bbox.attributes.get('minx').nodeValue
        miny = bbox.attributes.get('miny').nodeValue
        maxx = bbox.attributes.get('maxx').nodeValue
        maxy = bbox.attributes.get('maxy').nodeValue
        self.latlonbbox = (float(minx), float(miny), float(maxx), float(maxy))

        # get projection
        srs = layer.getElementsByTagName('SRS')[0].childNodes[0].data
        if len(srs.split(':')) == 1:
            epsg_id = srs
        else:
            epsg_id = srs.split(':')[1]

        p = epsg_code_to_projection(epsg_id)
        self.SetProjection(p)

        if p is None:
            self.error_msg = _('EPSG projection code %s not found!\n'\
                               'Setting projection to "None".\n'\
                               'Please set an appropriate projection yourself.'\
                               % epsg_id)

        # get title
        title = layer.getElementsByTagName('Title')[0].childNodes[0].data
        self.SetTitle(title.encode('latin1', 'replace'))

        self._capa_dom.unlink()

    def LatLongBoundingBox(self):
        """Return the layer's bounding box in lat-lon.
        """
        return self.latlonbbox

    def BoundingBox(self):
        """Return the layer's bounding box in the intrinsic coordinate system.
        """
        return self.bbox

    def GetMapImg(self, width, height, bbox):
        bbox_dict = { 'minx': bbox[0], 'miny': bbox[1],
                      'maxx': bbox[2], 'maxy': bbox[3] }
        epsg_id = int(self.GetProjection().EPSGCode())
        wms_response = self.getMap(self.url, 'JPEG', width, height,
                                   epsg_id, bbox_dict,
                                   [self.layer_name], version = '1.0')
        return wms_response



def render_wms_layer(renderer, layer):
    offx, offy = renderer.offset
    width, height = renderer.dc.GetSizeTuple()

    scale = renderer.scale
    xmin = (0 - offx) / scale
    ymin = (offy - height) / scale
    xmax = (width - offx) / scale
    ymax = (offy - 0) / scale

    img = layer.GetMapImg(width, height, (xmin, ymin, xmax, ymax))
    renderer.draw_raster_data(img, "JPEG")

    return ()

Thuban.UI.baserenderer.add_renderer_extension(WMSLayer, render_wms_layer)


class SelectWMSServer(wxDialog):

    ID_COMBOVALUE = 4003

    def __init__(self, parent):
        wxDialog.__init__(self, parent, -1, _("Select WMS Server"),
            style = wxDEFAULT_DIALOG_STYLE
                  | wxSYSTEM_MENU
                  | wxRESIZE_BORDER)

        self.combo_value = wxComboBox(self, self.ID_COMBOVALUE, size=(500,-1))
        self.combo_value.Append("")
        self.combo_value.Append('http://frida.intevation.org/cgi-bin/frida_wms?')
        #self.combo_value.Append('http://wms.jpl.nasa.gov/wms.cgi?')
        #self.combo_value.Append('http://eukrante.hq:9089/cgi-bin/wms_shg?')
        #self.combo_value.Append('http://131.220.106.112:8080/deegree0.7/wms?')
        #self.combo_value.Append('http://demo.cubewerx.com/demo/cubeserv/cubeserv.cgi?CONFIG=gita&')
        self.combo_value.SetSelection(0)

        button_ok = wxButton(self, wxID_OK, _("OK"))
        button_ok.SetDefault()
        button_close = wxButton(self, wxID_CANCEL, _("Close"))

        vbox = wxBoxSizer(wxVERTICAL)
        vbox.Add(self.combo_value, 1, wxEXPAND|wxALL|wxCB_SORT, 10)
        hbox = wxBoxSizer(wxHORIZONTAL)
        hbox.Add(button_ok, 0, wxALL, 10)
        hbox.Add(button_close, 0, wxALL, 10)
        vbox.Add(hbox, 0, 10)

        self.SetAutoLayout(True)
        self.SetSizer(vbox)
        vbox.Fit(self)
        vbox.SetSizeHints(self)
        self.Layout()

        EVT_BUTTON(self, wxID_OK, self.OnOK)
        EVT_BUTTON(self, wxID_CANCEL, self.OnCancel)

    def OnOK(self, event):
        self.url = self.combo_value.GetValue()
        self.EndModal(wxID_OK)

    def OnCancel(self, event):
        self.EndModal(wxID_CANCEL)

def wms_dialog(context):
    """Request URL from user and add WMS Layer.

    context -- The Thuban context.
    """
    dialog = SelectWMSServer(context.mainwindow)

    if dialog.ShowModal() == wxID_OK:
        url = dialog.url
    else:
        url = None
    dialog.Destroy()

    if url is None:
        return

    wms_layer = WMSLayer('A WMS Layer', url)
    if wms_layer.error_msg is not None:
        context.mainwindow.RunMessageBox(_('WMS'), wms_layer.error_msg)

    map = context.mainwindow.canvas.Map()
    if map.projection is None:
        map.SetProjection(wms_layer.projection)
    has_layers = map.HasLayers()
    map.AddLayer(wms_layer)
    if not has_layers:
        # if we're adding a layer to an empty map, fit the
        # new map to the window
        context.mainwindow.canvas.FitMapToWindow()

wxInitAllImageHandlers()
wms_extension = WMSExtension('WMS')

# register the new command
registry.Add(Command('wms', _('Add WMS layer ...'), wms_dialog,
                         helptext = _('Add a WMS Layer')))

# find the experimental menu (create it anew if not found)
main_menu = Thuban.UI.mainwindow.main_menu
experimental_menu = main_menu.find_menu('experimental')
if experimental_menu is None:
    experimental_menu = main_menu.InsertMenu('experimental', _('Experimenta&l'))

# finally add the new entry to the experimental menu
experimental_menu.InsertItem('wms')
