# Copyright (c) 2008 by Intevation GmbH
# Authors:
# Anthony Lenton <anthony@except.com.ar>
#
# This program is free software under the GPL (>=v2)
# Read the file COPYING coming with Thuban for details.

""" A Polish Map File parser.  This module contains the UI and shapefile
    saving code . """

import os
import wx

if __name__ != '__main__':
    from Thuban.UI.command import registry, Command
    from Thuban import _
    from Thuban.UI.mainwindow import main_menu
    from Thuban.UI import internal_from_wxstring

from Thuban.Model.layer import Layer
from Thuban.Model.classification import Classification, ClassGroupProperties
from Thuban.Model.classification import ClassGroupSingleton
from Thuban.Model.color import Color
import shapelib
import dbflib
from pfm import MPParser

def pfm2shp (src_fname, dest_fname):
    """Convert a file from Polish Map File into Shapefiles. """
    fp = open(src_fname)
    parser = MPParser()
    parser.parse (fp)
    
    poi_shp_filename = dest_fname + '_poi.shp'
    poi_dbf_filename = dest_fname + '_poi.dbf'
    poi_shp = shapelib.create(poi_shp_filename, shapelib.SHPT_POINT)
    poi_dbf = dbflib.create(poi_dbf_filename)
    poi_dbf.add_field('Label', dbflib.FTString, 80, 0)
    del poi_dbf
    poi_dbf = dbflib.open(poi_dbf_filename, 'r+b')
    i = 0
    for poi in parser.pois:
        obj = shapelib.SHPObject(shapelib.SHPT_POINT, i,
                                 [[(poi['long'], poi['lat'])]])
        poi_shp.write_object(-1, obj)
        poi_dbf.write_record(i, { 'Label': poi.get('Label', '') })
        i += 1
    del poi_shp
    del poi_dbf

    polygon_shp_filename = dest_fname + '_polygon.shp'
    polygon_dbf_filename = dest_fname + '_polygon.dbf'
    polygon_shp = shapelib.create(polygon_shp_filename, shapelib.SHPT_POLYGON)
    polygon_dbf = dbflib.create(polygon_dbf_filename)
    polygon_dbf.add_field('Label', dbflib.FTString, 80, 0)
    polygon_dbf.add_field('Type', dbflib.FTString, 80, 0)
    del polygon_dbf
    polygon_dbf = dbflib.open(polygon_dbf_filename, 'r+b')
    i = 0
    for poly in parser.polygons:
        if poly['Type'].lower() == '0x4b': # Skip background
            continue
        obj = shapelib.SHPObject(shapelib.SHPT_POLYGON, i,
                                 [zip (poly['long'], poly['lat'])])
        polygon_shp.write_object(-1, obj)
        polygon_dbf.write_record(i, {'Label': poly.get('Label', ''),
                                     'Type': poly.get('parsedType', '')})
        i += 1
    del polygon_shp
    del polygon_dbf

    bg_shp_filename = dest_fname + '_bg.shp'
    bg_dbf_filename = dest_fname + '_bg.dbf'
    bg_shp = shapelib.create(bg_shp_filename, shapelib.SHPT_POLYGON)
    bg_dbf = dbflib.create(bg_dbf_filename)
    bg_dbf.add_field('Label', dbflib.FTString, 80, 0)
    bg_dbf.add_field('Type', dbflib.FTString, 8, 0)
    del bg_dbf
    bg_dbf = dbflib.open(bg_dbf_filename, 'r+b')
    i = 0
    for poly in parser.polygons:
        if poly['Type'].lower() == '0x4b': # Process background
            obj = shapelib.SHPObject(shapelib.SHPT_POLYGON, i,
                                     [zip (poly['long'], poly['lat'])])
            bg_shp.write_object(-1, obj)
            bg_dbf.write_record(i, {'Label': poly.get('Label', ''),
                                    'Type': poly.get('Type', '')})
            i += 1
    del bg_shp
    del bg_dbf

    road_shp_filename = dest_fname + '_road.shp'
    road_dbf_filename = dest_fname + '_road.dbf'
    road_shp = shapelib.create(road_shp_filename, shapelib.SHPT_ARC)
    road_dbf = dbflib.create(road_dbf_filename)
    road_dbf.add_field('Label', dbflib.FTString, 80, 0)
    road_dbf.add_field('Type', dbflib.FTString, 80, 0)
    del road_dbf
    road_dbf = dbflib.open(road_dbf_filename, 'r+b')
    i = 0
    for poly in parser.polylines:
        obj = shapelib.SHPObject(shapelib.SHPT_ARC,
                                 i, [zip (poly['long'], poly['lat'])])
        road_shp.write_object(-1, obj)
        road_dbf.write_record(i, {'Label': poly.get('Label', ''),
                                  'Type': poly.get('parsedType', '')})
        i += 1
    del road_shp
    del road_dbf
    return parser

