#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""cyclograph.py """

# Copyright (C) 2008-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.

import slope
import iofile
import glal
import altitude_downloader
import report_html
from themes import ThemeManager
import os
import gettext
import configparser
import sys
import queue
import argparse
from version import VERSION, LastVersionQuery

# font-typ light, normal, bold
DEFAULT_CONF = {"version": "1.2",
                "orizzontal lines": "True",
                "legend": "True",
                "slopeinfo": "True",
                "effect-3d": "True",
                "theme": "Classic",
                "font-des 1": "Sans",
                "font-typ 1": "normal",
                "font-dim 1": "10",
                "font-des g": "Sans",
                "font-typ g": "normal",
                "font-dim g": "10",
                "font-des t": "Sans",
                "font-typ t": "bold",
                "font-dim t": "20",
                "width": "800",
                "height": "600",
                "color 1": "rgb(0,0,255)",
                "color 2": "rgb(0,255,0)",
                "color 3": "rgb(255,255,0)",
                "color 4": "rgb(255,0,0)",
                "color 5": "rgb(200,0,0)",
                "color 6": "rgb(150,0,0)",
                "color 7": "rgb(100,0,0)",
                "level 1": "0",
                "level 2": "3.0",
                "level 3": "6.0",
                "level 4": "9.0",
                "level 5": "12.0",
                "level 6": "15.0",
                "level 7": "18.0",
                "server": "geonames.org",
                "numcps": "20",
                "widthsize": "540",
                "heightsize": "400",
                "maximized": "False",
                "latestVersionCheck": "True",
                "authorname": "",
                "authormail": "",
                }

SECTIONS = ["global", "font", "resolution", "colors", "levels", "kml", "main window"]


class Cyclograph():
    """Controller according to MCV"""
    def __init__(self):
        self.slopelist = slope.SlopeList()
        self.init_configparser()
        size = (self.config.getint("main window", "widthsize"),
                self.config.getint("main window", "heightsize"),
                self.config.getboolean("main window", "maximized"))
        self.mygui = glal.gui.Gui(None, "CycloGraph", size)
        self.notebook = glal.Notebook(self.mygui.notebook, self.close)
        self.slopecounter = 1

        self.setguiopt()

        glal.bind(self.new_slope, self.mygui.menu_item_new)
        glal.bind(self.open_slope, self.mygui.menu_item_open)
        glal.bind(self.create_kml, self.mygui.menu_item_createkml)
        glal.bind(self.save_slope, self.mygui.menu_item_save)
        glal.bind(self.save_slope_as, self.mygui.menu_item_save_as)
        glal.bind(self.preferences, self.mygui.preferences)
        glal.bind(self.ser1, self.mygui.menu_item_s1)
        glal.bind(self.ser2, self.mygui.menu_item_s2)
        glal.bind(self.ser3, self.mygui.menu_item_s3)
        glal.bind(glal.signalbug, self.mygui.menu_item_bug)
        glal.bind_close(self.exit, self.mygui)

        # Toolbar
        glal.ToolbarBind(self.add_cp, self.mygui.action_add)
        glal.ToolbarBind(self.edit_cp, self.mygui.action_edit)
        glal.ToolbarBind(self.delete_cp, self.mygui.action_delete)
        glal.ToolbarBind(self.plot_graph, self.mygui.action_plot)
        glal.ToolbarBind(self.map_slope, self.mygui.action_map)
        glal.ToolbarBind(self.info, self.mygui.action_properties)

        glal.Message().subscribe(self.slope_add, self.slopelist.message, "SLOPE_ADD")
        glal.Message().subscribe(self.slope_del, self.slopelist.message, "SLOPE_DEL")
        self.mygui.show()

        # Check if this is the latest version
        if self.config.getboolean("main window", "latestVersionCheck"):
            def notify():
                self.versionchk.stop()
                if self.lvq.islast():
                    return
                glal.addstatusbartext(self.mygui, _("A new version of CycloGraph is out!")+"(%s)" % self.lvq.lastversion)
                del self.versionchk
                del self.lvq

            self.lvq = LastVersionQuery()
            self.lvq.start()
            self.versionchk = glal.Timer(10000, notify)  # wait 1s before notify
            self.versionchk.start()

    def init_configparser(self):
        """Create a new configparser, and inizializes with default values,
        if the user saved his preferencese, open configuration file and
        use this values
        """
        self.config = configparser.ConfigParser(DEFAULT_CONF)
        self.config.read(get_config_file_path())

        for sec in SECTIONS:
            if not self.config.has_section(sec):
                self.config.add_section(sec)
        if self.config.get("global", "version") > VERSION:
            # The configuration file has been saved by a future version of
            # CycloGraph thus it is not guaranteeted to be backward compatible.
            # It is better to leave the file as it is and use default settings.
            self.defaultpref()
            return
        if self.config.get("global", "version") < '1.1':
            glal.addstatusbartext(self.mygui,
                                 _("old cofiguration issue."))

    def setguiopt(self):
        """Set gui options to the value from preferences"""
        serv = self.config.get("kml", "server")
        glal.OptionCheck(self.mygui.menu_item_s1, True)
        glal.OptionCheck(self.mygui.menu_item_s2, serv == "earthtools.org")
        glal.OptionCheck(self.mygui.menu_item_s3, serv == "usgs.net")
        altitude_downloader.choose_service(serv)

