"""
@package core.settings

@brief Default GUI settings

List of classes:
 - settings::Settings

Usage:
@code
from core.settings import UserSettings
@endcode

(C) 2007-2017 by the GRASS Development Team
This program is free software under the GNU General Public License
(>=v2). Read the file COPYING that comes with GRASS for details.

@author Martin Landa <landa.martin gmail.com>
@author Luca Delucchi <lucadeluge gmail.com> (language choice)
"""

from __future__ import print_function

import os
import sys
import copy

from core import globalvar
from core.gcmd import GException, GError
from core.utils import GetSettingsPath, PathJoin, rgb2str


class Settings:
    """Generic class where to store settings"""

    def __init__(self):
        # settings file
        self.filePath = os.path.join(GetSettingsPath(), 'wx')

        # key/value separator
        self.sep = ';'

        # define default settings
        self._defaultSettings()  # -> self.defaultSettings

        # read settings from the file
        self.userSettings = copy.deepcopy(self.defaultSettings)
        try:
            self.ReadSettingsFile()
        except GException as e:
            print(e.value, file=sys.stderr)

        # define internal settings
        self._internalSettings()  # -> self.internalSettings

    def _generateLocale(self):
        """Generate locales
        """
        try:
            self.locs = os.listdir(
                os.path.join(
                    os.environ['GISBASE'],
                    'locale'))
            self.locs.append('en')  # GRASS doesn't ship EN po files
            self.locs.sort()
            # Add a default choice to not override system locale
            self.locs.insert(0, 'system')
        except:
            # No NLS
            self.locs = ['system']

        return 'system'

    def _defaultSettings(self):
        """Define default settings
        """
        try:
            projFile = PathJoin(os.environ["GRASS_PROJSHARE"], 'epsg')
        except KeyError:
            projFile = ''

        id_loc = self._generateLocale()

        self.defaultSettings = {
            #
            # general
            #
            'general': {
                # use default window layout (layer manager, displays, ...)
                'defWindowPos': {
                    'enabled': True,
                    'dim' : '1,1,%d,%d,%d,1,%d,%d' % \
                    (globalvar.GM_WINDOW_SIZE[0],
                     globalvar.GM_WINDOW_SIZE[1],
                     globalvar.GM_WINDOW_SIZE[0] + 1,
                     globalvar.MAP_WINDOW_SIZE[0],
                     globalvar.MAP_WINDOW_SIZE[1])
                },
                # workspace
                'workspace': {
                    'posDisplay': {
                        'enabled': False
                    },
                    'posManager': {
                        'enabled': False
                    },
                },
                # region
                'region': {
                    'resAlign': {
                        'enabled' : False
                    },
                },
            },
            'manager': {
                # show opacity level widget
                'changeOpacityLevel': {
                    'enabled': False
                },
                # ask when removing layer from layer tree
                'askOnRemoveLayer': {
                    'enabled': True
                },
                # ask when quiting wxGUI or closing display
                'askOnQuit': {
                    'enabled': True
                },
                # hide tabs
                'hideTabs': {
                    'search': False,
                    'pyshell': False,
                },
                'copySelectedTextToClipboard': {
                    'enabled': False
                },
            },
            #
            # appearance
            #
            'appearance': {
                'outputfont': {
                    'type': 'Courier New',
                    'size': '10',
                },
                # expand/collapse element list
                'elementListExpand': {
                    'selection': 0
                },
                'menustyle': {
                    'selection': 1
                },
                'gSelectPopupHeight': {
                    'value': 200
                },
                'iconTheme': {
                    'type': 'grass'
                },
                'commandNotebook': {
                    'selection': 0 if sys.platform in ('win32', 'darwin') else 1
                },
            },
            #
            # language
            #
            'language': {
                'locale': {
                    'lc_all': id_loc
                }
            },
            #
            # display
            #
            'display': {
                'font': {
                    'type': '',
                    'encoding': 'UTF-8',
                },
                'driver': {
                    'type': 'cairo'
                },
                'alignExtent': {
                    'enabled': True
                },
                'compResolution': {
                    'enabled': False
                },
                'autoRendering': {
                    'enabled': True
                },
                'autoZooming': {
                    'enabled': False
                },
                'showCompExtent': {
                    'enabled': True
                },
                'statusbarMode': {
                    'selection': 0
                },
                'bgcolor': {
                    'color': (255, 255, 255, 255),
                },
                'mouseWheelZoom': {
                    'selection': 1,
                },
                'scrollDirection': {
                    'selection': 0,
                },
                'nvizDepthBuffer': {
                    'value': '16',
                },
            },
            #
            # projection
            #
            'projection': {
                'statusbar': {
                    'proj4': '',
                    'epsg': '',
                    'projFile': projFile,
                },
                'format': {
                    'll': 'DMS',
                    'precision': 2,
                },
            },
            #
            # Attribute Table Manager
            #
            'atm': {
                'highlight': {
                    'color': (255, 255, 0, 255),
                    'width': 2,
                    'auto': True,
                },
                'leftDbClick': {
                    'selection': 1  # draw selected
                },
                'askOnDeleteRec': {
                    'enabled': True
                },
                'keycolumn': {
                    'value': 'cat'
                },
                'encoding': {
                    'value': '',
                }
            },
            #
            # Command
            #
            'cmd': {
                'overwrite': {
                    'enabled': False
                },
                'closeDlg': {
                    'enabled': False
                },
                'verbosity': {
                    'selection': 'grassenv'
                },
                'addNewLayer': {
                    'enabled': True,
                },
                'interactiveInput': {
                    'enabled': True,
                },
            },
            #
            # d.rast
            #
            'rasterLayer': {
                'opaque': {
                    'enabled': False
                },
                'colorTable': {
                    'enabled': False,
                    'selection': 'rainbow'
                },
            },
            #
            # d.vect
            #
            'vectorLayer': {
                'featureColor': {
                    'color': (0, 0, 0),
                    'transparent': {
                        'enabled': False
                    }
                },
                'areaFillColor': {
                    'color': (200, 200, 200),
                    'transparent': {
                        'enabled': False
                    }
                },
                'line': {
                    'width': 0,
                },
                'point': {
                    'symbol': 'basic/x',
                    'size': 5,
                },
                'showType': {
                    'point': {
                        'enabled': True
                    },
                    'line': {
                        'enabled': True
                    },
                    'centroid': {
                        'enabled': False
                    },
                    'boundary': {
                        'enabled': False
                    },
                    'area': {
                        'enabled': True
                    },
                    'face': {
                        'enabled': True
                    },
                },
                'randomColors': {
                    'enabled': False,
                },
            },
            #
            # vdigit
            #
            'vdigit': {
                # symbology
                'symbol': {
                    'newSegment': {
                        'enabled': None,
                        'color': (255, 0, 0, 255)
                    },  # red
                    'newLine': {
                        'enabled': None,
                        'color': (0, 86, 45, 255)
                    },  # dark green
                    'highlight': {
                        'enabled': None,
                        'color': (255, 255, 0, 255)
                    },  # yellow
                    'highlightDupl': {
                        'enabled': None,
                        'color': (255, 72, 0, 255)
                    },  # red
                    'point': {
                        'enabled': True,
                        'color': (0, 0, 0, 255)
                    },  # black
                    'line': {
                        'enabled': True,
                        'color': (0, 0, 0, 255)
                    },  # black
                    'boundaryNo': {
                        'enabled': True,
                        'color': (126, 126, 126, 255)
                    },  # grey
                    'boundaryOne': {
                        'enabled': True,
                        'color': (0, 255, 0, 255)
                    },  # green
                    'boundaryTwo': {
                        'enabled': True,
                        'color': (255, 135, 0, 255)
                    },  # orange
                    'centroidIn': {
                        'enabled': True,
                        'color': (0, 0, 255, 255)
                    },  # blue
                    'centroidOut': {
                        'enabled': True,
                        'color': (165, 42, 42, 255)
                    },  # brown
                    'centroidDup': {
                        'enabled': True,
                        'color': (156, 62, 206, 255)
                    },  # violet
                    'nodeOne': {
                        'enabled': True,
                        'color': (255, 0, 0, 255)
                    },  # red
                    'nodeTwo': {
                        'enabled': True,
                        'color': (0, 86, 45, 255)
                    },  # dark green
                    'vertex': {
                        'enabled': False,
                        'color': (255, 20, 147, 255)
                    },  # deep pink
                    'area': {
                        'enabled': True,
                        'color': (217, 255, 217, 255)
                    },  # green
                    'direction': {
                        'enabled': False,
                        'color': (255, 0, 0, 255)
                    },  # red
                },
                # display
                'lineWidth': {
                    'value': 2,
                    'units': 'screen pixels'
                },
                # snapping
                'snapping': {
                    'value': 10,
                    'unit': 0,  # new
                    'units': 'screen pixels'  # old for backwards comp.
                },
                'snapToVertex': {
                    'enabled': True
                },
                # digitize new record
                'addRecord': {
                    'enabled': True
                },
                'layer': {
                    'value': 1
                },
                'category': {
                    'value': 1
                },
                'categoryMode': {
                    'selection': 0
                },
                # delete existing feature(s)
                'delRecord': {
                    'enabled': True
                },
                # query tool
                'query': {
                    'selection': 0,
                    'box': True
                },
                'queryLength': {
                    'than-selection': 0,
                    'thresh': 0
                },
                'queryDangle': {
                    'than-selection': 0,
                    'thresh': 0
                },
                # select feature (point, line, centroid, boundary)
                'selectType': {
                    'point': {
                        'enabled': True
                    },
                    'line': {
                        'enabled': True
                    },
                    'centroid': {
                        'enabled': True
                    },
                    'boundary': {
                        'enabled': True
                    },
                },
                'selectThresh': {
                    'value': 10,
                    'unit': 0,  # new
                    'units': 'screen pixels'  # old for backwards comp.
                },
                'checkForDupl': {
                    'enabled': False
                },
                'selectInside': {
                    'enabled': False
                },
                # exit
                'saveOnExit': {
                    'enabled': False,
                },
                # break lines on intersection
                'breakLines': {
                    'enabled': True,
                },
                # close boundary (snap to the first node)
                'closeBoundary': {
                    'enabled': False,
                }
            },
            #
            # plots for profiles, histograms, and scatterplots
            #
            'profile': {
                'raster': {
                    'pcolor': (0, 0, 255, 255),  # line color
                    'pwidth': 1,  # line width
                    'pstyle': 'solid',  # line pen style
                    'datatype': 'cell',  # raster type
                },
                'font': {
                    'titleSize': 12,
                    'axisSize': 11,
                    'legendSize': 10,
                },
                'marker': {
                    'color': (0, 0, 0, 255),
                    'fill': 'transparent',
                    'size': 2,
                    'type': 'triangle',
                    'legend': _('Segment break'),
                },
                'grid': {
                    'color': (200, 200, 200, 255),
                    'enabled': True,
                },
                'x-axis': {
                    'type': 'auto',  # axis format
                    'min': 0,  # axis min for custom axis range
                    'max': 0,  # axis max for custom axis range
                    'log': False,
                },
                'y-axis': {
                    'type': 'auto',  # axis format
                    'min': 0,  # axis min for custom axis range
                    'max': 0,  # axis max for custom axis range
                    'log': False,
                },
                'legend': {
                    'enabled': True
                },
            },
            'histogram': {
                'raster': {
                    'pcolor': (0, 0, 255, 255),  # line color
                    'pwidth': 1,  # line width
                    'pstyle': 'solid',  # line pen style
                    'datatype': 'cell',  # raster type
                },
                'font': {
                    'titleSize': 12,
                    'axisSize': 11,
                    'legendSize': 10,
                },
                'grid': {
                    'color': (200, 200, 200, 255),
                    'enabled': True,
                },
                'x-axis': {
                    'type': 'auto',  # axis format
                    'min': 0,  # axis min for custom axis range
                    'max': 0,  # axis max for custom axis range
                    'log': False,
                },
                'y-axis': {
                    'type': 'auto',  # axis format
                    'min': 0,  # axis min for custom axis range
                    'max': 0,  # axis max for custom axis range
                    'log': False,
                },
                'legend': {
                    'enabled': True
                },
            },
            'scatter': {
                'raster': {
                    'pcolor': (0, 0, 255, 255),
                    'pfill': 'solid',
                    'psize': 1,
                    'ptype': 'dot',
                    # FIXME: this is only a quick fix
                    # using also names used in a base class for compatibility
                    # probably used only for initialization
                    # base should be rewritten to not require this
                    'pwidth': 1,  # required by wxplot/base, maybe useless here
                    'pstyle': 'dot',  # line pen style
                    'plegend': _('Data point'),
                    0: {'datatype': 'CELL'},
                    1: {'datatype': 'CELL'},
                },
                'font': {
                    'titleSize': 12,
                    'axisSize': 11,
                    'legendSize': 10,
                },
                'grid': {
                    'color': (200, 200, 200, 255),
                    'enabled': True,
                },
                'x-axis': {
                    'type': 'auto',  # axis format
                    'min': 0,  # axis min for custom axis range
                    'max': 0,  # axis max for custom axis range
                    'log': False,
                },
                'y-axis': {
                    'type': 'auto',  # axis format
                    'min': 0,  # axis min for custom axis range
                    'max': 0,  # axis max for custom axis range
                    'log': False,
                },
                'legend': {
                    'enabled': True
                },
            },
            'gcpman': {
                'rms': {
                    'highestonly': True,
                    'sdfactor': 1,
                },
                'symbol': {
                    'color': (0, 0, 255, 255),
                    'hcolor': (255, 0, 0, 255),
                    'scolor': (0, 255, 0, 255),
                    'ucolor': (255, 165, 0, 255),
                    'unused': True,
                    'size': 8,
                    'width': 2,
                },
            },
            'nviz': {
                'view': {
                    'persp': {
                        'value': 20,
                        'step': 2,
                    },
                    'position': {
                        'x': 0.84,
                        'y': 0.16,
                    },
                    'twist': {
                        'value': 0,
                    },
                    'z-exag': {
                        'min': 0,
                        'max': 10,
                        'value': 1,
                    },
                    'background': {
                        'color': (255, 255, 255, 255),  # white
                    },
                },
                'fly': {
                    'exag': {
                        'move': 5,
                        'turn': 5,
                    }
                },
                'animation': {
                    'fps': 24,
                    'prefix': _("animation")
                },
                'surface': {
                    'shine': {
                        'map': False,
                        'value': 60.0,
                    },
                    'color': {
                        'map': True,
                        'value': (100, 100, 100, 255),  # constant: grey
                    },
                    'draw': {
                        'wire-color': (136, 136, 136, 255),
                        'mode': 1,  # fine
                        'style': 1,  # surface
                        'shading': 1,  # gouraud
                        'res-fine': 6,
                        'res-coarse': 9,
                    },
                    'position': {
                        'x': 0,
                        'y': 0,
                        'z': 0,
                    },
                },
                'constant': {
                    'color': (100, 100, 100, 255),
                    'value': 0.0,
                    'transp': 0,
                    'resolution': 6
                },
                'vector': {
                    'lines': {
                        'show': False,
                        'width': 2,
                        'color': (0, 0, 0, 255),
                        'flat': False,
                        'height': 0,
                        'rgbcolumn': None,
                        'sizecolumn': None,
                    },
                    'points': {
                        'show': False,
                        'size': 100,
                        'autosize': True,
                        'width': 2,
                        'marker': 2,
                        'color': (0, 0, 0, 255),
                        'height': 0,
                        'rgbcolumn': None,
                        'sizecolumn': None,
                    }
                },
                'volume': {
                    'color': {
                        'map': True,
                        'value': (100, 100, 100, 255),  # constant: grey
                    },
                    'draw': {
                        'mode': 0,  # isosurfaces
                        'shading': 1,  # gouraud
                        'resolution': 3,  # polygon resolution
                        'box': False  # draw wire box
                    },
                    'shine': {
                        'map': False,
                        'value': 60,
                    },
                    'topo': {
                        'map': None,
                        'value': 0.0
                    },
                    'transp': {
                        'map': None,
                        'value': 0
                    },
                    'mask': {
                        'map': None,
                        'value': ''
                    },
                    'slice_position': {
                        'x1': 0,
                        'x2': 1,
                        'y1': 0,
                        'y2': 1,
                        'z1': 0,
                        'z2': 1,
                        'axis': 0,
                    }
                },
                'cplane': {
                    'shading': 4,
                    'rotation': {
                        'rot': 180,
                        'tilt': 0
                    },
                    'position': {
                        'x': 0,
                        'y': 0,
                        'z': 0
                    }
                },
                'light': {
                    'position': {
                        'x': 0.68,
                        'y': -0.68,
                        'z': 80,
                    },
                    'bright': 80,
                    'color': (255, 255, 255, 255),  # white
                    'ambient': 20,
                },
                'fringe': {
                    'elev': 55,
                    'color': (128, 128, 128, 255),  # grey
                },
                'arrow': {
                    'color': (0, 0, 0),
                },
                'scalebar': {
                    'color': (0, 0, 0),
                }
            },
            'modeler': {
                'disabled': {
                    'color': (211, 211, 211, 255),  # light grey
                },
                'action': {
                    'color': {
                        'valid': (180, 234, 154, 255),  # light green
                        'invalid': (255, 255, 255, 255),  # white
                        'running': (255, 0, 0, 255),     # red
                    },
                    'size': {
                        'width': 125,
                        'height': 50,
                    },
                    'width': {
                        'parameterized': 2,
                        'default': 1,
                    },
                },
                'data': {
                    'color': {
                        'raster': (215, 215, 248, 255),  # light blue
                        'raster3d': (215, 248, 215, 255),  # light green
                        'vector': (248, 215, 215, 255),  # light red
                        'dbtable': (255, 253, 194, 255),  # light yellow
                    },
                    'size': {
                        'width': 175,
                        'height': 50,
                    },
                },
                'loop': {
                    'color': {
                        'valid': (234, 226, 154, 255),  # dark yellow
                    },
                    'size': {
                        'width': 175,
                        'height': 40,
                    },
                },
                'if-else': {
                    'size': {
                        'width': 150,
                        'height': 40,
                    },
                },
                'comment': {
                    'color': (255, 233, 208, 255),  # light yellow
                    'size': {
                        'width': 200,
                        'height': 100,
                    },
                },
            },
            'mapswipe': {
                'cursor': {
                    'color': (0, 0, 0, 255),
                    'size': 12,
                    'width': 1,
                    'type': {
                        'selection': 0,
                    }
                },
            },
            'animation': {
                'bgcolor': {
                    'color': (255, 255, 255, 255),
                },
                'nprocs': {
                    'value': -1,
                },
                'font': {
                    'bgcolor': (255, 255, 255, 255),
                    'fgcolor': (0, 0, 0, 255),
                },
                'temporal': {
                    'format': '%Y-%m-%d %H:%M:%S',
                    'nodata': {
                        'enable': False
                    },
                },
            },
        }

        # quick fix, http://trac.osgeo.org/grass/ticket/1233
        # TODO
        if sys.platform == 'darwin':
            self.defaultSettings['general']['defWindowPos']['enabled'] = False

    def _internalSettings(self):
        """Define internal settings (based on user settings)
        """
        self.internalSettings = {}
        for group in list(self.userSettings.keys()):
            self.internalSettings[group] = {}
            for key in list(self.userSettings[group].keys()):
                self.internalSettings[group][key] = {}

        # self.internalSettings['general']["mapsetPath"]['value'] = self.GetMapsetPath()
        self.internalSettings['appearance']['elementListExpand']['choices'] = \
            (_("Collapse all except PERMANENT and current"),
             _("Collapse all except PERMANENT"),
             _("Collapse all except current"),
             _("Collapse all"),
             _("Expand all"))

        self.internalSettings['language'][
            'locale']['choices'] = tuple(self.locs)
        self.internalSettings['atm']['leftDbClick']['choices'] = (
            _('Edit selected record'), _('Display selected'))

        self.internalSettings['cmd']['verbosity']['choices'] = ('grassenv',
                                                                'verbose',
                                                                'quiet')

        self.internalSettings['appearance'][
            'iconTheme']['choices'] = ('grass',)
        self.internalSettings['appearance']['menustyle']['choices'] = \
            (_("Classic (labels only)"),
             _("Combined (labels and module names)"),
             _("Expert (module names only)"))
        self.internalSettings['appearance']['gSelectPopupHeight']['min'] = 50
        # there is also maxHeight given to TreeCtrlComboPopup.GetAdjustedSize
        self.internalSettings['appearance']['gSelectPopupHeight']['max'] = 1000
        self.internalSettings['appearance']['commandNotebook']['choices'] = \
            (_("Basic top"),
             _("Basic left"),
             _("Fancy green"),
             _("List left"))

        self.internalSettings['display'][
            'driver']['choices'] = ['cairo', 'png']
        self.internalSettings['display']['statusbarMode'][
            'choices'] = None  # set during MapFrame init
        self.internalSettings['display']['mouseWheelZoom']['choices'] = (
            _('Zoom and recenter'), _('Zoom to mouse cursor'), _('Nothing'))
        self.internalSettings['display']['scrollDirection']['choices'] = (
            _('Scroll forward to zoom in'), _('Scroll back to zoom in'))

        self.internalSettings['nviz']['view'] = {}
        self.internalSettings['nviz']['view']['twist'] = {}
        self.internalSettings['nviz']['view']['twist']['min'] = -180
        self.internalSettings['nviz']['view']['twist']['max'] = 180
        self.internalSettings['nviz']['view']['persp'] = {}
        self.internalSettings['nviz']['view']['persp']['min'] = 1
        self.internalSettings['nviz']['view']['persp']['max'] = 100
        self.internalSettings['nviz']['view']['height'] = {}
        self.internalSettings['nviz']['view']['height']['value'] = -1
        self.internalSettings['nviz']['view']['z-exag'] = {}
        self.internalSettings['nviz']['view']['z-exag']['llRatio'] = 1
        self.internalSettings['nviz']['view']['rotation'] = None
        self.internalSettings['nviz']['view']['focus'] = {}
        self.internalSettings['nviz']['view']['focus']['x'] = -1
        self.internalSettings['nviz']['view']['focus']['y'] = -1
        self.internalSettings['nviz']['view']['focus']['z'] = -1
        self.internalSettings['nviz']['view']['dir'] = {}
        self.internalSettings['nviz']['view']['dir']['x'] = -1
        self.internalSettings['nviz']['view']['dir']['y'] = -1
        self.internalSettings['nviz']['view']['dir']['z'] = -1
        self.internalSettings['nviz']['view']['dir']['use'] = False

        for decor in ('arrow', 'scalebar'):
            self.internalSettings['nviz'][decor] = {}
            self.internalSettings['nviz'][decor]['position'] = {}
            self.internalSettings['nviz'][decor]['position']['x'] = 0
            self.internalSettings['nviz'][decor]['position']['y'] = 0
            self.internalSettings['nviz'][decor]['size'] = 100
        self.internalSettings['nviz']['vector'] = {}
        self.internalSettings['nviz']['vector']['points'] = {}
        self.internalSettings['nviz']['vector']['points']['marker'] = ("x",
                                                                       _("box"),
                                                                       _("sphere"),
                                                                       _("cube"),
                                                                       _("diamond"),
                                                                       _("aster"),
                                                                       _("gyro"),
                                                                       _("histogram"))
        self.internalSettings['vdigit']['bgmap'] = {}
        self.internalSettings['vdigit']['bgmap']['value'] = ''

        self.internalSettings['mapswipe']['cursor']['type'] = {}
        self.internalSettings['mapswipe']['cursor']['type'][
            'choices'] = (_("cross"), _("box"), _("circle"))

    def ReadSettingsFile(self, settings=None):
        """Reads settings file (mapset, location, gisdbase)"""
        if settings is None:
            settings = self.userSettings

        self._readFile(self.filePath, settings)

        # set environment variables
        font = self.Get(group='display', key='font', subkey='type')
        enc = self.Get(group='display', key='font', subkey='encoding')
        if font:
            os.environ["GRASS_FONT"] = font
        if enc:
            os.environ["GRASS_ENCODING"] = enc

    def _readFile(self, filename, settings=None):
        """Read settings from file to dict

        :param filename: settings file path
        :param settings: dict where to store settings (None for self.userSettings)
        """
        if settings is None:
            settings = self.userSettings

        if not os.path.exists(filename):
            return

        try:
            fd = open(filename, "r")
        except IOError:
            sys.stderr.write(
                _("Unable to read settings file <%s>\n") %
                filename)
            return

        try:
            line = ''
            for line in fd.readlines():
                line = line.rstrip('%s' % os.linesep)
                group, key = line.split(self.sep)[0:2]
                kv = line.split(self.sep)[2:]
                subkeyMaster = None
                if len(kv) % 2 != 0:  # multiple (e.g. nviz)
                    subkeyMaster = kv[0]
                    del kv[0]
                idx = 0
                while idx < len(kv):
                    if subkeyMaster:
                        subkey = [subkeyMaster, kv[idx]]
                    else:
                        subkey = kv[idx]
                    value = kv[idx + 1]
                    value = self._parseValue(value, read=True)
                    self.Append(settings, group, key, subkey, value)
                    idx += 2
        except ValueError as e:
            print(_(
                "Error: Reading settings from file <%(file)s> failed.\n"
                "\t\tDetails: %(detail)s\n"
                "\t\tLine: '%(line)s'\n") % {
                'file': filename, 'detail': e, 'line': line},
                file=sys.stderr)
            fd.close()

        fd.close()

    def SaveToFile(self, settings=None):
        """Save settings to the file"""
        if settings is None:
            settings = self.userSettings

        dirPath = GetSettingsPath()
        if not os.path.exists(dirPath):
            try:
                os.mkdir(dirPath)
            except:
                GError(_('Unable to create settings directory'))
                return

        try:
            file = open(self.filePath, "w")
            for group in list(settings.keys()):
                for key in list(settings[group].keys()):
                    subkeys = list(settings[group][key].keys())
                    file.write('%s%s%s%s' % (group, self.sep, key, self.sep))
                    for idx in range(len(subkeys)):
                        value = settings[group][key][subkeys[idx]]
                        if isinstance(value, dict):
                            if idx > 0:
                                file.write(
                                    '%s%s%s%s%s' %
                                    (os.linesep, group, self.sep, key, self.sep))
                            file.write('%s%s' % (subkeys[idx], self.sep))
                            kvalues = list(settings[group][key][subkeys[idx]].keys())
                            srange = range(len(kvalues))
                            for sidx in srange:
                                svalue = self._parseValue(
                                    settings[group][key][
                                        subkeys[idx]][
                                        kvalues[sidx]])
                                file.write('%s%s%s' % (kvalues[sidx], self.sep,
                                                       svalue))
                                if sidx < len(kvalues) - 1:
                                    file.write('%s' % self.sep)
                        else:
                            if idx > 0 and isinstance(
                                    settings[group][key][subkeys[idx - 1]],
                                    dict):
                                file.write(
                                    '%s%s%s%s%s' %
                                    (os.linesep, group, self.sep, key, self.sep))
                            value = self._parseValue(
                                settings[group][key][subkeys[idx]])
                            file.write(
                                '%s%s%s' %
                                (subkeys[idx], self.sep, value))
                            if idx < len(subkeys) - 1 and not isinstance(
                                    settings[group][key][subkeys[idx + 1]],
                                    dict):
                                file.write('%s' % self.sep)
                    file.write(os.linesep)
        except IOError as e:
            raise GException(e)
        except Exception as e:
            raise GException(_('Writing settings to file <%(file)s> failed.'
                               '\n\nDetails: %(detail)s') %
                             {'file': self.filePath, 'detail': e})
        file.close()
        return self.filePath

    def _parseValue(self, value, read=False):
        """Parse value to be store in settings file"""
        if read:  # -> read settings (cast values)
            if value == 'True':
                value = True
            elif value == 'False':
                value = False
            elif value == 'None':
                value = None
            elif ':' in value:  # -> color
                try:
                    value = tuple(map(int, value.split(':')))
                except ValueError:  # -> string
                    pass
            else:
                try:
                    value = int(value)
                except ValueError:
                    try:
                        value = float(value)
                    except ValueError:
                        pass
        else:  # -> write settings
            if isinstance(value, type(())):  # -> color
                value = str(value[0]) + ':' +\
                    str(value[1]) + ':' + \
                    str(value[2])

        return value

    def Get(self, group, key=None, subkey=None, settings_type='user'):
        """Get value by key/subkey

        Raise KeyError if key is not found

        :param group: settings group
        :param key: (value, None)
        :param subkey: (value, list or None)
        :param settings_type: 'user', 'internal', 'default'

        :return: value
        """

        if settings_type == 'user':
            settings = self.userSettings
        elif settings_type == 'internal':
            settings = self.internalSettings
        else:
            settings = self.defaultSettings

        try:
            if subkey is None:
                if key is None:
                    return settings[group]
                else:
                    return settings[group][key]
            else:
                if isinstance(subkey, type(tuple())) or \
                        isinstance(subkey, type(list())):
                    return settings[group][key][subkey[0]][subkey[1]]
                else:
                    return settings[group][key][subkey]

        except KeyError:
            print("Settings: unable to get value '%s:%s:%s'\n" % (
                group, key, subkey), file=sys.stderr)

    def Set(self, group, value, key=None, subkey=None, settings_type='user'):
        """Set value of key/subkey

        Raise KeyError if group/key is not found

        :param group: settings group
        :param key: key (value, None)
        :param subkey: subkey (value, list or None)
        :param value: value
        :param settings_type: 'user', 'internal', 'default'
        """

        if settings_type == 'user':
            settings = self.userSettings
        elif settings_type == 'internal':
            settings = self.internalSettings
        else:
            settings = self.defaultSettings

        try:
            if subkey is None:
                if key is None:
                    settings[group] = value
                else:
                    settings[group][key] = value
            else:
                if isinstance(subkey, type(tuple())) or \
                        isinstance(subkey, type(list())):
                    settings[group][key][subkey[0]][subkey[1]] = value
                else:
                    settings[group][key][subkey] = value
        except KeyError:
            raise GException("%s '%s:%s:%s'" %
                             (_("Unable to set "), group, key, subkey))

    def Append(self, dict, group, key, subkey, value, overwrite=True):
        """Set value of key/subkey

        Create group/key/subkey if not exists

        :param dict: settings dictionary to use
        :param group: settings group
        :param key: key
        :param subkey: subkey (value or list)
        :param value: value
        :param overwrite: True to overwrite existing value
        """

        hasValue = True
        if group not in dict:
            dict[group] = {}
            hasValue = False

        if key not in dict[group]:
            dict[group][key] = {}
            hasValue = False

        if isinstance(subkey, list):
            # TODO: len(subkey) > 2
            if subkey[0] not in dict[group][key]:
                dict[group][key][subkey[0]] = {}
                hasValue = False
            if subkey[1] not in dict[group][key][subkey[0]]:
                hasValue = False

            try:
                if overwrite or (not overwrite and not hasValue):
                    dict[group][key][subkey[0]][subkey[1]] = value
            except TypeError:
                print(_("Unable to parse settings '%s'") % value +
                      ' (' + group + ':' + key + ':' + subkey[0] +
                      ':' + subkey[1] + ')', file=sys.stderr)
        else:
            if subkey not in dict[group][key]:
                hasValue = False

            try:
                if overwrite or (not overwrite and not hasValue):
                    dict[group][key][subkey] = value
            except TypeError:
                print(_("Unable to parse settings '%s'") % value +
                      ' (' + group + ':' + key + ':' + subkey + ')',
                      file=sys.stderr)

    def GetDefaultSettings(self):
        """Get default user settings"""
        return self.defaultSettings

    def Reset(self, key=None):
        """Reset to default settings

        :param key: key in settings dict (None for all keys)
        """
        if not key:
            self.userSettings = copy.deepcopy(self.defaultSettings)
        else:
            self.userSettings[key] = copy.deepcopy(self.defaultSettings[key])

