# -*- coding: utf-8 -*-
"""qt.gui.py"""

# Copyright (C) 2009-2017 Federico Brega, Pierluigi Villani

# 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 3
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.

from PyQt5 import QtCore
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from . import ui_qt
import glal
import os.path
from themes import ThemeManager
from version import VERSION, VERSION_CHECK_ENABLED
import io
from functools import reduce

class Gui(QMainWindow, ui_qt.Ui_MainWindow):
    """Main frame"""
    def __init__(self, parent, title, size):
        QMainWindow.__init__(self, parent)
        self.setupUi(self)
        self.setWindowTitle(title)
        self.notebook.removeTab(0)
        self.cg_exit = None  # check on exit callback
        self.setContextMenuPolicy(QtCore.Qt.PreventContextMenu)
        self.resize(QtCore.QSize(size[0], size[1]))

        # center Gui
        screen = QDesktopWidget().screenGeometry()
        dimension = self.geometry()
        self.move((screen.width() - dimension.width()) / 2,
                  (screen.height() - dimension.height()) / 2)
        if size[2]:
            self.showMaximized()

        # Set standard icons
        icon1 = QIcon(QPixmap(":/qt-icons/pixmaps/open.png"))
        icon1 = QIcon.fromTheme("document-open",  icon1)
        self.menu_item_open.setIcon(icon1)
        icon2 = QIcon(QPixmap(":/qt-icons/pixmaps/save_as.png"))
        icon2 = QIcon.fromTheme("document-save-as",  icon2)
        self.menu_item_save_as.setIcon(icon2)
        icon0 = QIcon(QPixmap(":/qt-icons/pixmaps/save.png"))
        icon0 = QIcon.fromTheme("document-save",  icon0)
        self.menu_item_save.setIcon(icon0)
        icon3 = QIcon(QPixmap(":/qt-icons/pixmaps/new.png"))
        icon3 = QIcon.fromTheme("document-new",  icon3)
        self.menu_item_new.setIcon(icon3)
        icon_about = QIcon.fromTheme("help-about")
        self.menu_item_about.setIcon(icon_about)
        icon_bug = QIcon.fromTheme("tools-report-bug")
        self.menu_item_bug.setIcon(icon_bug)
        icon_exit = QIcon.fromTheme("application-exit")
        self.menu_item_exit.setIcon(icon_exit)
        icon_server = QIcon.fromTheme("network-server")
        self.menu_Kml_server.setIcon(icon_server)
        icon_pref = QIcon.fromTheme("preferences-desktop")
        self.preferences.setIcon(icon_pref)

        # Group the altitude services to have radio button.
        serv_Action_group = QActionGroup(self)
        serv_Action_group.addAction(self.menu_item_s1)
        serv_Action_group.addAction(self.menu_item_s2)
        serv_Action_group.addAction(self.menu_item_s3)

        self.menu_item_about.triggered.connect(self.onabout)

    def closeEvent(self, event):
        """If a close event is generated call the controller"""
        if self.cg_exit():
            event.accept()
        else:
            event.ignore()

    def quit(self):
        """ Exit from CycloGraph"""
        self.close()

    def getdimensions(self):
        """Return window width and height"""
        return (self.size().width(), self.size().height())

    def ismaximized(self):
        """Return true if window is maximized"""
        return self.isMaximized()

    def addstatusbartext(self, text):
        """Show text in the statusbar for 5000 ms"""
        self.statusbar.showMessage(text, 5000)

    def onabout(self):
        """About dialog"""
        description = """CycloGraph"""
        licence = """
CycloGraph 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 3 of the License, or (at your option) any later version.

CycloGraph is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with this program. If not, see http://www.gnu.org/licenses/.
"""
        text = 'Copyright (C) 2008-2017 Federico Brega, Pierluigi Villani\n' + \
        'http://cyclograph.sourceforge.net\n' + licence
        QMessageBox.about(self, description +" "+ VERSION, text)

class Page(QFrame, ui_qt.Ui_Frame_Page):
    """Page showed in a tab"""
    def __init__(self, parent):
        QFrame.__init__(self, parent)
        self.savedfilepath = None
        self.slopecounternew = 0
        self.modified = False
        self.setupUi(self)

    def insert_row(self, row_num, cp):
        """Insert item to this page at position row_num"""
        self.tableWidget.insertRow(row_num)
        for col in range(len(cp)):
            item = QTableWidgetItem(str(cp[col]))
            self.tableWidget.setItem(row_num, col, item)

    def delete_row(self, row_num):
        """Removes row at position row_num in this page."""
        self.tableWidget.removeRow(row_num)

    def get_sel_row(self):
        """Gives the row number that is selected."""
        row = self.tableWidget.currentRow()
        return row

    def get_sel_multi_row(self):
        """Gives a list with the row number of selected items."""
        rows = [self.tableWidget.row(item) \
                for item in self.tableWidget.selectedItems()]
        rows = list(set(rows))
        rows.sort()
        return rows