# Functions responding to menu events ###

    def new_slope(self, event=None):
        """ Create new slope"""
        pagen = self.slopelist.new_slope()
        self.slopelist.set_author(pagen, self.config.get("global", "authorname"))
        self.slopelist.set_email(pagen, self.config.get("global", "authormail"))
        pag = self.notebook.Page()
        pag.slopecounternew = self.slopecounter
        self.notebook.add_page(pag, _('slope')+" "+str(self.slopecounter))
        self.slopecounter = self.slopecounter + 1
        glal.enable_saving(self.mygui, True)

    def open_slope(self, event=None, filepath=None):
        """ Open a file"""
        # statusok = True
        types = [('Supported files', '*.cgx *.csv *.xml *.gpx *.kml *.fitlog *.tcx *.crp *.sal *.txt'),
                    ('CycloGraphXml (*.cgx)', '*.cgx'),
                    ('CycloGraph (*.csv)', '*.csv'),
                    ('Ciclomaniac (*.xml)', '*.xml'),
                    ('GPS eXchange Format (*.gpx)', '*.gpx'),
                    ('Keyhole Markup Language (*.kml)', '*.kml'),
                    ('SportTracks Fitlog files (*.fitlog)', '*.fitlog'),
                    ('Training Center xml (*.tcx)', '*.tcx'),
                    ('Ciclotour (*.crp)', '*.crp'),
                    ('Salitaker (*.sal)', '*.sal'),
                    ('Ciclomaniac (*.txt)', '*.txt')]
        if not filepath:
            filepath = glal.FileDialog(self.mygui, _("Choose a file"), '', "",
                                    types, glal.FD_OPEN)
        if not filepath:
            return
        for nump in range(len(self.slopelist)):
            # Check if the file has already been opened.
            page = self.notebook.get_page(nump)
            if page.savedfilepath == filepath:
                # Select the page of the selected file.
                self.notebook.setselpagnum(nump)
                return
        slope_num = self.slopelist.new_slope()
        pag = self.notebook.Page()
        pag.savedfilepath = filepath
        self.notebook.add_page(pag, "")

        slopefile = None
        filetype = "None"

        if filepath.lower().endswith(".gpx"):
            slopefile = iofile.load_gpxfile(filepath)
            filetype = "gpx"
        elif filepath.lower().endswith(".kml"):
            slopefile = iofile.load_kmlfile(filepath)
            filetype = "kml"
        elif filepath.lower().endswith(".fitlog"):
            slopefile = iofile.load_fitlogfile(filepath)
            filetype = "fitlog"
        elif filepath.lower().endswith(".tcx"):
            slopefile = iofile.load_tcxfile(filepath)
            filetype = "tcx"

        if filetype == "None":
            statusok = iofile.open_file(filepath, self.slopelist, slope_num)
        else:
            if slopefile is not None:
                max_num_cps = len(slopefile)
                (seltype, num, start_dist, end_dist) = glal.gui.numcpsdlg(max_num_cps,
                                                    slopefile.min_dist(),
                                                    slopefile.max_dist(),
                                                    filetype)
                if (seltype == -1):
                    nbook = None
                    self.slopelist.del_slope(slope_num)
                    self.notebook.remove_page(slope_num)
                    return
                if not slopefile.hasAltitudes:
                    self.import_altitudes(num, seltype, slopefile, start_dist, end_dist, slope_num)
                    return
                statusok = slopefile.newSlope(self.slopelist, slope_num, num, start_dist, end_dist, seltype)
                # listcoords = self.slopelist.get_coords(slope_num)
                # print(listcoords)
            else:
                statusok = False

        if not statusok:
            nbook = None
            self.slopelist.del_slope(slope_num)
            self.notebook.remove_page(slope_num)
            glal.addstatusbartext(self.mygui, _("Unable to open file"))
            return
        self.tabupdate(slope_num)
        glal.enable_saving(self.mygui,  True)
        if statusok == 2:
            glal.addstatusbartext(self.mygui,
                                  _("Old file format: please save it again."))

    def create_kml(self, event=None):
        """ Create kml file"""
        # TODO: check for internet connection
        def downloadfunc(url):
            """Download current create_kml page"""
            from urllib.request import urlopen
            if url:
                response = urlopen(url)
                return response

        def savefunc(content, mode):
            """Save as kml current create_kml selected route"""
            if mode != 'DRAW' and mode != 'YOURS':
                content = iofile.gpxtokml(content)
            filepath = glal.FileDialog(None, _("kml name"), "", "",
                   [("Keyhole Markup Language (*.kml)", "*.kml")], glal.FD_SAVE)
            if not filepath:
                return
            if not filepath.endswith('.kml'):
                filepath = filepath+".kml"
            with open(filepath, "w") as outfile:
                print(content, file=outfile)

        create = glal.gui.Create_kml(self.mygui, downloadfunc, savefunc)
        accepted = create.show_modal()
        if not accepted:
            create.destroy()
            return
        if not create.content:
            glal.addstatusbartext(self.mygui,
            _("Network error : unable to create an handler to download kml route."))
            return
        if create.mode == 'DRAW':
            # If the user drawned the path cg must use every point,
            # and not waste user effort.
            numcps = 99999  # Hack: unlikely any user will draw more points.
            kmlcontent = create.content
        else:
            numcps = self.config.getint("kml", "numcps")
        if create.mode == 'ORS':
            gpxcontent = create.content
            kmlcontent = iofile.gpxtokml(gpxcontent)
        elif create.mode == 'BROU':
            gpxcontent = create.content
            kmlcontent = iofile.gpxtokml(gpxcontent)
        elif create.mode == 'YOURS':
            kmlcontent = create.content
        kmlfile = iofile.load_kmlfile(kmlcontent)
        if kmlfile is None:
            glal.addstatusbartext(self.mygui, _("Error on loading slope"))
            create.destroy()
            return
        slope_num = self.slopelist.new_slope()
        nbook = self.notebook.Page()
        nbook.slopecounternew = self.slopecounter
        self.notebook.add_page(nbook, _('slope %d') % self.slopecounter)
        self.slopecounter = self.slopecounter + 1
        self.tabupdate(slope_num, '*')

        self.import_altitudes(numcps, 0, kmlfile,
                                         kmlfile.min_dist(),
                                         kmlfile.max_dist(),
                                         slope_num)
        create.destroy()

    def import_altitudes(self, num, seltype, slopefile, start_dist, end_dist, slope_num):
        """ Start the import kml process in CycloGraph"""
        # TODO: check for internet connection
        self.slopenumber_kml = slope_num
        slopefile.getCoords(self.slopelist, slope_num, start_dist, end_dist)
        self.pdlg = glal.ProgressDialog()
        refresh_time = 30  # FIXME: is the measure unit milliseconds?
        self.timer = glal.Timer(refresh_time, self.on_altitude_importing)
        self.queue = queue.Queue()
        self.prsrthrd = altitude_downloader.ImporterThread(self.queue, slopefile, num, start_dist, end_dist, seltype)
        self.prsrthrd.daemon = True
        # start thread and timer
        self.prsrthrd.start()
        self.timer.start()

    def on_altitude_importing(self, widget=None):
        """ Called by a timer:
        check if thread is still executing
        (if not stop timer and, according to status, handle the produced slope)
        reads the progress of importer-thread
        updates the progress dialog
        check if operation is cancelled and tells to thread
        """
        try:
            (progress, chkp) = self.queue.get(block=False)
        except queue.Empty:
            # print("is alive "+str(self.prsrthrd.isAlive()))
            if not self.prsrthrd.isAlive():
                self.timer.stop()
                self.pdlg.destroy()
                if self.prsrthrd.status != 'OK':
                    slope_num = self.slopenumber_kml
                    self.slopelist.del_slope(slope_num)
                    self.notebook.remove_page(slope_num)
                    glal.addstatusbartext(self.mygui,
                                          _("Error while loading data"))
                else:
                    self.tabupdate(self.slopenumber_kml, '*')
                    glal.enable_saving(self.mygui, True)
                del self.prsrthrd
            # there is nothing to update but gtk wants True
            return True
        self.slopelist.add_cp(self.slopenumber_kml, *chkp)
        # There is a new progress value => update the gui
        want2abort = self.pdlg.update(progress)
        if want2abort:
            self.prsrthrd.abort()
        # need to return True to keep timer_gtk running
        return True

    def save_slope_as(self, event=None, pagenumber=None, filename=None):
        """ Save_as selected slope """
        if pagenumber is None:
            pagenumber = self.notebook.getselpagnum()
        myslope = self.slopelist.get_slope_copy(pagenumber)
        filepath = glal.FileDialog(self.mygui, _("Save as"), '', "",
            [("CycloGraphXml(*.cgx)", "*.cgx")], glal.FD_SAVE, filename)
        if not filepath:
            return False
        if not filepath.endswith('.cgx'):
            filepath = filepath + '.cgx'
        iofile.save_file(filepath, myslope)
        page = self.notebook.get_page(pagenumber)
        page.savedfilepath = filepath
        self.tabupdate(pagenumber)
        return True

    def save_slope(self, event=None):
        """ Save selected slope """
        pagenumber = self.notebook.getselpagnum()
        page = self.notebook.get_page(pagenumber)
        myslope = self.slopelist.get_slope_copy(pagenumber)
        filepath = page.savedfilepath
        if filepath is None or not filepath.endswith('.cgx'):
            filepath = glal.FileDialog(self.mygui, _("Choose a file"), '', "",
                [("CycloGraphXml(*.cgx)", "*.cgx")], glal.FD_SAVE)
            if not filepath:
                return
        if not filepath.endswith('.cgx'):
            filepath = filepath + '.cgx'
        iofile.save_file(filepath, myslope)
        page.savedfilepath = filepath
        self.tabupdate(pagenumber)
        glal.addstatusbartext(self.mygui, _("File saved"))

    def exit(self):
        """ Check if there are unsaved slopes, and cg is ready to exit
        This funciotn is called by the gui"""
        if not len(self.slopelist):
            if not self.mygui.ismaximized():
                dim = self.mygui.getdimensions()
                self.config.set("main window", "widthsize", str(dim[0]))
                self.config.set("main window", "heightsize", str(dim[1]))
            self.config.set("main window", "maximized",
                            "%s" % self.mygui.ismaximized())
            self.savepref()
            return True
        for pagenum in reversed(range(len(self.slopelist))):
            self.check_unsaved(pagenum)
        if not len(self.slopelist):
            if not self.mygui.ismaximized():
                dim = self.mygui.getdimensions()
                self.config.set("main window", "widthsize", str(dim[0]))
                self.config.set("main window", "heightsize", str(dim[1]))
            self.config.set("main window", "maximized",
                            "%s" % self.mygui.ismaximized())
            self.savepref()
            return True
        else:
            return False

    def preferences(self, event=None):
        config = configparser.ConfigParser(DEFAULT_CONF)
        config.read(get_config_file_path())

        for sec in SECTIONS:
            if not config.has_section(sec):
                config.add_section(sec)
        if config.get("global", "version") > VERSION:
            self.defaultpref()
        settings = getconfig(config)
        pref = glal.gui.preferences(self.mygui, settings, DEFAULT_CONF)
        if not pref:
            return
        self.config.set("global", "orizzontal lines", str(pref['olines']))
        self.config.set("global", "legend", str(pref['legend']))
        self.config.set("global", "theme", pref['theme'])
        self.config.set("global", "slopeinfo", str(pref['sinfo']))
        self.config.set("global", "effect-3d", str(pref['3d']))
        self.config.set("global", "latestVersionCheck", str(pref['vcheck']))
        self.config.set("global", "authorname", str(pref['aname']))
        self.config.set("global", "authormail", str(pref['amail']))
        font = pref['fdesc']
        self.config.set("font", "font-des 1", font["des"])
        self.config.set("font", "font-typ 1", font["typ"])
        self.config.set("font", "font-dim 1", str(font["dim"]))
        font = pref['ftitle']
        self.config.set("font", "font-des t", font["des"])
        self.config.set("font", "font-typ t", font["typ"])
        self.config.set("font", "font-dim t", str(font["dim"]))
        font = pref['fgrad']
        self.config.set("font", "font-des g", font["des"])
        self.config.set("font", "font-typ g", font["typ"])
        self.config.set("font", "font-dim g", str(font["dim"]))
        self.config.set("resolution", "width", pref['width'])
        self.config.set("resolution", "height", pref['height'])
        self.config.set("kml", "server", pref['serv'])
        colorlist = pref['colors']
        levellist = pref['levels']
        for i in range(0, 7):
            self.config.set("colors", "color "+str(i+1), str(colorlist[i]))
            self.config.set("levels", "level "+str(i+1), str(levellist[i]))
        self.savepref()
        self.setguiopt()