UserSettings = Settings()


def GetDisplayVectSettings():
    settings = list()
    if not UserSettings.Get(
            group='vectorLayer', key='featureColor',
            subkey=['transparent', 'enabled']):
        featureColor = UserSettings.Get(
            group='vectorLayer',
            key='featureColor',
            subkey='color')
        settings.append('color=%s' % rgb2str.get(
            featureColor, ':'.join(map(str, featureColor))))
    else:
        settings.append('color=none')
    if not UserSettings.Get(
            group='vectorLayer', key='areaFillColor',
            subkey=['transparent', 'enabled']):
        fillColor = UserSettings.Get(
            group='vectorLayer',
            key='areaFillColor',
            subkey='color')
        settings.append('fcolor=%s' %
                        rgb2str.get(fillColor, ':'.join(map(str, fillColor))))
    else:
        settings.append('fcolor=none')

    settings.append(
        'width=%s' %
        UserSettings.Get(
            group='vectorLayer',
            key='line',
            subkey='width'))
    settings.append(
        'icon=%s' %
        UserSettings.Get(
            group='vectorLayer',
            key='point',
            subkey='symbol'))
    settings.append(
        'size=%s' %
        UserSettings.Get(
            group='vectorLayer',
            key='point',
            subkey='size'))
    types = []
    for ftype in ['point', 'line', 'boundary', 'centroid', 'area', 'face']:
        if UserSettings.Get(group='vectorLayer',
                            key='showType', subkey=[ftype, 'enabled']):
            types.append(ftype)
    settings.append('type=%s' % ','.join(types))

    if UserSettings.Get(group='vectorLayer',
                        key='randomColors', subkey='enabled'):
        settings.append('-c')

    return settings