class Managecp(QDialog, ui_qt.Ui_manag_dlg):
    """Add and edit dialog"""
    def __init__(self, parent, title,
                 dist="", alt="", name=""):
        QDialog.__init__(self, parent)
        self.setupUi(self)
        self.setWindowTitle(title)
        self.distance.setText(dist)
        self.altitude.setText(alt)
        self.name.setText(name)

    def show_modal(self):
        """Shows the dialog and returns True if the value has to be chenged."""
        val = self.exec_()
        return val == QDialog.Accepted

    def destroy(self):
        """Destroy current managecp"""
        pass

    def getcp(self):
        """Gives the modified values."""
        dist = self.distance.text()
        alt = self.altitude.text()
        name = self.name.text()
        return (dist, alt, name)

class FormSlopeInfo(QDialog, ui_qt.Ui_Slope_Dialog):
    """info dialog about the slope"""
    def __init__(self, parent, slopedata):
        QDialog.__init__(self, parent)
        self.setupUi(self)
        self.s_name.setText(slopedata['name'])
        self.s_state.setText(slopedata['state'])
        self.s_author.setText(slopedata['author'])
        self.s_email.setText(slopedata['email'])
        self.average_grad.setText(slopedata['average_grad'])
        self.max_grad.setText(slopedata['max_grad'])
        self.height_difference.setText(slopedata['height_difference'])
        self.height_gain.setText(slopedata['height_gain'])
        self.s_comment.setText(slopedata['comment'])

    def getslopeinfo(self):
        """Returns the slope infos: name, state, comment"""
        return {'name': self.s_name.text(),
                'state': self.s_state.text(),
                'author': self.s_author.text(),
                'email': self.s_email.text(),
                'comment': self.s_comment.toPlainText()}

FONT_TYP = {
            QFont.Light: "light",
            QFont.Normal: "normal",
            QFont.Bold: "bold",
            QFont.DemiBold: "bold",
            QFont.Black: "bold",
           }