# Functions related to kml server ###
    def ser1(self, event=None):
        """Set kml server1"""
        self.config.set("kml", "server", "geonames.org")
        altitude_downloader.choose_service("geonames.org")

    def ser2(self, event=None):
        """Set kml server2"""
        self.config.set("kml", "server", "earthtools.org")
        altitude_downloader.choose_service("earthtools.org")

    def ser3(self, event=None):
        """Set kml server3"""
        self.config.set("kml", "server", "usgs.net")
        altitude_downloader.choose_service("usgs.net")

    def savepref(self, event=None):
        """Save preferences.
        Configuration file is save in the user's home
        as standardized byXDG."""
        with open(get_config_file_path(), "w") as fid:
            self.config.write(fid)

    def defaultpref(self, event=None):
        """Load default preferences: delete current config parser
        and recreate a new configparser with default values"""
        del self.config
        self.config = configparser.ConfigParser(DEFAULT_CONF)
        for sec in SECTIONS:
            if not self.config.has_section(sec):
                self.config.add_section(sec)
        self.setguiopt()

# Functions related to a single Page ###

    def close(self, *argument):  # only qt < 4.5 doesn't pass any argument
        """Close a single page of notebook."""
        if not len(self.slopelist):
            return
        if not argument:
            pagenum = self.notebook.getselpagnum()
        else:
            pagenum = self.notebook.get_pagenum(argument)
        self.check_unsaved(pagenum)

    def tabupdate(self, num, tag=''):
        """Update page.modified and title of the page tab"""
        page = self.notebook.get_page(num)
        name = self.slopelist.get_name(num)
        if tag == '':
            page.modified = False
        else:
            page.modified = True

        if name == '' or name is None:
            if page.savedfilepath is not None:
                self.notebook.set_page_label(num, tag+os.path.split(page.savedfilepath)[-1])
            else:
                self.notebook.set_page_label(num,  tag+_('slope')+" "+str(page.slopecounternew))
            return
        self.notebook.set_page_label(num,  tag+name)

    def check_unsaved(self, pagenum):
        """ Check if passed page is not saved and ask to save it"""
        page = self.notebook.get_page(pagenum)
        if page.modified:
            if page.savedfilepath is None:
                response = glal.gui.save_changes(self.mygui, _('slope') + " " +
                                                str(page.slopecounternew))
                if (response == 'SAVE'
                        and not self.save_slope_as(None, pagenum)) \
                        or response != 'DISCARD':
                    return
            else:
                filename = os.path.split(page.savedfilepath)[-1]
                response = glal.gui.save_changes(self.mygui, filename)
                if response == 'SAVE':
                    if not filename.endswith('.cgx'):
                        self.save_slope_as(None, pagenum,
                                                 filename.rsplit('.', 1)[0]+'.cgx')
                        return
                    filepath = page.savedfilepath
                    myslope = self.slopelist.get_slope_copy(pagenum)
                    iofile.save_file(filepath, myslope)
                    page.savedfilepath = filepath
                    page.modified = False
                elif not response == 'DISCARD':
                    return
        self.notebook.remove_page(pagenum)
        self.slopelist.del_slope(pagenum)
        if not len(self.slopelist):
            glal.enable_saving(self.mygui, False)

    def plot_graph(self, event=None):
        """Open the Plot windows showing a graphical image of current slope"""
        # slope_num == pagenumber == num
        num = self.notebook.getselpagnum()
        myslope = self.slopelist.get_slope_copy(num)
        if len(myslope) < 2:
            glal.gui.geterrnocp(self.mygui)
            return
        plotdata = getconfig(self.config)
        myplot = glal.gui.Plot(self.mygui, myslope,
                                plotdata, export_as_image, save_pdf, save_html)
        myplot.showplot()

    def map_slope(self, event=None):
        """Open a Map window"""
        num = self.notebook.getselpagnum()
        slope = self.slopelist.get_slope_copy(num)
        if len(slope.coords) != 0:
            (lat, lng) = slope.coords[0]
            stringc = "%s %s" % (lng, lat)
            for i in range(1, len(slope.coords)):
                (lat, lng) = slope.coords[i]
                stringc += ",%s %s" % (lng, lat)
            glal.gui.Map(self.mygui, slope.name, stringc, save_map_image)
        else:
            glal.gui.mapcoorderr(self.mygui)

    def add_cp(self, event=None):
        """Add a check point to current slope."""
        num = self.notebook.getselpagnum()
        mng = glal.gui.Managecp(self.mygui, _("Add"), "", "", "")
        if mng.show_modal():
            (dist, alt, name) = mng.getcp()
            if (dist != "") and (alt != ""):
                self.slopelist.add_cp(num, float(dist), float(alt), name)
                self.tabupdate(num, '*')
            else:
                glal.gui.managecperr(self.mygui)
        mng.destroy()

    def edit_cp(self, event=None):
        """Edit a check point to current slope."""
        num = self.notebook.getselpagnum()
        page = self.notebook.get_page(num)
        editcp = page.get_sel_row()
        myslope = self.slopelist.get_slope_copy(num)
        if editcp < 0:
            return
        (dist, alt, name) = (str(myslope.cps[editcp][0]),
                             str(myslope.cps[editcp][1]),
                             str(myslope.cps[editcp][2]))
        mng = glal.gui.Managecp(self.mygui, _("Edit"), dist, alt, name)
        if mng.show_modal():
            (dist, alt, name) = mng.getcp()
            if (dist != "") and (alt != ""):
                self.slopelist.remove_cp(num, editcp)
                self.slopelist.add_cp(num, float(dist), float(alt), name)
                page = self.notebook.get_page(num)
                self.tabupdate(num, '*')
            else:
                glal.gui.managecperr(self.mygui)
        mng.destroy()

    def delete_cp(self, event=None):
        """Delete a check point to current slope."""
        num = self.notebook.getselpagnum()
        page = self.notebook.get_page(num)
        rm_cps = page.get_sel_multi_row()
        # remove selected cps starting from the end
        # to not affect row number of non removed items
        rm_cps.reverse()
        for i in rm_cps:
            self.slopelist.remove_cp(num, i)
        self.tabupdate(num, '*')

    def info(self, event=None):
        """Show informations on current slope"""
        num = self.notebook.getselpagnum()
        # need to call calc to have the correct value of average and max gradient
        myslope = self.slopelist.get_slope_copy(num)
        myslope.calc()
        slopedata = {'name': self.slopelist.get_name(num),
                     'state': self.slopelist.get_state(num),
                     'author': self.slopelist.get_author(num),
                     'email': self.slopelist.get_email(num),
                     'comment': self.slopelist.get_comment(num),
                     'average_grad': "%.1f" % self.slopelist.get_average_grad(num)+' %',
                     'max_grad': "%.1f" % self.slopelist.get_max_grad(num)+' %',
                     'height_difference': str(self.slopelist.get_height_difference(num)) + ' m',
                     'height_gain': str(self.slopelist.get_height_gain(num)) + ' m',
                     }
        dlg = glal.gui.formslopeinfo(self.mygui, slopedata)
        if not dlg:
            return
        self.slopelist.set_name(num, dlg['name'])
        self.slopelist.set_country(num, dlg['state'])
        self.slopelist.set_author(num, dlg['author'])
        self.slopelist.set_email(num, dlg['email'])
        self.slopelist.set_comment(num, dlg['comment'])
        self.tabupdate(num, '*')

