"""
/***************************************************************************
        MapServerExport  - A QGIS plugin to export a saved project file
                            to a MapServer map file
                             -------------------
    begin                : 2008-01-07
    copyright            : (C) 2008 by Gary E.Sherman
    email                : sherman at mrcc.com
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/
/* Adapted by Erik van de Pol */
"""
# Import the PyQt and QGIS libraries
from PyQt4.QtCore import * 
from PyQt4.QtGui import *
from xml.dom import minidom
from qgis.core import *
# Initialize Qt resources from file resources.py
import resources_rc
# Import the code for the dialog
from mapserverexportdialog import MapServerExportDialog
# Import the ms_export script that does the real work
from ms_export import *

class MapServerExport: 

  def __init__(self, iface):
    # Save reference to the QGIS interface
    self.iface = iface

  def setCurrentTheme(self, theThemeName):
    # Set icons to the current theme
    self.action.setIcon(self.getThemeIcon("mapserver_export.png"))

  def getThemeIcon(self, theName):
    # get the icon from the best available theme
    myCurThemePath = QgsApplication.activeThemePath() + "/plugins/" + theName;
    myDefThemePath = QgsApplication.defaultThemePath() + "/plugins/" + theName;
    myQrcPath = ":/plugins/mapserver_export/" + theName;
    if QFile.exists(myCurThemePath):
      return QIcon(myCurThemePath)
    elif QFile.exists(myDefThemePath):
      return QIcon(myDefThemePath)
    elif QFile.exists(myQrcPath):
      return QIcon(myQrcPath)
    else:
      return QIcon()

  def initGui(self):  
    # Create action that will start plugin configuration
    self.action = QAction(self.getThemeIcon("mapserver_export.png"), \
        "MapServer Export", self.iface.mainWindow())
    # connect the action to the run method
    QObject.connect(self.action, SIGNAL("triggered()"), self.run) 
    QObject.connect(self.iface, SIGNAL("currentThemeChanged ( QString )"), self.setCurrentTheme)
    # Add toolbar button and menu item
    self.iface.addWebToolBarIcon(self.action)
    self.iface.addPluginToWebMenu("&MapServer Export...", self.action)

  def unload(self):
    # Remove the plugin menu item and icon
    self.iface.removePluginWebMenu("&MapServer Export...",self.action)
    self.iface.removeWebToolBarIcon(self.action)

  # run method that performs all the real work
  def run(self): 
    # create and show the MapServerExport dialog 
    self.dlg = MapServerExportDialog(self.iface.mainWindow())
    # attach events to inputs and buttons
    QObject.connect(self.dlg.ui.btnChooseFile, SIGNAL("clicked()"), self.setMapFile)
    QObject.connect(self.dlg.ui.txtMapFilePath, SIGNAL("textChanged(QString)"), self.mapfileChanged)
    QObject.connect(self.dlg.ui.btnChooseProjectFile, SIGNAL("clicked()"), self.setProjectFile)
    QObject.connect(self.dlg.ui.chkExpLayersOnly, SIGNAL("clicked(bool)"), self.toggleLayersOnly)
    QObject.connect(self.dlg.ui.checkBoxCurrentProject, SIGNAL("clicked(bool)"), self.toggleUseCurrentProject)
    QObject.connect(self.dlg.ui.btnChooseFooterFile, SIGNAL("clicked()"), self.setFooterFile)
    QObject.connect(self.dlg.ui.btnChooseHeaderFile, SIGNAL("clicked()"), self.setHeaderFile)
    QObject.connect(self.dlg.ui.btnChooseTemplateFile, SIGNAL("clicked()"), self.setTemplateFile)
    QObject.connect(self.dlg.ui.buttonBox, SIGNAL("accepted()"), self.ok_clicked)
    QObject.connect(self.dlg.ui.buttonBox, SIGNAL("helpRequested()"), self.helpRequested)
    # qgs-project defaults to current instance
    project = QgsProject.instance()
    self.dlg.ui.txtQgisFilePath.setText(project.fileName())
    # get some settings from former successfull exports
    # defaults are defined in ms_export.py and set in mapserverexportdialog.py
    settings = QSettings()
    # map-file name and force mapfileChanged to enable/disable ok button
    self.dlg.ui.txtMapFilePath.setText(settings.value("/MapserverExport/mapfileName", QVariant("")).toString ()) 
    self.mapfileChanged(self.dlg.ui.txtMapFilePath.text())
    # map width and height
    if settings.contains("/MapserverExport/mapWidth"):
      self.dlg.ui.txtMapWidth.setText(settings.value("/MapserverExport/mapWidth").toString ())
    if settings.contains("/MapserverExport/mapHeight"):
      self.dlg.ui.txtMapHeight.setText(settings.value("/MapserverExport/mapHeight").toString ())
    # MapServer IMAGETYPE's [gif|png|jpeg|wbmp|gtiff|swf|userdefined]
    self.dlg.ui.cmbMapImageType.addItems(QStringList(["agg","png","gif","jpeg","wbmp","gtiff","swf","userdefined"]))
    if settings.contains("/MapserverExport/imageType"):
      idx = self.dlg.ui.cmbMapImageType.findText(settings.value("/MapserverExport/imageType").toString ())
      self.dlg.ui.cmbMapImageType.setCurrentIndex(idx)
    # MapServer URL (default value already set by dialog defaults)
    if settings.contains("/MapserverExport/mapserverUrl"):
      self.dlg.ui.txtMapServerUrl.setText(settings.value("/MapserverExport/mapserverUrl").toString())
    # set title or default to one if none available
    title = project.title()
    if title == "":
      title = "QGIS-MAP"
    self.dlg.ui.txtMapName.setText(title)
    # TODO: fetch units used from current project
    # QGIS: Meters, Feet, Degrees, UnknownUnit since 1.4 also: DecimalDegrees, DegreesMinutesSeconds, DegreesDecimalMinutes 	
    # Mapserver: UNITS [feet|inches|kilometers|meters|miles|dd]
    self.dlg.show()

  def helpRequested(self):
    QgsContextHelp.run( "MapServerExport" )

  def ok_clicked(self):
    # check if current project is saved or dirty
    if not self.checkCurrentProject():
      # abort because user apparently did not wat to save or Cancelled
      return
    if not self.checkMapFile():
      print "Failed for Map file check, try again..."
      return
    else:
      self.saveMapFile()

  def toggleUseCurrentProject(self, boolUseCurrent):
    self.dlg.ui.txtQgisFilePath.setEnabled(not boolUseCurrent)
    self.dlg.ui.btnChooseProjectFile.setEnabled(not boolUseCurrent)
    if boolUseCurrent:
      #if self.dlg.ui.txtQgisFilePath.text().size() == 0:
      # reload path of current project
      self.dlg.ui.txtQgisFilePath.setText(QgsProject.instance().fileName())
    else:  
      # open dialog to choose project file
      self.setProjectFile()
    

  def saveMapFile(self):   
    # get the settings from the dialog and export the map file
    print "Creating exporter using '%s' and '%s'" % (self.dlg.ui.txtQgisFilePath.text(), self.dlg.ui.txtMapFilePath.text())
    exporter = Qgis2Map(unicode(self.dlg.ui.txtMapFilePath.text()))
    # Parse qgis project file and check success
    if not(exporter.setQgsProject(self.dlg.ui.txtQgisFilePath.text())):
      QMessageBox.warning(self.dlg, "No Map file export!", "Map file not exported because no valid qgis project file was given.")
      return
    if exporter.projectHasNewSymbology():
      QMessageBox.information(self.dlg, "New Symbology layer(s) found", "The project you selected holds layer(s) which use 'New Symbology'.\n\nCurrently this plugin is not able to handle this.\n\nPlease change symbology of these layer(s) to 'Old Symbology'.")
      self.dlg.hide()
      return
    self.dlg.hide()
    print "Setting options"
    exporter.setOptions(
        unicode(self.dlg.ui.txtMapServerUrl.text()),
        unicode(self.dlg.ui.cmbMapUnits.itemData( self.dlg.ui.cmbMapUnits.currentIndex() ).toString()),
        unicode(self.dlg.ui.cmbMapImageType.currentText()),
        unicode(self.dlg.ui.txtMapName.text()),
        unicode(self.dlg.ui.txtMapWidth.text()),
        unicode(self.dlg.ui.txtMapHeight.text()),
        unicode(self.dlg.ui.txtWebTemplate.text()),
        unicode(self.dlg.ui.txtWebHeader.text()),
        unicode(self.dlg.ui.txtWebFooter.text()),
        self.dlg.ui.checkBoxDump.isChecked(),
        self.dlg.ui.checkBoxForce.isChecked(),
        self.dlg.ui.checkBoxAntiAlias.isChecked(),
        self.dlg.ui.checkBoxPartials.isChecked(),
        self.dlg.ui.chkExpLayersOnly.isChecked(),
        unicode(self.dlg.ui.txtFontsetPath.text()),
        unicode(self.dlg.ui.txtSymbolsetPath.text())
    )
    print "Calling writeMapFile"
    try:
      result = exporter.writeMapFile()
    except Exception, err:
      QMessageBox.information(self.dlg, "MapServer Export Error", str(err))
      return
    # ok succesfull: write some setting for a next session
    settings = QSettings()
    # mapfile name 
    settings.setValue("/MapserverExport/mapfileName", QVariant(self.dlg.ui.txtMapFilePath.text()))
    # map width and heigth
    settings.setValue("/MapserverExport/mapWidth", QVariant(self.dlg.ui.txtMapWidth.text()))
    settings.setValue("/MapserverExport/mapHeight", QVariant(self.dlg.ui.txtMapHeight.text()))
    # mapserver url
    settings.setValue("/MapserverExport/mapserverUrl", QVariant(self.dlg.ui.txtMapServerUrl.text()))
    # map ImageType
    settings.setValue("/MapserverExport/imageType", QVariant(self.dlg.ui.cmbMapImageType.currentText()))
    # show results
    QMessageBox.information(self.dlg, "MapServer Export Results", result)

  def mapfileChanged(self, text):
    # Enable OK button
    btnOk = self.dlg.ui.buttonBox.button(QDialogButtonBox.Ok)
    if text.size() > 0:
      btnOk.setEnabled(True)
    else:
      btnOk.setEnabled(False)      

  def checkMapFile(self):
    # Check if map file name is provided
    mapFileName = self.dlg.ui.txtMapFilePath.text()
    if mapFileName.size() == 0:
      QMessageBox.warning(self.dlg, "Not saved!", "Map file not saved because no file name was given")
      return False
    # Check/fix for .map extension (mapserver fails to read otherwise)
    if not mapFileName.trimmed().endsWith('.map'):
      mapFileName += '.map'
    self.dlg.ui.txtMapFilePath.setText(mapFileName)
    # Check if map file exists and we should overwrite it
    if QFile(mapFileName).exists():
      if QMessageBox.Cancel == QMessageBox.question(self.dlg, "Overwrite excisting Map file?",
                    "Map file \"" + mapFileName + "\" already exists. \nShould we overwrite it?",
                    QMessageBox.Yes, QMessageBox.Cancel):
        return False
    # mapfile ok, extension ok, overwrite  ok
    return True
    
  # check if current project is saved and or dirty (has modifications)
  def checkCurrentProject(self, forUnload=False):
    project = QgsProject.instance()
    # question: save project on loading export dialog?
    if project.isDirty():
      msg = "Save project to \"" + project.fileName() + "\" before exporting?\nOnly the last saved version of your project will be exported."
      if project.fileName()=="":
        msg = "Please save project before exporting.\nOnly saved projects can be exported."
      if forUnload:
        msg = "Save project first?\nAfter saving, this project will be unloaded."
      shouldSave = QMessageBox.question(self.dlg, "Save?", msg,
                    QMessageBox.Yes, QMessageBox.No, QMessageBox.Cancel)
      if shouldSave == QMessageBox.Yes:
        if project.fileName().size() == 0:
          # project has not yet been saved:
          saveAsFileName = QFileDialog.getSaveFileName(self.dlg,
                      "Save QGIS Project file as...", ".",
                      "QGIS Project Files (*.qgs)", "Filter list for selecting files from a dialog box")
          # Check that a file was selected
          if saveAsFileName.size() == 0:
            QMessageBox.warning(self.dlg, "Not saved!", "QGis project file not saved because no file name was given.")
            # fall back to using current project if available
            self.dlg.ui.txtQgisFilePath.setText(project.fileName())
          else:
            if not saveAsFileName.trimmed().endsWith('.qgs'):
              saveAsFileName += '.qgs'
            self.dlg.ui.txtQgisFilePath.setText(saveAsFileName)
            project.setFileName(saveAsFileName)
            project.write()
        else:
          project.write()
        #project ok now
        return True
      elif shouldSave == QMessageBox.No and forUnload:
        # unloading a non saved project: just leave ...
        return True
      elif shouldSave == QMessageBox.No:
        # users does not want to save project, but has to because only saved projects can be exported
        return False
      elif shouldSave == QMessageBox.Cancel:
        # user cancelled
        return False
    else: 
      # project saved and not dirty
      return True
      
  def setMapFile(self):
    mapFileName = QFileDialog.getSaveFileName(self.dlg, "Name for the map file", \
      self.dlg.ui.txtMapFilePath.text(), "MapServer map files (*.map);;All files (*.*)","Filter list for selecting files from a dialog box")
    self.dlg.ui.txtMapFilePath.setText(mapFileName)

  def setProjectFile(self):
    # check if it's needed to save current project, will return False if user cancelled
    if not self.checkCurrentProject(True):
      return
    qgisProjectFile = QFileDialog.getOpenFileName(self.dlg, "Choose a QGIS Project", \
      ".", "QGIS Project Files (*.qgs);;", "Filter list for selecting files from a dialog box")
    if not qgisProjectFile:
      # cancelled: check checkBoxCurrentProject again 
      self.dlg.ui.checkBoxCurrentProject.setChecked(True)
      self.dlg.ui.txtQgisFilePath.setEnabled(False)
      return
    try:
      # reading a non-qgs or not existing file results in qgis crash
      # QgsProject.instance().read(QFileInfo(qgisProjectFile))
      # we try to open the file first to see if it can be parsed...
      exporter = Qgis2Map(unicode(self.dlg.ui.txtMapFilePath.text()))
      if exporter.setQgsProject(qgisProjectFile):
        # project file OK !!
        pass
      if exporter.projectHasPostgisLayers():
        loadProject = QMessageBox.question(self.dlg, "Load project?", 
            "The project you selected holds one or more postgis layers. \nTo be able to export a valid DATA string in the map file,\nthe project should be loaded into QGIS. \nNot loading can result in non valid DATA strings in map file.\nSo, should we load it into qgis?",
            QMessageBox.Yes, QMessageBox.No)
        if loadProject == QMessageBox.Yes:
          QgsProject.instance().read(QFileInfo(qgisProjectFile))
        else:
          # postgis, but user refuses to load it, just go and see what happens
          pass
      else:
        # NO postgis, go
        pass
    except Exception, err:
      QMessageBox.information(self.dlg, "Error reading or loading the selected project file", str(err))
      self.dlg.ui.checkBoxCurrentProject.setChecked(True)
      self.dlg.ui.txtQgisFilePath.setEnabled(False)
      return
    self.dlg.ui.txtQgisFilePath.setText(qgisProjectFile)

  def setTemplateFile(self):
    templateFile = QFileDialog.getOpenFileName(self.dlg, "Choose the MapServer template file", \
      ".", "All files (*.*)", "Filter list for selecting files from a dialog box")
    self.dlg.ui.txtWebTemplate.setText(templateFile)

  def setHeaderFile(self):
    headerFile = QFileDialog.getOpenFileName(self.dlg, "Choose the MapServer header file", \
      ".", "All files (*.*)",  "Filter list for selecting files from a dialog box")
    self.dlg.ui.txtWebHeader.setText(headerFile)

  def setFooterFile(self):
    footerFile = QFileDialog.getOpenFileName(self.dlg, "Choose the MapServer footer file", \
        ".", "All files (*.*)", "Filter list for selecting files from a dialog box")
    self.dlg.ui.txtWebFooter.setText(footerFile)
  
  def toggleLayersOnly(self, isChecked):
    # disable other sections if only layer export is desired
    self.dlg.ui.grpPaths.setEnabled(not isChecked)
    self.dlg.ui.grpMap.setEnabled(not isChecked)