class Preferences(QDialog, ui_qt.Ui_Pref_Dialog):

    """Set gradient's color"""
    def __init__(self, parent, pref, default_pref):
        QDialog.__init__(self, parent)
        self.setupUi(self)

        self.default = default_pref
        # General
        self.olines.setChecked(pref['olines'])
        self.effect3d.setChecked(pref['3d'])
        self.legend.setChecked(pref['legend'])
        self.showinfo.setChecked(pref['sinfo'])
        self.vcheck.setChecked(pref['vcheck'])
        if not VERSION_CHECK_ENABLED:
            self.label.hide()
            self.vcheck.hide()
        res = 0
        if pref['width'] == 1600:
            res = 3
        if pref['width'] == 1280:
            res = 2
        elif pref['width'] == 1024:
            res = 1
        self.res.setCurrentIndex(res)

        current = 0
        for theme in pref['themelist']:
            self.theme.addItem("")
            self.theme.setItemText(current, theme)
            if theme == pref['theme']:
                self.theme.setCurrentIndex(current)
            current += 1

        self.serv.setCurrentIndex(0)
        if pref['serv'] == 'usgs.net':
            self.serv.setCurrentIndex(2)
        elif pref['serv'] == 'earthtools.org':
            self.serv.setCurrentIndex(1)
        # Fonts
        self.fontbuttonlist = [self.fdesc,
                               self.fgrad,
                               self.ftitle]
        self.fontbuttongroup = QButtonGroup(parent)
        for id_, button in enumerate(self.fontbuttonlist):
            self.fontbuttongroup.addButton(button, id_)
        desc = pref['fdesc']
        self.fdesc.setText(desc['des']+' | '+str(desc["dim"]))
        self.fdesc.desc = desc
        desc = pref['fgrad']
        self.fgrad.setText(desc['des']+' | '+str(desc["dim"]))
        self.fgrad.desc = desc
        desc = pref['ftitle']
        self.ftitle.setText(desc['des']+' | '+str(desc["dim"]))
        self.ftitle.desc = desc
        self.fontbuttongroup.buttonClicked[int].connect(self.font_button_clicked)
        # Color group buttons
        self.buttonlist = [self.button1,
                           self.button2,
                           self.button3,
                           self.button4,
                           self.button5,
                           self.button6,
                           self.button7]
        self.buttongroup = QButtonGroup(parent)
        for id_, button in enumerate(self.buttonlist):
            self.buttongroup.addButton(button, id_)
        self.colorlist = [[int(x) for x in color[4:-1].split(',')] \
                           for color in pref['colors']]
        for index in range(len(self.buttonlist)):
            self.setcolor(index, self.colorlist[index])
        self.buttonBox.clicked['QAbstractButton *'].connect(self.restore_default)
        self.bindsgc()
        # Author info
        self.aname.setText(pref['aname'])
        self.amail.setText(pref['amail'])

        # Level group SpinButtons
        self.levlist = [self.lev_1,
                           self.lev_2,
                           self.lev_3,
                           self.lev_4,
                           self.lev_5,
                           self.lev_6,
                           self.lev_7]
        for i in range(len(pref['levels'])):
            self.levlist[i].setValue(float(pref['levels'][i]))
            self.levlist[i].editingFinished.connect(self.updatelevelconstraints)
        self.updatelevelconstraints()

    def restore_default(self, button):
        button_id = self.buttonBox.standardButton(button)
        if button_id == QDialogButtonBox.RestoreDefaults:
            self.olines.setChecked(bool(self.default['orizzontal lines']))
            self.showinfo.setChecked(bool(self.default['slopeinfo']))
            self.effect3d.setChecked(bool(self.default['effect-3d']))
            self.vcheck.setChecked(bool(self.default['latestVersionCheck']))
            res = 0
            if self.default['width'] == 1600:
                res = 3
            if self.default['width'] == 1280:
                res = 2
            elif self.default['width'] == 1024:
                res = 1
            self.res.setCurrentIndex(res)
            index = self.theme.findText(self.default['theme'])
            if index:
                self.theme.setCurrentIndex(index)
            self.serv.setCurrentIndex(0)
            if self.default['server'] == 'usgs.net':
                self.serv.setCurrentIndex(2)
            elif self.default['server'] == 'earthtools.org':
                self.serv.setCurrentIndex(1)

            self.fdesc.setText(self.default['font-des 1']+' | '+self.default['font-dim 1'])
            self.fdesc.desc = {
                                   "des": self.default['font-des 1'],
                                   "typ": self.default['font-typ 1'],
                                   "dim": self.default['font-dim 1']
                               }
            self.fgrad.setText(self.default['font-des g']+' | '+self.default['font-dim g'])
            self.fgrad.desc = {
                                   "des": self.default['font-des g'],
                                   "typ": self.default['font-typ g'],
                                   "dim": self.default['font-dim g']
                               }
            self.ftitle.setText(self.default['font-des t']+' | '+self.default['font-dim t'])
            self.ftitle.desc = {
                                   "des": self.default['font-des t'],
                                   "typ": self.default['font-typ t'],
                                   "dim": self.default['font-dim t']
                                }
            self.aname.setText(self.default['authorname'])
            self.amail.setText(self.default['authormail'])

            colorlist = [self.default["color "+str(i)] for i in range(1, 8)]
            self.colorlist = [list(map(int, color[4:-1].split(','))) \
                               for color in colorlist]
            for index in range(len(self.buttonlist)):
                self.setcolor(index, self.colorlist[index])

            levellist = [self.default["level "+str(i)] for i in range(1, 8)]
            for i in range(len(self.levlist)):
                # disable range fot this widget
                self.levlist[i].setMinimum(0.0)
                self.levlist[i].setMaximum(90.0)
                # set the range for this widget
                self.levlist[i].setValue(float(levellist[i]))
            self.updatelevelconstraints()

    def bindsgc(self):
        """Bind Setgcolor buttons """
        self.buttongroup.buttonClicked[int].connect(self.changecolor)

    def setcolor(self, index, color):
        """Set color in the colorlist"""
        qcolor = QColor(*color)
        palette = self.buttonlist[index].palette()
        palette.setColor(QPalette.Button, qcolor)
        self.colorlist[index] = (color[0], color[1], color[2])
        self.buttonlist[index].setStyleSheet("QWidget { background-color: %s }"
            % qcolor.name())

    def updatelevelconstraints(self):
        for i in range(1, len(self.levlist)):
            self.levlist[i-1].setMaximum(self.levlist[i].value()-0.1)
        for i in range(len(self.levlist)-1):
            self.levlist[i+1].setMinimum(self.levlist[i].value()+0.1)

    def changecolor(self, index):
        """Change the color used by plot to fill different classes of gradients
        """
        button = self.buttonlist[index]
        palette = button.palette()
        qcolor = palette.color(QPalette.Button)
        newcolor = QColorDialog.getColor(qcolor, self)
        if newcolor.isValid():
            self.setcolor(index,
                          (newcolor.red(),
                           newcolor.green(),
                           newcolor.blue()))

    def font_button_clicked(self, index):
        button = self.fontbuttonlist[index]
        qfont = QFont()
        qfont.setFamily(button.desc["des"])
        qfont.setPointSize(int(button.desc["dim"]))
        try:
            res = eval('QFont.'+button.desc["typ"].capitalize())
        except AttributeError:
            res = QFont.Normal
        qfont.setWeight(res)
        (qfont, ok) = QFontDialog.getFont(qfont, self)
        if ok:
            button.desc = {
                            "des": str(qfont.family()),
                            "typ": FONT_TYP[qfont.weight()],
                            "dim": str(qfont.pointSize())
                          }
            button.setText(str(qfont.family())+' | '+str(qfont.pointSize()))

    def getconfig(self):
        """Returns selected config options"""
        res = str(self.res.currentText()).split(' ')
        settings = {
                    'olines': self.olines.isChecked(),
                    'theme': str(self.theme.currentText()),
                    'sinfo': self.showinfo.isChecked(),
                    'legend': self.legend.isChecked(),
                    'vcheck': self.vcheck.isChecked(),
                    '3d': self.effect3d.isChecked(),
                    'aname': self.aname.text(),
                    'amail': self.amail.text(),
                    'fdesc': self.fdesc.desc,
                    'ftitle': self.ftitle.desc,
                    'fgrad': self.fgrad.desc,
                    'width': res[0],
                    'height': res[2],
                    'serv': str(self.serv.currentText()),
                    'colors': ['rgb(%d,%d,%d)' % (clr[0], clr[1], clr[2]) \
                                for clr in self.colorlist],
                    'levels': ['%f' % (lev.value()) for lev in self.levlist]
                   }
        if not VERSION_CHECK_ENABLED:
            del settings['vcheck']
        return settings