# Functions activated in response to Messages by the GUI ###

    def slope_add(self, pagenumber, row_num):
        """Callback from a SLOPE ADD message"""
        page = self.notebook.get_page(pagenumber)
        if page is None:
            return
        myslope = self.slopelist.get_slope_copy(pagenumber)
        page.insert_row(row_num, myslope.cps[row_num])

    def slope_del(self, pagenumber, row_num):
        """Callback from a SLOPE DEL message"""
        page = self.notebook.get_page(pagenumber)
        if page is None:
            return
        page.delete_row(row_num)


# Functions related to Map ###
def save_map_image(mapwindow):
    """ Save map to an image"""
    formats = [("Portable Network Graphics (*.png)", "*.png"),
               ("Bitmap (*.bmp)", "*.bmp"),
               ("Joint Photographic Experts Group (*.jpg)", "*.jpg")]
    filepath = ''
    # Control if the right extension is given by user
    while not filepath.lower().endswith(('.png', '.bmp', '.jpg')):
        if filepath:
            filepath += '.png'
        filepath = glal.FileDialog(None, _("Save map"), filepath,
                                   "", formats, glal.FD_SAVE)
        if not filepath:
            return
    mapwindow.saveimage(filepath)


# Functions related to Plot ###
def export_as_image(imgslope, settings):
    """ Save a plot to an image"""
    formats = [("Portable Network Graphics (*.png)", "*.png"),
               ("Bitmap (*.bmp)", "*.bmp"),
               ("Joint Photographic Experts Group (*.jpg)", "*.jpg"),
               ("Scalable Vector Graphics (*.svg)", "*.svg")]
    filepath = ''
    # Control if the right extension is given by user
    while not filepath.lower().endswith(('.png', '.bmp', '.jpg', '.svg')):
        if filepath:
            filepath += '.png'
        filepath = glal.FileDialog(None, _("Save plot"), filepath,
                                   "", formats, glal.FD_SAVE)
        if not filepath:
            return

    format = filepath.rsplit('.', 1)[1]
    if format.lower() == 'svg':
        image = glal.Image_svg(settings['width'], settings['height'],
                               imgslope.paint)
    else:
        image = glal.Image(settings['width'], settings['height'],
                           imgslope.paint)
    image.plot(settings)
    image.savetofile(filepath, format)