def getPolygonClassification(polygons):
    """ Returns a Classification with the needed ClassGroups to describe the
        provided polygons """
    clazz = Classification()
    waterProps = ClassGroupProperties()
    waterProps.SetFill (Color (0.7, 0.7, 1))
    vegetProps = ClassGroupProperties()
    vegetProps.SetFill (Color (0.7, 1, 0.7))
    buildProps = ClassGroupProperties()
    buildProps.SetFill (Color (0.6, 0.6, 0.6))
    iceProps = ClassGroupProperties()
    iceProps.SetFill (Color (1, 1, 1))
    earthProps = ClassGroupProperties()
    earthProps.SetFill (Color (0.9, 0.6, 0.3))
    
    typeProps = {'Parking Garage': buildProps, 'City': buildProps,
                 'Car Park (Parking Lot)': buildProps, 'Glacier': iceProps,
                 'University': buildProps, 'Hospital': buildProps,
                 'Cemetery': buildProps, 'City Park': vegetProps,
                 'National park': vegetProps, 'Reservation': vegetProps,
                 'Depth area - blue 4': waterProps,
                 'Depth area - blue 5': waterProps,
                 'Depth area - blue 2': waterProps,
                 'Depth area - blue 3': waterProps,
                 'Depth area - blue 1': waterProps, 'Tundra': iceProps,
                 'Man made area': buildProps, 'Industrial': buildProps,
                 'Airport Runway': buildProps, 'Land - urban': buildProps,
                 'Military': buildProps, 'Intermittent River/Lake': waterProps,
                 'Lake': waterProps, 'Depth area - white 1': waterProps,
                 'Airport': buildProps, 'State Park': vegetProps,
                 'Land - non-urban': earthProps, 'Ocean': waterProps,
                 'Scrub': vegetProps, 'River': waterProps, 'Bridge': buildProps,
                 'Golf': vegetProps, 'Depth area - white': waterProps,
                 'Marina': waterProps, 'Blue-Unknown': waterProps,
                 'Orchard or plantation': vegetProps, 'Flats': earthProps,
                 'Woods': vegetProps, 'Wetland': earthProps, 'Sea': waterProps,
                 'Shopping Centre': buildProps, 'Land - white': iceProps,
                 'Sport': buildProps}

    types = list(set([p['parsedType'] for p in polygons]))
    for t in types:
        if t in typeProps.keys():
            group = ClassGroupSingleton (value=t, props=typeProps[t], label=t)
            clazz.AppendGroup (group)
    return clazz

def getPolylineClassification(polylines):
    """ Returns a Classification with the needed ClassGroups to describe the
        provided polylines """
    clazz = Classification()
    waterProps = ClassGroupProperties()
    waterProps.SetLineColor (Color (0.5, 0.5, 0.8))
    minorProps = ClassGroupProperties()
    minorProps.SetLineColor (Color (0.1, 0.9, 0.1))
    majorProps = ClassGroupProperties()
    majorProps.SetLineColor (Color (1, 1, 0.1))
    roadProps = ClassGroupProperties()
    roadProps.SetLineColor (Color (0.4, 0.4, 0.4))
    railProps = ClassGroupProperties()
    railProps.SetLineColor (Color (0.2, 0.2, 0.2))
    earthProps = ClassGroupProperties()
    earthProps.SetLineColor (Color (0.9, 0.6, 0.3))
    boundProps = ClassGroupProperties()
    boundProps.SetLineColor (Color (0.1, 0.1, 0.1))
    dangerProps = ClassGroupProperties()
    dangerProps.SetLineColor (Color (1, 0.1, 0.1))

    typeProps = {'River channel': waterProps, 'Alley-thick': roadProps,
                 'Principal Highway-thick': minorProps,
                 'Intermittent River': waterProps, 'Airport Runway': roadProps,
                 'Danger line': dangerProps, 'Ramp': roadProps,
                 'Arterial Road-medium': minorProps,
                 'Major Highway-thick': majorProps,
                 'Principal Highway-medium': majorProps, 'River': waterProps,
                 'International Boundary': boundProps,
                 'County Boundary': boundProps, 'Political Boundary':boundProps,
                 'Ferry': waterProps, 'Bridge': roadProps, 'Road': roadProps,
                 'Stream-thin': waterProps, 'Track/Trail': earthProps,
                 'Railroad': railProps, 'Unpaved Road-thin': earthProps,
                 'Arterial Road-thick': minorProps, 'Road-thin': roadProps,
                 'Major Highway Connector-thick': majorProps,
                 'Roundabout': minorProps}

    types = list(set([p['parsedType'] for p in polylines]))
    for t in types:
        if t in typeProps.keys():
            group = ClassGroupSingleton (value=t, props=typeProps[t], label=t)
            clazz.AppendGroup (group)
    return clazz