class Plot(QMainWindow):
    """ Plot window"""
    def __init__(self, parent, slope, plot_settings,
                 exportfunc, exportfunc_pdf, exportfunc_html):
        QMainWindow.__init__(self, parent)
        self.panel = AnimatedPanel(slope)
        self.setCentralWidget(self.panel)
        title = "CycloGraph"
        if slope.name:
            title += " - " + slope.name
        self.setWindowTitle(title)
        self.myslope = slope

        self.menu_item_save = QAction(_('Save'), self)
        icon_save = QIcon(QPixmap(":/qt-icons/pixmaps/save.png"))
        icon_save = QIcon.fromTheme("document-save-as", icon_save)
        self.menu_item_save.setIcon(icon_save)
        self.menu_item_savepdf = QAction(_('Save PDF'), self)
        self.menu_item_savehtml = QAction(_('Save html'), self)
        self.menu_item_olines = QAction(self)
        self.menu_item_olines.setCheckable(True)
        self.menu_item_olines.setChecked(plot_settings['olines'])
        self.menu_item_olines.setText(_("&Orizzontal lines"))
        self.menu_item_3d = QAction(self)
        self.menu_item_3d.setCheckable(True)
        self.menu_item_3d.setChecked(plot_settings['3d'])
        self.menu_item_3d.setText(_("3D &effect"))
        self.menu_item_legend = QAction(self)
        self.menu_item_legend.setCheckable(True)
        self.menu_item_legend.setChecked(plot_settings['legend'])
        self.menu_item_legend.setText(_("Show &legend"))

        self.themeitems = {}
        self.theme_action_group = QActionGroup(self)
        self.thememanager = ThemeManager()
        for theme in self.thememanager.getthemeslist():
            themeitem = QAction(self)
            themeitem.setCheckable(True)
            if plot_settings['theme'] == theme:
                themeitem.setChecked(True)
            themeitem.setText(theme)
            self.theme_action_group.addAction(themeitem)
            self.themeitems[themeitem] = theme

        menubar = self.menuBar()
        options_menu = menubar.addMenu(_('&Options'))
        options_menu.addAction(self.menu_item_save)
        options_menu.addAction(self.menu_item_savepdf)
        options_menu.addAction(self.menu_item_savehtml)
        options_menu.addAction(self.menu_item_olines)
        options_menu.addAction(self.menu_item_3d)
        options_menu.addAction(self.menu_item_legend)
        themes_menu = QMenu(options_menu)
        themes_menu.setTitle(_("&Theme"))
        for action in list(self.themeitems.keys()):
            themes_menu.addAction(action)
        options_menu.addAction(themes_menu.menuAction())
        res_menu = QMenu(options_menu)
        res_menu.setTitle(_("&Saving resolution"))
        self.resitems = {}
        res_action_group = QActionGroup(self)
        rlist = [('800', '600'), ('1024', '768'), ('1280', '800'), ('1600', '1200')]
        for res in rlist:
            resitem = QAction(self)
            resitem.setCheckable(True)
            restext = res[0]+'x'+res[1]
            resitem.setText(restext)
            if plot_settings['width'] == int(res[0]):
                resitem.setChecked(True)
            res_action_group.addAction(resitem)
            self.resitems[resitem] = restext
            res_menu.addAction(resitem)
        options_menu.addAction(res_menu.menuAction())
        self.exportfunc = exportfunc
        self.exportfunc_pdf = exportfunc_pdf
        self.exportfunc_html = exportfunc_html
        self.myslope.calc()
        self.settings = plot_settings
        self.panel.settings = self.settings
        self.panel.devc = glal.DeviceContext()
        self.resize(plot_settings['width'], plot_settings['height'])

        self.settings['colors'] = plot_settings['colors']

        self.animation3d = QtCore.QPropertyAnimation(self.panel, b"depth")
        self.animation3d.setDuration(1000)
        self.animation3d.setStartValue(100)
        self.animation3d.setEndValue(0)
        self.animationTh = QtCore.QPropertyAnimation(self.panel, b"opacity")
        self.animationTh.setDuration(1000)
        self.animationTh.setStartValue(1.0)
        self.animationTh.setEndValue(0.0)

        self.menu_item_save.triggered.connect(self.saveimage)
        self.menu_item_savepdf.triggered.connect(self.savepdf)
        self.menu_item_savehtml.triggered.connect(self.savehtml)
        self.menu_item_olines.triggered.connect(self.olines)
        self.menu_item_3d.triggered.connect(self.change3d)
        self.menu_item_legend.triggered.connect(self.changelegend)
        self.theme_action_group.triggered['QAction *'].connect(self.changetheme)
        res_action_group.triggered['QAction *'].connect(self.changeres)

    def showplot(self):
        """ Show plot"""
        self.show()

    def saveimage(self):
        """ Save image"""
        self.exportfunc(self.myslope, self.settings)

    def savepdf(self):
        """ Save pdf"""
        self.exportfunc_pdf(self.myslope, self.settings)

    def savehtml(self):
        """ Save html report"""
        self.exportfunc_html(self.myslope, self.settings)

    def change3d(self):
        """ If active switch 3d effect off, else turn it on."""
        if self.settings['3d']:
            self.animation3d.setDirection(self.animation3d.Forward)
        else:
            self.animation3d.setDirection(self.animation3d.Backward)
        self.animation3d.start()

    def changelegend(self):
        """ If active draws legend"""
        self.panel.shot()
        self.settings['legend'] = not self.settings['legend']
        self.animationTh.start()

    def olines(self):
        """ If active draws orizzontal lines"""
        self.panel.shot()
        self.settings['olines'] = not self.settings['olines']
        self.animationTh.start()

    def changetheme(self, action):
        """Change the actual slope theme."""
        self.panel.shot()
        self.settings["theme"] = self.themeitems[action]
        self.animationTh.start()

    def changeres(self, action):
        """Change saving resolution."""
        text = self.resitems[action]
        width, height = text.split('x')
        self.settings['width'] = int(width)
        self.settings['height'] = int(height)