def save_pdf(imgslope, settings):
    """ Save a pdf from current plot"""
    formats = [("Portable Document Format (*.pdf)", "*.pdf")]
    filepath = ''
    # Check if the right extension is given by user
    while not filepath.lower().endswith('.pdf'):
        if filepath:
            filepath += '.pdf'
        filepath = glal.FileDialog(None, _("Save plot to pdf"), filepath,
                                   "", formats, glal.FD_SAVE)
        if not filepath:
            return

    pdf = glal.Pdf(filepath)
    pdfconfig = settings.copy()
    pdfconfig['sinfo'] = False
    pdf.plot_image(pdfconfig, 793, 600,
                   # settings['width'], settings['height'],)
                   imgslope.paint)
    pdf.addtitle(_("Slope"))
    pdf.addtext(_("Name") + ": " + imgslope.name)
    pdf.addtext(_("Country") + ": " + imgslope.country)
    pdf.addtext(_("Average gradient") + ": " + "%.1f" % imgslope.average_grad+" %")
    pdf.addtext(_("Max gradient") + ": " + "%.1f" % imgslope.max_grad+" %")
    pdf.addtext(_("Height difference") + ": " + str(imgslope.height_difference)+" m")
    pdf.addtext(_("Height gain") + ": " + str(imgslope.height_gain)+" m")
    pdf.addtitle(_("Author"))
    pdf.addtext(_("Name") + ": " + imgslope.author)
    pdf.addtext(_("E-mail") + ": " + imgslope.email)
    # TODO:use correct indentation to include notes on the pdf output
    # pdf.addtitle(_("Note"))
    # pdf.addtext(imgslope.comment)
    pdf.save()