def getBackgroundClassification():
    """ Returns a Classification with a ClassGroup for the
        background polygon """
    clazz = Classification()
    bgProps = ClassGroupProperties()
    bgProps.SetFill (Color (1, 1, 0.9))
    grp = ClassGroupSingleton (value="0x4b", props=bgProps, label="Background")
    clazz.AppendGroup (grp)
    return clazz



def import_mp_dialog(context):
    """Request filename from user and run importing of mp file.

    context -- The Thuban context.
    """
    dlg = wx.FileDialog(context.mainwindow,
                       _("Select MP file"), ".", "",
                       _("Polish Map Files (*.mp)|*.mp|") +
                       _("Text Files (*.txt)|*.txt|") +
                       _("All Files (*.*)|*.*"),
                       wx.OPEN|wx.OVERWRITE_PROMPT)
    if dlg.ShowModal() == wx.ID_OK:
        pfm_filename = internal_from_wxstring(dlg.GetPath())
        dlg.Destroy()
    else:
        return

    if pfm_filename.find ('.') == -1:
        basename = filename
    else:
        basename = pfm_filename[:pfm_filename.rfind ('.')]

    parser = pfm2shp (pfm_filename, basename)
    # Now load the newly created shapefile
    poi_filename = basename + '_poi.shp'
    bg_filename = basename + '_bg.shp'
    road_filename = basename + '_road.shp'
    polygon_filename = basename + '_polygon.shp'
    title = os.path.splitext(os.path.basename(pfm_filename))[0]
    map = context.mainwindow.canvas.Map()
    has_layers = map.HasLayers()

    try:
        store = context.session.OpenShapefile(bg_filename)
    except IOError:
        # the layer couldn't be opened
        context.mainwindow.RunMessageBox(_('Add PFM Layer'),
                           _("Can't open the file '%s'.") % bg_filename)
    else:
        layer = Layer(title + ' - Background', store)
        layer.SetClassificationColumn ('Type')
        layer.SetClassification (getBackgroundClassification())
        map.AddLayer(layer)
    try:
        store = context.session.OpenShapefile(poi_filename)
    except IOError:
        # the layer couldn't be opened
        context.mainwindow.RunMessageBox(_('Add PFM Layer'),
                           _("Can't open the file '%s'.") % poi_filename)
    else:
        layer = Layer(title + ' - Points', store)
        map.AddLayer(layer)

    try:
        store = context.session.OpenShapefile(polygon_filename)
    except IOError:
        # the layer couldn't be opened
        context.mainwindow.RunMessageBox(_('Add PFM Layer'),
                           _("Can't open the file '%s'.") % polygon_filename)
    else:
        layer = Layer(title + ' - Polygons', store)
        layer.SetClassificationColumn ('Type')
        layer.SetClassification (getPolygonClassification(parser.polygons))
        map.AddLayer(layer)

    try:
        store = context.session.OpenShapefile(road_filename)
    except IOError:
        # the layer couldn't be opened
        context.mainwindow.RunMessageBox(_('Add PFM Layer'),
                           _("Can't open the file '%s'.") % road_filename)
    else:
        layer = Layer(title + ' - Lines', store)
        layer.SetClassificationColumn ('Type')
        layer.SetClassification (getPolylineClassification(parser.polylines))
        map.AddLayer(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()

# register the new command
registry.Add(Command('import-mp', _("(experimental) ") + _('Import PFM-file'),
                     import_mp_dialog, helptext=_('Import a Polish Map File')))

# find the extension menu (create it anew if not found)
extensions_menu = main_menu.FindOrInsertMenu('extensions',
                                               _('E&xtensions'))

# finally add the new entry to the menu
extensions_menu.InsertItem('import-mp')