class AnimatedPanel(QWidget):
    """This class implements a personalized widget that draws a slope.
    It also implements the fade between different images and 2D to 3D.
    Using this class prevents from drawing on the menu."""
    def __init__(self, slope, parent=None):
        QWidget.__init__(self, parent)
        self.__old_opacity = 0.0
        self.__depth = 100
        self.myslope = slope

    def shot(self):
        """Create an internal pixmap representing the current state
        of the widget"""
        self.__old_pixmap = QPixmap(self.size())
        self.render(self.__old_pixmap)

    def oldOpacity(self):
        """Get the opacity of the pixamp of the old slope"""
        return self.__old_opacity

    def setOldOpacity(self, opacity):
        """Set the opacity of the pixamp of the old slope"""
        self.__old_opacity = opacity
        self.repaint()

    def changeres(self, action):
        """Change saving resolution."""
        text = self.resitems[action]
        width, height = text.split('x')
        self.settings['width'] = int(width)
        self.settings['height'] = int(height)
    opacity = QtCore.pyqtProperty("double", oldOpacity, setOldOpacity)

    def depth(self):
        """Get the depth of the current slope (0 means 2D)"""
        return self.__depth

    def setDepth(self, depth):
        """Set the depth of the current slope"""
        self.__depth = depth
        self.settings['3d'] = (depth != 0)
        self.repaint()
    depth = QtCore.pyqtProperty("int", depth, setDepth)

    def paintEvent(self, event):
        """Actually draws the content."""
        self.myslope.depth = self.__depth
        self.devc.init_draw_surf(self)
        self.myslope.paint(self.settings, self.devc)
        if self.__old_opacity > 0.0:
            self.devc.paint.resetTransform()
            self.devc.paint.setOpacity(self.__old_opacity)
            self.devc.paint.drawPixmap(0, 0, self.__old_pixmap)
        self.devc.end_draw()