def save_html(imgslope, settings):
    """ Save an html from current plot"""
    formats = [("HyperText Markup Language (*.html)", "*.html")]
    filepath = ''
    # Check if the right extension is given by user
    while not filepath.lower().endswith('.html'):
        if filepath:
            filepath += '.html'
        filepath = glal.FileDialog(None, _("Save plot to html"), filepath,
                                   "", formats, glal.FD_SAVE)
        if not filepath:
            return

    image = glal.Image_svg(settings['width'], settings['height'], imgslope.paint)
    image.plot(settings)

    reportpage = report_html.Report_html(imgslope.name)
    reportpage.add_image(image.svg)
    if len(imgslope.coords) != 0:
        reportpage.add_map(imgslope, settings['width'], settings['height'])
    reportpage.addtitle(_("Slope"))
    reportpage.addtext(_("Name")+": ", imgslope.name)
    reportpage.addtext(_("Country")+": ", imgslope.country)
    reportpage.addtext(_("Average gradient")+": ", "%.1f" % imgslope.average_grad+" %")
    reportpage.addtext(_("Max gradient")+": ", "%.1f" % imgslope.max_grad+" %")
    reportpage.addtext(_("Height difference")+": ", str(imgslope.height_difference)+" m")
    reportpage.addtext(_("Height gain")+": ", str(imgslope.height_gain)+" m")
    reportpage.addtitle(_("Author"))
    reportpage.addtext(_("Name")+": ", imgslope.author)
    reportpage.addtext(_("E-mail")+": ", imgslope.email)
    if imgslope.comment != '':
        reportpage.addtitle(_("Note")+": ")
        reportpage.addnote(imgslope.comment)
    reportpage.save(filepath)


def get_config_file_path():
    config_path = os.path.expanduser(os.path.normpath('~/.config/CycloGraph'))
    if 'XDG_CONFIG_HOME' in os.environ and os.path.isabs(os.environ['XDG_CONFIG_HOME']):
        config_path = os.environ['XDG_CONFIG_HOME'] + os.path.normpath('/CycloGraph')
    config_file = config_path + os.path.normpath('/CycloGraph.conf')

    # Create a directory if needed
    if not os.path.exists(config_path):
        os.makedirs(config_path)
        # migrate old configuration file if there is any
        old_file = os.path.expanduser(os.path.normpath('~/.cyclograph.cfg'))
        if os.path.exists(old_file):
            os.rename(old_file, config_file)

    return config_file


def getconfig(config):
    """ Return current settings from config"""
    settings = {
                'olines': config.getboolean("global", "orizzontal lines"),
                'legend': config.getboolean("global", "legend"),
                'theme': config.get("global", "theme"),
                'sinfo': config.getboolean("global", "slopeinfo"),
                '3d': config.getboolean("global", "effect-3d"),
                'vcheck': config.getboolean("global", "latestVersionCheck"),
                'fdesc': {
                          "des": config.get("font", "font-des 1"),
                          "typ": config.get("font", "font-typ 1"),
                          "dim": config.getint("font", "font-dim 1")
                          },
                'ftitle': {
                           "des": config.get("font", "font-des t"),
                           "typ": config.get("font", "font-typ t"),
                           "dim": config.getint("font", "font-dim t")
                           },
                'fgrad': {
                          "des": config.get("font", "font-des g"),
                          "typ": config.get("font", "font-typ g"),
                          "dim": config.getint("font", "font-dim g")
                          },
                'width': config.getint("resolution", "width"),
                'height': config.getint("resolution", "height"),
                'themelist': ThemeManager().getthemeslist(),
                'serv': config.get("kml", "server"),
                'aname': str(config.get("global", "authorname")),
                'amail': str(config.get("global", "authormail")),
                'colors': [str(config.get("colors",
                    "color "+str(i))) for i in range(1, 8)],
                'levels': [config.getfloat("levels",
                    "level "+str(i)) for i in range(1, 8)]
                }
    return settings