class Map(QDialog, ui_qt.Ui_map):
    """Show map of slopes"""
    def __init__(self, parent, slopename, stringc, savefnct):
        QDialog.__init__(self, parent)
        self.setupUi(self)
        self.savefnct = savefnct
        self.setWindowFlags(QtCore.Qt.Window)
        self.setWindowTitle(_("Map") + " - " + slopename)
        self.buttonBox.clicked['QAbstractButton *'].connect(self.button_clicked)
        mapfile = QtCore.QFile(":/openlayers/cyclograph/map.html")
        if mapfile.open(QtCore.QFile.ReadOnly):
            mapdata = bytes(mapfile.readAll()).decode()
            htmlMap = mapdata % (stringc,)
            mapfile.close()
            self.webView.setHtml(htmlMap, QtCore.QUrl('file://'))
        self.show()

    def saveimage(self, filepath):
        """ Save map image"""
        page = self.webView.page()
        img = QImage(page.contentsSize().toSize(), QImage.Format_ARGB32)
        painter = QPainter(img)
        self.webView.render(painter)
        painter.end()
        img.save(filepath)

    def button_clicked(self, button):
        button_id = self.buttonBox.standardButton(button)
        if button_id == QDialogButtonBox.Save:
            self.savefnct(self)

class Create_kml(QDialog, ui_qt.Ui_Create_KML_Dialog):
    """Create kml dialog"""
    MODE = {_("Open Route Service"): 'ORS',
            _(u'brouter'): 'BROU',
            _('yournavigation'): 'YOURS',
            _("Draw on the map"): 'DRAW'}

    def __init__(self, parent, downloadfunc, savefunc):
        QDialog.__init__(self, parent)
        self.setupUi(self)
        self.setWindowFlags(QtCore.Qt.Window)
        self.downloadfunc = downloadfunc  # used to downlaod from url
        self.savefunc = savefunc
        self.content = None
        self.mode = None
        self.mode_changed(self.listWidget.currentItem().text())
        self.splitter.setStretchFactor(0, 1)
        self.splitter.setStretchFactor(1, 3)

        self.buttonBox.clicked['QAbstractButton *'].connect(self.button_clicked)
        self.listWidget.currentTextChanged['const QString&'].connect(self.mode_changed)

    def webview_init(self):
        """ Check mode and set webview accordingly"""
        if self.mode == 'ORS':
            self.webView.setUrl(QtCore.QUrl("http://openrouteservice.org/"))
        elif self.mode == 'BROU':
            self.webView.setUrl(QtCore.QUrl("http://brouter.de/brouter-web/"))
        elif self.mode == 'YOURS':
            self.webView.setUrl(QtCore.QUrl("http://yournavigation.org/"))
        elif self.mode == 'DRAW':
            url = QtCore.QUrl("qrc:/openlayers/cyclograph/openlayers.html")  # .fromLocalFile(os.path.join(os.path.dirname(__file__),"openlayers.html"))
            self.webView.setUrl(url)

    def button_clicked(self, button):
        button_id = self.buttonBox.standardButton(button)
        if button_id == QDialogButtonBox.Ok:
            self.import_kml()
        elif button_id == QDialogButtonBox.Save:
            self.save_kml()
        elif button_id == QDialogButtonBox.Cancel:
            self.content = None
            #Close the dialog immediately
            QDialog.reject(self)

    def mode_changed(self, sel_string):
        """ Change current mode"""
        newmode = self.MODE[str(sel_string)]
        if newmode != self.mode:
            self.mode = newmode
            self.webview_init()

    def get_ors(self, callback):
        page = self.webView.page()

        #jsresult = frame.evaluateJavaScript("document.getElementById('gpx').innerHTML")
        def jscallback(jsresult):
            if jsresult is None:
                self.content = None
                return
            gpxcontent = str(jsresult)
            from xml.sax.saxutils import unescape
            self.content = unescape(gpxcontent)
            callback()
        page.runJavaScript("document.getElementById('gpx').innerHTML", jscallback) #FIXME: this is not working: getElementById returns null

    def get_brou(self, callback):
        """ Returns kml content from BROU service"""
        page = self.webView.page()

        def jscallback(jsresult):
            if jsresult is None:
                self.content = None
                return
            url = str(jsresult)
            handler = self.downloadfunc(url)
            self.content = handler.read().decode('utf-8')
            callback()
        page.runJavaScript("$(\".value\").find('a:contains(\"GPX\")')[0].href", jscallback)

    def get_yours(self, callback):
        """ Returns kml content from YOURS service"""
        page = self.webView.page()

        def jscallback(jsresult):
            if jsresult is None:
                self.content = None
                return
            elemtext = str(jsresult)
            text = elemtext.split('href="http://yournavigation.org/?')
            turl = "http://www.yournavigation.org/api/1.0/gosmore.php?format=kml&"
            if len(text) > 1:
                turl += text[1].split("\">")[0]
                turl = turl.replace("&amp;", "&")
            handler = self.downloadfunc(turl)
            self.content = handler.read().decode('utf-8')
            callback()
        page.runJavaScript("document.getElementById('feature_info').innerHTML", jscallback)

    def get_draw(self, callback):
        page = self.webView.page()

        def jscallback(jsresult):
            self.content = jsresult
            callback()
        page.runJavaScript("createKml()", jscallback)

    def download(self, callback):
        if self.mode == 'ORS':
            self.get_ors(callback)
        elif self.mode == 'BROU':
            self.get_brou(callback)
        elif self.mode == 'YOURS':
            self.get_yours(callback)
        elif self.mode == 'DRAW':
            self.get_draw(callback)

    def import_kml(self):
        def import_callback():
            QDialog.accept(self)

        self.download(import_callback)

    def save_kml(self):
        def download_callback():
            if not self.content:
                return
            self.savefunc(self.content, self.mode)
            # Do not accept the dialog: it must stay open if the user wants to
            # import without opening the file saved or if he hasn't saved and
            # wants to go back.

        self.download(download_callback)


    def show_modal(self):
        """Shows the dialog and returns True if the value has to be chenged."""
        val = self.exec_()
        return val == QDialog.Accepted

class NumcpsDialog(QDialog, ui_qt.Ui_numcps_dlg):
    """ask cps number you want to import"""
    def __init__(self, max_num_cps, start_dist, finish_dist, file_ext):
        QDialog.__init__(self, None)
        self.setupUi(self)
        self.slider.setMaximum(max_num_cps)
        self.spinBox.setMaximum(max_num_cps)
        self.start.setRange(start_dist, finish_dist)
        self.start.setValue(start_dist)
        self.end.setRange(start_dist, finish_dist)
        self.end.setValue(finish_dist)
        selrange = self.end.value() - self.start.value()
        # the value of the slider_2 is multiplied by 1000 to have a resolution of 0.001
        self.slider_2.setMaximum(selrange*1000)
        self.doubleSpinBox_2.setMaximum(selrange)
        text = _("The %s file contains %d points.\nChoose how many will be imported.") % (file_ext, max_num_cps)
        self.label.setText(text)
        self.file_ext = file_ext
        self.label_2.setText(_("The %s file contains a route of %.3f Km.\nChoose what range you want between points.") % (file_ext, finish_dist))
        self.label_3.setText(_("Choose the type of import you want"))

        self.proceedButton.clicked.connect(self.onProceedClicked)
        self.start.valueChanged.connect(self.updateRange)
        self.end.valueChanged.connect(self.updateRange)
        self.slider_2.valueChanged.connect(self.updateSlider_2)
        self.doubleSpinBox_2.valueChanged.connect(self.updateRange_2)

    def onProceedClicked(self):
        btn = self.buttonGroup.checkedButton()
        if btn == self.all_ or btn == self.automatic:
            self.accept()
        elif btn == self.number:
            self.stackedWidget.setCurrentIndex(1)
        elif btn == self.distance_2:
            self.stackedWidget.setCurrentIndex(2)

    def updateRange(self):
        "The range changed"
        self.start.setMaximum(self.end.value() - self.end.singleStep())
        self.end.setMinimum(self.start.value() + self.start.singleStep())
        self.slider_2.setMaximum((self.end.value() - self.start.value())*1000)
        self.doubleSpinBox_2.setMaximum(self.end.value() - self.start.value())

    def updateRange_2(self):
        "The range_2 changed"
        spinval = self.doubleSpinBox_2.value()
        self.slider_2.setValue(int(spinval*1000))

    def updateSlider_2(self):
        "The Slider_2 value changed"
        sliderval = self.slider_2.value()/1000.0
        self.doubleSpinBox_2.setValue(sliderval)