########################################################


def usage():
    """ Print usage on standard error"""
    sys.stderr.write("usage: %s {-q|-G|-f}\n" % sys.argv[0])
    sys.stderr.write("\t--qt --gtk3 or --file=*.cgx file for cli\n")


def opt_parser(argv):
    """ Parse console input"""
    domain = gettext.textdomain(None)
    gettext.textdomain("help")
    parser = argparse.ArgumentParser(prog="CycloGraph", description="CycloGraph "+VERSION)

    parser.set_defaults(filename="")
    parser.set_defaults(gui="cli")
    parser.add_argument("-v", "--version", action="version", version="%(prog)s "+VERSION)
    parser.add_argument("-G", "--gtk3", help="gtk3 graphical user interface", action="store_const", dest="gui", const="gtk3")
    parser.add_argument("-q", "--qt", help="qt graphical user interface", action="store_const", dest="gui", const="qt")
    parser.add_argument("-f", "--file", dest="filename", help="open cgx, csv, gpx, tcx, sal, crp or cyclomaniac file", metavar="FILE")
    parser.add_argument("-s", "--size", dest="size", action="store", help="output size widthxheight")
    parser.add_argument("-l", "--list", dest="_list", action="store_true", help="list available themes and exit")
    parser.add_argument("-o", "--options", action='append', dest='optionslist', default=[], help="add option to optionlist", choices=['3d', 'legend', 'olines', 'sinfo'])
    parser.add_argument("-t", "--theme", action="store", dest="theme", help="use selected theme")
    parser.add_argument("-e", "--extension", dest="extension", default="", help="choose default file format extension", metavar="EXT")
    options = parser.parse_args()
    if options._list:
        # print("available themes are:")
        # User asked for the available themes, so telling him is quite useless
        # also is less machine readable: silence is golden.
        for theme in ThemeManager().getthemeslist():
            print(theme)
        sys.exit(0)
    if options.theme and \
       options.theme not in ThemeManager().getthemeslist():
        parser.error('there is no theme named "%s"' % options.theme)
    gettext.textdomain(domain)
    return options

########################################################


def main_qt():
    """Main application for qt interface"""
    app = glal.initapp(sys.argv)

    arg = [str(s) for s in app.arguments()]
    options = opt_parser(arg)

    # WARNING: The return value of CycloGraph MUST be stored in a variable
    # otherwise the application could segfault.
    controller = Cyclograph()
    if options.filename:
        controller.open_slope(filepath=options.filename)
    sys.exit(app.exec_())


def main_gtk3():
    """Main application for gtk3 interface"""
    controller = Cyclograph()
    options = opt_parser(sys.argv)
    if options.filename:
        controller.open_slope(filepath=options.filename)
    glal.initapp()


def main_cli():
    """Main application for cli interface"""
    slopelist = slope.SlopeList()
    slope_num = slopelist.new_slope()
    opt = opt_parser(sys.argv)
    filein = opt.filename
    if filein == "-":
        filein = sys.stdin
    success = iofile.open_file(filein, slopelist, slope_num, opt.extension)
    if not success:
        print(('Unable to open "%s"' % filein), file=sys.stderr)
        return False
    devc = glal.DeviceContext()
    myslope = slopelist.get_slope_copy(slope_num)
    myslope.calc()

    config = configparser.ConfigParser(DEFAULT_CONF)
    config.read(get_config_file_path())

    for sec in SECTIONS:
        if not config.has_section(sec):
            config.add_section(sec)
    settings = getconfig(config)
    if opt.theme:
        settings['theme'] = opt.theme  # FIXME: this should work also with GUIs
    size_w = settings['width']
    size_h = settings['height']
    if opt.optionslist:
        if "3d" in opt.optionslist:
            settings['3d'] = True
        else:
            settings['3d'] = False
        if "legend" in opt.optionslist:
            settings['legend'] = True
        else:
            settings['legend'] = False
        if "olines" in opt.optionslist:
            settings['olines'] = True
        else:
            settings['olines'] = False
        if "sinfo" in opt.optionslist:
            settings['sinfo'] = True
        else:
            settings['sinfo'] = False
    if opt.size:
        size_el = opt.size.split('x')
        if len(size_el) == 2:
            try:
                el_w = int(size_el[0])
                el_h = int(size_el[1])
                if (el_w > 0) and (el_h > 0):
                    size_w = el_w
                    size_h = el_h
            except ValueError:
                print(('ValueError reading size "%s"' % opt.size), file=sys.stderr)
    devc.init_draw_surf((size_w, size_h), settings['3d'])
    myslope.paint(settings, devc)
    devc.end_draw()

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