def preferences(parent, cgpref, defaultpref):
    """ set Preference dialog"""
    dlg = Preferences(parent, cgpref, defaultpref)
    if dlg.exec_() == QDialog.Rejected:
        return {}
    else:
        return dlg.getconfig()


def formslopeinfo(parent, slopedata):
    """ Show slope's info"""
    dlg = FormSlopeInfo(parent, slopedata)
    if dlg.exec_() == QDialog.Rejected:
        return {}
    else:
        return dlg.getslopeinfo()


def FileDialog(parent, title, dirname, filename, other, file_types, rw, FD_OPEN, FD_SAVE):
    """ Show File dialog"""
    filters = reduce(lambda x, y: x + ';;' + y,
                      [("%s (%s)" % elem) for elem in file_types])
    # print filters
    if rw == FD_OPEN:
        result = QFileDialog.getOpenFileName(parent,  title,
                                                 dirname,  filters)
    elif rw == FD_SAVE:
        result = QFileDialog.getSaveFileName(parent,  title,
                                                 dirname,  filters)
    if len(result) == 2:
        #result is a tuple (filename, selected_file_option)
        return str(result[0])
    else:
        return None


def numcpsdlg(max_num_cps, start_dist, finish_dist, file_ext):
    """ask how many cps in a gpx or tcx files shall be imported"""
    dlg = NumcpsDialog(max_num_cps, start_dist, finish_dist, file_ext)
    if dlg.exec_() == QDialog.Rejected:
        return (-1, 0, start_dist, finish_dist)
    elif dlg.buttonGroup.checkedButton() == dlg.automatic:
        return (0, -1, dlg.start.value(), dlg.end.value())
    elif dlg.buttonGroup.checkedButton() == dlg.all_:
        return (0, max_num_cps, dlg.start.value(), dlg.end.value())
    elif dlg.stackedWidget.currentIndex() == 1:
        return (0, dlg.slider.value(), dlg.start.value(), dlg.end.value())
    else:
        value = dlg.doubleSpinBox_2.value()
        return (1, value, dlg.start.value(), dlg.end.value())


def geterrnoroute(parent):
    """Get an error message explaining that you should have selected one
       route"""
    return QMessageBox.warning(parent, _('No route'), _("Specify one route first."),
                                        QMessageBox.Ok)


def managecperr(parent):
    """Get an error message explaining that you should have inserted
       altitude and distance"""
    return QMessageBox.critical(parent, _("Form incomplete"),
                                      _("Distance and altitude are required."),
                                      QMessageBox.Ok)


def mapcoorderr(parent):
    """Get an error message explaining that there are no coords in the slope"""
    return QMessageBox.critical(parent, _("No coordinates"),
                                      _("there aren't coordinates associated with this slope."),
                                      QMessageBox.Ok)


def geterrnocp(parent):
    """Get an error message explaining that you should have at least two cps
       to plot
    """
    return QMessageBox.warning(parent, _('Not enough check points'), _(
"""There aren't enougth check points to plot.
 At least two points are required to plot."""),
                                        QMessageBox.Ok)


def save_changes(parent, filename):
    """Show a message if you close a modified and not saved slope"""
    response = None
    SAVE = _('Save')
    DISCARD = _('Close without saving')
    CANCEL = _('Cancel')
    message = QMessageBox(parent)
    message.setText(_("Save changes to %s before closing?") % filename)
    message.setWindowTitle(_('Save changes'))
    message.setIcon(QMessageBox.Warning)
    save    = message.addButton(SAVE,    QMessageBox.AcceptRole)
    discard = message.addButton(DISCARD, QMessageBox.DestructiveRole)
    cancel  = message.addButton(CANCEL,  QMessageBox.RejectRole)
    message.exec_()
    response = message.clickedButton()
    if response == save:
        return 'SAVE'
    if response == discard:
        return 'DISCARD'
    if response == cancel:
        return 'CANCEL'
    return response

# vim:sw=4:softtabstop=4:expandtab
