"""
@package animation.dialogs

@brief Dialogs for animation management, changing speed of animation

Classes:
 - dialogs::SpeedDialog
 - dialogs::InputDialog
 - dialogs::EditDialog
 - dialogs::ExportDialog
 - dialogs::AnimSimpleLayerManager
 - dialogs::AddTemporalLayerDialog


(C) 2013 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 Anna Petrasova <kratochanna gmail.com>
"""
import os
import wx
import copy
import datetime
import wx.lib.filebrowsebutton as filebrowse
import wx.lib.scrolledpanel as SP
import wx.lib.colourselect as csel

from core.gcmd import GMessage, GError, GException
from core import globalvar
from gui_core.dialogs import MapLayersDialog, GetImageHandlers
from gui_core.preferences import PreferencesBaseDialog
from gui_core.forms import GUI
from core.settings import UserSettings
from core.utils import _
from gui_core.gselect import Select
from gui_core.widgets import FloatValidator
from gui_core.wrap import SpinCtrl

from animation.utils import TemporalMode, getRegisteredMaps, getNameAndLayer, getCpuCount
from animation.data import AnimationData, AnimLayer
from animation.toolbars import AnimSimpleLmgrToolbar, SIMPLE_LMGR_STDS
from gui_core.simplelmgr import SimpleLayerManager, \
    SIMPLE_LMGR_RASTER, SIMPLE_LMGR_VECTOR, SIMPLE_LMGR_TB_TOP

from grass.pydispatch.signal import Signal
import grass.script.core as gcore


class SpeedDialog(wx.Dialog):

    def __init__(self, parent, title=_("Adjust speed of animation"),
                 temporalMode=None, minimumDuration=0, timeGranularity=None,
                 initialSpeed=200):
        wx.Dialog.__init__(self, parent=parent, id=wx.ID_ANY, title=title,
                           style=wx.DEFAULT_DIALOG_STYLE)
        # signal emitted when speed has changed; has attribute 'ms'
        self.speedChanged = Signal('SpeedDialog.speedChanged')
        self.minimumDuration = minimumDuration
        # self.framesCount = framesCount
        self.defaultSpeed = initialSpeed
        self.lastAppliedValue = self.defaultSpeed
        self.lastAppliedValueTemp = self.defaultSpeed

        self._layout()

        self.temporalMode = temporalMode
        self.timeGranularity = timeGranularity

        self._fillUnitChoice(self.choiceUnits)
        self.InitTimeSpin(self.defaultSpeed)

    def SetTimeGranularity(self, gran):
        self._timeGranularity = gran

    def GetTimeGranularity(self):
        return self._timeGranularity

    timeGranularity = property(
        fset=SetTimeGranularity,
        fget=GetTimeGranularity)

    def SetTemporalMode(self, mode):
        self._temporalMode = mode
        self._setTemporalMode()

    def GetTemporalMode(self):
        return self._temporalMode

    temporalMode = property(fset=SetTemporalMode, fget=GetTemporalMode)

    def _layout(self):
        """Layout window"""
        mainSizer = wx.BoxSizer(wx.VERTICAL)
        #
        # simple mode
        #
        self.nontemporalBox = wx.StaticBox(parent=self, id=wx.ID_ANY,
                                           label=' %s ' % _("Simple mode"))
        box = wx.StaticBoxSizer(self.nontemporalBox, wx.VERTICAL)
        gridSizer = wx.GridBagSizer(hgap=5, vgap=5)

        labelDuration = wx.StaticText(
            self, id=wx.ID_ANY, label=_("Frame duration:"))
        labelUnits = wx.StaticText(self, id=wx.ID_ANY, label=_("ms"))
        self.spinDuration = SpinCtrl(
            self,
            id=wx.ID_ANY,
            min=self.minimumDuration,
            max=10000,
            initial=self.defaultSpeed)
        # TODO total time

        gridSizer.Add(
            labelDuration, pos=(0, 0),
            flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT)
        gridSizer.Add(self.spinDuration, pos=(0, 1), flag=wx.ALIGN_CENTER)
        gridSizer.Add(
            labelUnits, pos=(0, 2),
            flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT)
        gridSizer.AddGrowableCol(0)

        box.Add(
            gridSizer,
            proportion=1,
            border=5,
            flag=wx.ALL | wx.EXPAND)
        self.nontemporalSizer = gridSizer
        mainSizer.Add(
            box,
            proportion=0,
            flag=wx.EXPAND | wx.ALL,
            border=5)
        #
        # temporal mode
        #
        self.temporalBox = wx.StaticBox(parent=self, id=wx.ID_ANY,
                                        label=' %s ' % _("Temporal mode"))
        box = wx.StaticBoxSizer(self.temporalBox, wx.VERTICAL)
        gridSizer = wx.GridBagSizer(hgap=5, vgap=5)

        labelTimeUnit = wx.StaticText(
            self, id=wx.ID_ANY, label=_("Time unit:"))
        labelDuration = wx.StaticText(
            self, id=wx.ID_ANY, label=_("Duration of time unit:"))
        labelUnits = wx.StaticText(self, id=wx.ID_ANY, label=_("ms"))
        self.spinDurationTemp = SpinCtrl(
            self, id=wx.ID_ANY, min=self.minimumDuration, max=10000,
            initial=self.defaultSpeed)
        self.choiceUnits = wx.Choice(self, id=wx.ID_ANY)

        # TODO total time

        gridSizer.Add(
            labelTimeUnit, pos=(0, 0),
            flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT)
        gridSizer.Add(self.choiceUnits, pos=(0, 1),
                      flag=wx.ALIGN_CENTER | wx.EXPAND)
        gridSizer.Add(
            labelDuration, pos=(1, 0),
            flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT)
        gridSizer.Add(
            self.spinDurationTemp, pos=(
                1, 1), flag=wx.ALIGN_CENTER | wx.EXPAND)
        gridSizer.Add(
            labelUnits, pos=(1, 2),
            flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT)
        gridSizer.AddGrowableCol(1)

        self.temporalSizer = gridSizer
        box.Add(
            gridSizer,
            proportion=1,
            border=5,
            flag=wx.ALL | wx.EXPAND)
        mainSizer.Add(
            box,
            proportion=0,
            flag=wx.EXPAND | wx.ALL,
            border=5)

        self.btnOk = wx.Button(self, wx.ID_OK)
        self.btnApply = wx.Button(self, wx.ID_APPLY)
        self.btnCancel = wx.Button(self, wx.ID_CANCEL)
        self.btnOk.SetDefault()

        self.btnOk.Bind(wx.EVT_BUTTON, self.OnOk)
        self.btnApply.Bind(wx.EVT_BUTTON, self.OnApply)
        self.btnCancel.Bind(wx.EVT_BUTTON, self.OnCancel)
        self.Bind(wx.EVT_CLOSE, self.OnCancel)
        # button sizer
        btnStdSizer = wx.StdDialogButtonSizer()
        btnStdSizer.AddButton(self.btnOk)
        btnStdSizer.AddButton(self.btnApply)
        btnStdSizer.AddButton(self.btnCancel)
        btnStdSizer.Realize()

        mainSizer.Add(btnStdSizer, proportion=0,
                      flag=wx.EXPAND | wx.ALL | wx.ALIGN_RIGHT, border=5)

        self.SetSizer(mainSizer)
        mainSizer.Fit(self)

    def _setTemporalMode(self):
        self.nontemporalBox.Enable(
            self.temporalMode == TemporalMode.NONTEMPORAL)
        self.temporalBox.Enable(self.temporalMode == TemporalMode.TEMPORAL)
        for child in self.temporalSizer.GetChildren():
            child.GetWindow().Enable(self.temporalMode == TemporalMode.TEMPORAL)
        for child in self.nontemporalSizer.GetChildren():
            child.GetWindow().Enable(self.temporalMode == TemporalMode.NONTEMPORAL)

        self.Layout()

    def _fillUnitChoice(self, choiceWidget):
        timeUnitsChoice = [
            _("year"),
            _("month"),
            _("day"),
            _("hour"),
            _("minute"),
            _("second")]
        timeUnits = ["years", "months", "days", "hours", "minutes", "seconds"]
        for item, cdata in zip(timeUnitsChoice, timeUnits):
            choiceWidget.Append(item, cdata)

        if self.temporalMode == TemporalMode.TEMPORAL:
            unit = self.timeGranularity[1]
            index = 0
            for i, timeUnit in enumerate(timeUnits):
                if timeUnit.startswith(unit):
                    index = i
                    break
            choiceWidget.SetSelection(index)
        else:
            choiceWidget.SetSelection(0)

    def OnOk(self, event):
        self._apply()
        self.OnCancel(None)

    def OnApply(self, event):
        self._apply()

    def OnCancel(self, event):
        self.spinDuration.SetValue(self.lastAppliedValue)
        self.spinDurationTemp.SetValue(self.lastAppliedValueTemp)
        self.Hide()

    def InitTimeSpin(self, timeTick):
        if self.temporalMode == TemporalMode.TEMPORAL:
            index = self.choiceUnits.GetSelection()
            unit = self.choiceUnits.GetClientData(index)
            delta = self._timedelta(unit=unit, number=1)
            seconds1 = self._total_seconds(delta)

            number, unit = self.timeGranularity
            number = float(number)
            delta = self._timedelta(unit=unit, number=number)
            seconds2 = self._total_seconds(delta)
            value = timeTick
            ms = value * seconds1 / float(seconds2)
            self.spinDurationTemp.SetValue(ms)
        else:
            self.spinDuration.SetValue(timeTick)

    def _apply(self):
        if self.temporalMode == TemporalMode.NONTEMPORAL:
            ms = self.spinDuration.GetValue()
            self.lastAppliedValue = self.spinDuration.GetValue()
        elif self.temporalMode == TemporalMode.TEMPORAL:
            index = self.choiceUnits.GetSelection()
            unit = self.choiceUnits.GetClientData(index)
            delta = self._timedelta(unit=unit, number=1)
            seconds1 = self._total_seconds(delta)

            number, unit = self.timeGranularity
            number = float(number)
            delta = self._timedelta(unit=unit, number=number)
            seconds2 = self._total_seconds(delta)

            value = self.spinDurationTemp.GetValue()
            ms = value * seconds2 / float(seconds1)
            # minimumDuration set to 0, too restrictive
            if ms < self.minimumDuration:
                GMessage(parent=self, message=_(
                    "Animation speed is too high."))
                return
            self.lastAppliedValueTemp = self.spinDurationTemp.GetValue()
        else:
            return

        self.speedChanged.emit(ms=ms)

    def _timedelta(self, unit, number):
        if unit in "years":
            delta = datetime.timedelta(days=365.25 * number)
        elif unit in "months":
            delta = datetime.timedelta(days=30.4375 * number)  # 365.25/12
        elif unit in "days":
            delta = datetime.timedelta(days=1 * number)
        elif unit in "hours":
            delta = datetime.timedelta(hours=1 * number)
        elif unit in "minutes":
            delta = datetime.timedelta(minutes=1 * number)
        elif unit in "seconds":
            delta = datetime.timedelta(seconds=1 * number)

        return delta

    def _total_seconds(self, delta):
        """timedelta.total_seconds is new in version 2.7.
        """
        return delta.seconds + delta.days * 24 * 3600


class InputDialog(wx.Dialog):

    def __init__(self, parent, mode, animationData):
        wx.Dialog.__init__(self, parent=parent, id=wx.ID_ANY,
                           style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER)
        if mode == 'add':
            self.SetTitle(_("Add new animation"))
        elif mode == 'edit':
            self.SetTitle(_("Edit animation"))

        self.animationData = animationData
        self._tmpLegendCmd = None

        self._layout()
        self.OnViewMode(event=None)

    def _layout(self):
        self.notebook = wx.Notebook(parent=self, style=wx.BK_DEFAULT)
        sizer = wx.BoxSizer(wx.VERTICAL)
        self.notebook.AddPage(
            self._createGeneralPage(
                self.notebook),
            _("General"))
        self.notebook.AddPage(
            self._createAdvancedPage(
                self.notebook),
            _("Advanced"))
        sizer.Add(
            self.notebook,
            proportion=1,
            flag=wx.ALL | wx.EXPAND,
            border=3)

        # buttons
        self.btnOk = wx.Button(self, wx.ID_OK)
        self.btnCancel = wx.Button(self, wx.ID_CANCEL)
        self.btnOk.SetDefault()
        self.btnOk.Bind(wx.EVT_BUTTON, self.OnOk)
        # button sizer
        btnStdSizer = wx.StdDialogButtonSizer()
        btnStdSizer.AddButton(self.btnOk)
        btnStdSizer.AddButton(self.btnCancel)
        btnStdSizer.Realize()

        sizer.Add(btnStdSizer, proportion=0,
                  flag=wx.EXPAND | wx.ALL | wx.ALIGN_RIGHT, border=5)
        self.SetSizer(sizer)
        sizer.Fit(self)

    def _createGeneralPage(self, parent):
        panel = wx.Panel(parent=parent)
        mainSizer = wx.BoxSizer(wx.VERTICAL)

        self.windowChoice = wx.Choice(
            panel,
            id=wx.ID_ANY,
            choices=[
                _("top left"),
                _("top right"),
                _("bottom left"),
                _("bottom right")])
        self.windowChoice.SetSelection(self.animationData.windowIndex)

        self.nameCtrl = wx.TextCtrl(
            panel, id=wx.ID_ANY, value=self.animationData.name)

        self.nDChoice = wx.Choice(panel, id=wx.ID_ANY)
        mode = self.animationData.viewMode
        index = 0
        for i, (viewMode, viewModeName) in enumerate(
                self.animationData.viewModes):
            self.nDChoice.Append(viewModeName, clientData=viewMode)
            if mode == viewMode:
                index = i

        self.nDChoice.SetSelection(index)
        self.nDChoice.SetToolTipString(_("Select 2D or 3D view"))
        self.nDChoice.Bind(wx.EVT_CHOICE, self.OnViewMode)

        gridSizer = wx.FlexGridSizer(cols=2, hgap=5, vgap=5)
        gridSizer.Add(
            wx.StaticText(
                panel,
                id=wx.ID_ANY,
                label=_("Name:")),
            flag=wx.ALIGN_CENTER_VERTICAL)
        gridSizer.Add(self.nameCtrl, proportion=1, flag=wx.EXPAND)
        gridSizer.Add(
            wx.StaticText(
                panel,
                id=wx.ID_ANY,
                label=_("Window position:")),
            flag=wx.ALIGN_CENTER_VERTICAL)
        gridSizer.Add(
            self.windowChoice,
            proportion=1,
            flag=wx.ALIGN_RIGHT)
        gridSizer.Add(
            wx.StaticText(
                panel,
                id=wx.ID_ANY,
                label=_("View mode:")),
            flag=wx.ALIGN_CENTER_VERTICAL)
        gridSizer.Add(self.nDChoice, proportion=1, flag=wx.ALIGN_RIGHT)
        gridSizer.AddGrowableCol(0, 1)
        gridSizer.AddGrowableCol(1, 1)
        mainSizer.Add(
            gridSizer,
            proportion=0,
            flag=wx.ALL | wx.EXPAND,
            border=5)
        label = _(
            "For 3D animation, please select only one space-time dataset\n"
            "or one series of map layers.")
        self.warning3DLayers = wx.StaticText(panel, label=label)
        self.warning3DLayers.SetForegroundColour(
            wx.SystemSettings.GetColour(wx.SYS_COLOUR_GRAYTEXT))
        mainSizer.Add(
            self.warning3DLayers,
            proportion=0,
            flag=wx.EXPAND | wx.LEFT,
            border=5)

        self.dataPanel = self._createDataPanel(panel)
        self.threeDPanel = self._create3DPanel(panel)
        mainSizer.Add(
            self.dataPanel,
            proportion=1,
            flag=wx.EXPAND | wx.ALL,
            border=3)
        mainSizer.Add(
            self.threeDPanel,
            proportion=0,
            flag=wx.EXPAND | wx.ALL,
            border=3)

        panel.SetSizer(mainSizer)
        mainSizer.Fit(panel)

        return panel

    def _createDataPanel(self, parent):
        panel = wx.Panel(parent)
        slmgrSizer = wx.BoxSizer(wx.VERTICAL)
        self._layerList = copy.deepcopy(self.animationData.layerList)
        self.simpleLmgr = AnimSimpleLayerManager(parent=panel,
                                                 layerList=self._layerList,
                                                 modal=True)
        self.simpleLmgr.SetMinSize((globalvar.DIALOG_GSELECT_SIZE[0], 80))
        slmgrSizer.Add(
            self.simpleLmgr,
            proportion=1,
            flag=wx.EXPAND | wx.ALL,
            border=5)

        self.legend = wx.CheckBox(panel, label=_("Show raster legend"))
        self.legend.SetValue(bool(self.animationData.legendCmd))
        self.legendBtn = wx.Button(panel, label=_("Set options"))
        self.legend.Bind(wx.EVT_CHECKBOX, self.OnLegend)
        self.legendBtn.Bind(wx.EVT_BUTTON, self.OnLegendProperties)

        hbox = wx.BoxSizer(wx.HORIZONTAL)
        hbox.Add(self.legend, proportion=1, flag=wx.ALIGN_CENTER_VERTICAL)
        hbox.Add(self.legendBtn, proportion=0, flag=wx.LEFT, border=5)
        slmgrSizer.Add(
            hbox,
            proportion=0,
            flag=wx.EXPAND | wx.ALL,
            border=3)

        panel.SetSizerAndFit(slmgrSizer)
        panel.SetAutoLayout(True)

        return panel

    def _create3DPanel(self, parent):
        panel = wx.Panel(parent, id=wx.ID_ANY)
        dataStBox = wx.StaticBox(parent=panel, id=wx.ID_ANY,
                                 label=' %s ' % _("3D view parameters"))
        dataBoxSizer = wx.StaticBoxSizer(dataStBox, wx.VERTICAL)

        # workspace file
        self.fileSelector = filebrowse.FileBrowseButton(
            parent=panel,
            id=wx.ID_ANY,
            size=globalvar.DIALOG_GSELECT_SIZE,
            labelText=_("Workspace file:"),
            dialogTitle=_(
                "Choose workspace file to "
                "import 3D view parameters"),
            buttonText=_('Browse'),
            startDirectory=os.getcwd(),
            fileMode=0,
            fileMask="GRASS Workspace File (*.gxw)|*.gxw")
        if self.animationData.workspaceFile:
            self.fileSelector.SetValue(self.animationData.workspaceFile)
        self.paramLabel = wx.StaticText(
            panel, wx.ID_ANY, label=_("Parameter for animation:"))
        self.paramChoice = wx.Choice(
            panel, id=wx.ID_ANY, choices=self.animationData.nvizParameters)
        self.paramChoice.SetStringSelection(self.animationData.nvizParameter)

        hbox = wx.BoxSizer(wx.HORIZONTAL)
        hbox.Add(
            self.fileSelector,
            proportion=1,
            flag=wx.EXPAND | wx.ALIGN_CENTER)
        dataBoxSizer.Add(
            hbox,
            proportion=0,
            flag=wx.EXPAND | wx.ALL,
            border=3)

        hbox = wx.BoxSizer(wx.HORIZONTAL)
        hbox.Add(
            self.paramLabel,
            proportion=1,
            flag=wx.ALIGN_CENTER_VERTICAL)
        hbox.Add(self.paramChoice, proportion=1, flag=wx.EXPAND)
        dataBoxSizer.Add(
            hbox,
            proportion=0,
            flag=wx.EXPAND | wx.ALL,
            border=3)

        panel.SetSizerAndFit(dataBoxSizer)
        panel.SetAutoLayout(True)

        return panel

    def _createAdvancedPage(self, parent):
        panel = wx.Panel(parent=parent)

        mainSizer = wx.BoxSizer(wx.VERTICAL)
        box = wx.StaticBox(
            parent=panel, label=" %s " %
            _("Animate region change (2D view only)"))
        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)

        gridSizer = wx.GridBagSizer(hgap=3, vgap=3)
        gridSizer.Add(wx.StaticText(panel, label=_("Start region:")),
                      pos=(0, 0), flag=wx.ALIGN_CENTER_VERTICAL)
        self.stRegion = Select(parent=panel, type='region', size=(200, -1))
        if self.animationData.startRegion:
            self.stRegion.SetValue(self.animationData.startRegion)
        gridSizer.Add(
            self.stRegion, pos=(0, 1),
            flag=wx.ALIGN_CENTER_VERTICAL | wx.EXPAND)

        self.endRegRadio = wx.RadioButton(
            panel, label=_("End region:"), style=wx.RB_GROUP)
        gridSizer.Add(self.endRegRadio, pos=(1, 0), flag=wx.EXPAND)
        self.endRegion = Select(parent=panel, type='region', size=(200, -1))
        gridSizer.Add(
            self.endRegion, pos=(1, 1),
            flag=wx.ALIGN_CENTER_VERTICAL | wx.EXPAND)
        self.zoomRadio = wx.RadioButton(panel, label=_("Zoom value:"))
        self.zoomRadio.SetToolTipString(_("N-S/E-W distances in map units used to "
                                          "gradually reduce region."))
        gridSizer.Add(self.zoomRadio, pos=(2, 0), flag=wx.EXPAND)

        zoomSizer = wx.BoxSizer(wx.HORIZONTAL)
        self.zoomNS = wx.TextCtrl(panel, validator=FloatValidator())
        self.zoomEW = wx.TextCtrl(panel, validator=FloatValidator())
        zoomSizer.Add(wx.StaticText(panel, label=_("N-S:")), proportion=0,
                      flag=wx.ALIGN_CENTER_VERTICAL | wx.LEFT, border=3)
        zoomSizer.Add(self.zoomNS, proportion=1, flag=wx.LEFT, border=3)
        zoomSizer.Add(wx.StaticText(panel, label=_("E-W:")), proportion=0,
                      flag=wx.ALIGN_CENTER_VERTICAL | wx.LEFT, border=3)
        zoomSizer.Add(self.zoomEW, proportion=1, flag=wx.LEFT, border=3)
        gridSizer.Add(
            zoomSizer, pos=(2, 1),
            flag=wx.ALIGN_CENTER_VERTICAL | wx.EXPAND)
        if self.animationData.endRegion:
            self.endRegRadio.SetValue(True)
            self.zoomRadio.SetValue(False)
            self.endRegion.SetValue(self.animationData.endRegion)
        if self.animationData.zoomRegionValue:
            self.endRegRadio.SetValue(False)
            self.zoomRadio.SetValue(True)
            zoom = self.animationData.zoomRegionValue
            self.zoomNS.SetValue(str(zoom[0]))
            self.zoomEW.SetValue(str(zoom[1]))

        self.endRegRadio.Bind(
            wx.EVT_RADIOBUTTON,
            lambda evt: self._enableRegionWidgets())
        self.zoomRadio.Bind(
            wx.EVT_RADIOBUTTON,
            lambda evt: self._enableRegionWidgets())
        self._enableRegionWidgets()

        gridSizer.AddGrowableCol(1)
        sizer.Add(gridSizer, proportion=0, flag=wx.EXPAND | wx.ALL, border=3)
        mainSizer.Add(sizer, proportion=0, flag=wx.EXPAND | wx.ALL, border=3)

        panel.SetSizer(mainSizer)
        mainSizer.Fit(panel)

        return panel

    def _enableRegionWidgets(self):
        """Enables/disables region widgets
        according to which radiobutton is active."""
        endReg = self.endRegRadio.GetValue()
        self.endRegion.Enable(endReg)
        self.zoomNS.Enable(not endReg)
        self.zoomEW.Enable(not endReg)

    def OnViewMode(self, event):
        mode = self.nDChoice.GetSelection()
        self.Freeze()
        self.simpleLmgr.Activate3D(mode == 1)
        self.warning3DLayers.Show(mode == 1)

        # disable region widgets for 3d
        regSizer = self.stRegion.GetContainingSizer()
        for child in regSizer.GetChildren():
            if child.IsSizer():
                for child_ in child.GetSizer().GetChildren():
                    child_.GetWindow().Enable(mode != 1)
            elif child.IsWindow():
                child.GetWindow().Enable(mode != 1)
        self._enableRegionWidgets()

        # update layout
        sizer = self.threeDPanel.GetContainingSizer()
        sizer.Show(self.threeDPanel, mode == 1, True)
        sizer.Layout()
        self.Thaw()

    def OnLegend(self, event):
        if not self.legend.IsChecked():
            return
        if self._tmpLegendCmd or self.animationData.legendCmd:
            return
        cmd = ['d.legend', 'at=5,50,2,5']
        GUI(parent=self, modal=True).ParseCommand(
            cmd=cmd, completed=(self.GetOptData, '', ''))

    def OnLegendProperties(self, event):
        """Set options for legend"""
        if self._tmpLegendCmd:
            cmd = self._tmpLegendCmd
        elif self.animationData.legendCmd:
            cmd = self.animationData.legendCmd
        else:
            cmd = ['d.legend', 'at=5,50,2,5']

        GUI(parent=self, modal=True).ParseCommand(
            cmd=cmd, completed=(self.GetOptData, '', ''))

    def GetOptData(self, dcmd, layer, params, propwin):
        """Process decoration layer data"""
        if dcmd:
            self._tmpLegendCmd = dcmd

            if not self.legend.IsChecked():
                self.legend.SetValue(True)
        else:
            if not self._tmpLegendCmd and not self.animationData.legendCmd:
                self.legend.SetValue(False)

    def _update(self):
        if self.nDChoice.GetSelection() == 1 and len(self._layerList) > 1:
            raise GException(_("Only one series or space-time "
                               "dataset is accepted for 3D mode."))
        hasSeries = False
        for layer in self._layerList:
            if layer.active and hasattr(layer, 'maps'):
                hasSeries = True
                break
        if not hasSeries:
            raise GException(_("No map series or space-time dataset added."))

        self.animationData.layerList = self._layerList
        self.animationData.name = self.nameCtrl.GetValue()
        self.animationData.windowIndex = self.windowChoice.GetSelection()

        sel = self.nDChoice.GetSelection()
        self.animationData.viewMode = self.nDChoice.GetClientData(sel)
        self.animationData.legendCmd = None
        if self._tmpLegendCmd:
            if self.legend.IsChecked():
                self.animationData.legendCmd = self._tmpLegendCmd

        if self.threeDPanel.IsShown():
            self.animationData.workspaceFile = self.fileSelector.GetValue()
        if self.threeDPanel.IsShown():
            self.animationData.nvizParameter = self.paramChoice.GetStringSelection()
        # region (2d only)
        if self.animationData.viewMode == '3d':
            self.animationData.startRegion = None
            self.animationData.endRegion = None
            self.animationData.zoomRegionValue = None
            return
        isEnd = self.endRegRadio.GetValue() and self.endRegion.GetValue()
        isZoom = self.zoomRadio.GetValue() and self.zoomNS.GetValue() and self.zoomEW.GetValue()
        isStart = self.stRegion.GetValue()
        condition = bool(isStart) + bool(isZoom) + bool(isEnd)
        if condition == 1:
            raise GException(_("Region information is not complete"))
        elif condition == 2:
            self.animationData.startRegion = isStart
            if isEnd:
                self.animationData.endRegion = self.endRegion.GetValue()
                self.animationData.zoomRegionValue = None
            else:
                self.animationData.zoomRegionValue = (
                    float(self.zoomNS.GetValue()),
                    float(self.zoomEW.GetValue()))
                self.animationData.endRegion = None
        else:
            self.animationData.startRegion = None
            self.animationData.endRegion = None
            self.animationData.zoomRegionValue = None

    def OnOk(self, event):
        try:
            self._update()
            self.EndModal(wx.ID_OK)
        except (GException, ValueError, IOError) as e:
            GError(
                message=str(e),
                showTraceback=False,
                caption=_("Invalid input"))


class EditDialog(wx.Dialog):

    def __init__(self, parent, evalFunction, animationData, maxAnimations):
        wx.Dialog.__init__(self, parent=parent, id=wx.ID_ANY,
                           style=wx.DEFAULT_DIALOG_STYLE)
        self.animationData = copy.deepcopy(animationData)
        self.eval = evalFunction
        self.SetTitle(_("Add, edit or remove animations"))
        self._layout()
        self.SetSize((300, -1))
        self.maxAnimations = maxAnimations
        self.result = None

    def _layout(self):
        mainSizer = wx.BoxSizer(wx.VERTICAL)
        box = wx.StaticBox(
            parent=self,
            id=wx.ID_ANY,
            label=" %s " %
            _("List of animations"))
        sizer = wx.StaticBoxSizer(box, wx.VERTICAL)
        gridBagSizer = wx.GridBagSizer(hgap=5, vgap=5)
        gridBagSizer.AddGrowableCol(0)
        # gridBagSizer.AddGrowableCol(1,1)

        self.listbox = wx.ListBox(
            self, id=wx.ID_ANY, choices=[],
            style=wx.LB_SINGLE | wx.LB_NEEDED_SB)
        self.listbox.Bind(wx.EVT_LISTBOX_DCLICK, self.OnEdit)

        self.addButton = wx.Button(self, id=wx.ID_ANY, label=_("Add"))
        self.addButton.Bind(wx.EVT_BUTTON, self.OnAdd)
        self.editButton = wx.Button(self, id=wx.ID_ANY, label=_("Edit"))
        self.editButton.Bind(wx.EVT_BUTTON, self.OnEdit)
        self.removeButton = wx.Button(self, id=wx.ID_ANY, label=_("Remove"))
        self.removeButton.Bind(wx.EVT_BUTTON, self.OnRemove)

        self._updateListBox()

        gridBagSizer.Add(self.listbox, pos=(0, 0), span=(3, 1),
                         flag=wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, border=0)
        gridBagSizer.Add(self.addButton, pos=(0, 1),
                         flag=wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, border=0)
        gridBagSizer.Add(self.editButton, pos=(1, 1),
                         flag=wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, border=0)
        gridBagSizer.Add(self.removeButton, pos=(2, 1),
                         flag=wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, border=0)
        sizer.Add(
            gridBagSizer,
            proportion=0,
            flag=wx.ALL | wx.EXPAND,
            border=5)
        mainSizer.Add(sizer, proportion=0,
                      flag=wx.EXPAND | wx.ALL, border=5)

        # buttons
        self.btnOk = wx.Button(self, wx.ID_OK)
        self.btnCancel = wx.Button(self, wx.ID_CANCEL)
        self.btnOk.SetDefault()
        self.btnOk.Bind(wx.EVT_BUTTON, self.OnOk)
        # button sizer
        btnStdSizer = wx.StdDialogButtonSizer()
        btnStdSizer.AddButton(self.btnOk)
        btnStdSizer.AddButton(self.btnCancel)
        btnStdSizer.Realize()

        mainSizer.Add(btnStdSizer, proportion=0,
                      flag=wx.EXPAND | wx.ALL | wx.ALIGN_RIGHT, border=5)

        self.SetSizer(mainSizer)
        mainSizer.Fit(self)

    def _updateListBox(self):
        self.listbox.Clear()
        for anim in self.animationData:
            self.listbox.Append(anim.name, clientData=anim)
        if self.animationData:
            self.listbox.SetSelection(0)

    def _getNextIndex(self):
        indices = [anim.windowIndex for anim in self.animationData]
        for i in range(self.maxAnimations):
            if i not in indices:
                return i
        return None

    def OnAdd(self, event):
        windowIndex = self._getNextIndex()
        if windowIndex is None:
            GMessage(
                self,
                message=_("Maximum number of animations is %d.") %
                self.maxAnimations)
            return
        animData = AnimationData()
        # number of active animations
        animationIndex = len(self.animationData)
        animData.SetDefaultValues(windowIndex, animationIndex)
        dlg = InputDialog(parent=self, mode='add', animationData=animData)
        dlg.CenterOnParent()
        if dlg.ShowModal() == wx.ID_CANCEL:
            dlg.Destroy()
            return
        dlg.Destroy()
        self.animationData.append(animData)

        self._updateListBox()

    def OnEdit(self, event):
        index = self.listbox.GetSelection()
        if index == wx.NOT_FOUND:
            return

        animData = self.listbox.GetClientData(index)
        dlg = InputDialog(parent=self, mode='edit', animationData=animData)
        dlg.CenterOnParent()
        if dlg.ShowModal() == wx.ID_CANCEL:
            dlg.Destroy()
            return
        dlg.Destroy()

        self._updateListBox()

    def OnRemove(self, event):
        index = self.listbox.GetSelection()
        if index == wx.NOT_FOUND:
            return

        animData = self.listbox.GetClientData(index)
        self.animationData.remove(animData)

        self._updateListBox()

    def GetResult(self):
        return self.result

    def OnOk(self, event):
        indices = set([anim.windowIndex for anim in self.animationData])
        if len(indices) != len(self.animationData):
            GError(
                parent=self, message=_(
                    "More animations are using one window."
                    " Please select different window for each animation."))
            return
        try:
            temporalMode, tempManager = self.eval(self.animationData)
        except GException as e:
            GError(parent=self, message=e.value, showTraceback=False)
            return
        self.result = (self.animationData, temporalMode, tempManager)

        self.EndModal(wx.ID_OK)


class ExportDialog(wx.Dialog):

    def __init__(self, parent, temporal, timeTick):
        wx.Dialog.__init__(
            self,
            parent=parent,
            id=wx.ID_ANY,
            title=_("Export animation"),
            style=wx.DEFAULT_DIALOG_STYLE)
        self.decorations = []

        self.temporal = temporal
        self.timeTick = timeTick
        self._layout()

        # export animation
        self.doExport = Signal('ExportDialog::doExport')

        wx.CallAfter(self._hideAll)

    def _layout(self):
        notebook = wx.Notebook(self, id=wx.ID_ANY)
        mainSizer = wx.BoxSizer(wx.VERTICAL)

        notebook.AddPage(
            page=self._createExportFormatPanel(notebook),
            text=_("Format"))
        notebook.AddPage(
            page=self._createDecorationsPanel(notebook),
            text=_("Decorations"))
        mainSizer.Add(notebook, proportion=0,
                      flag=wx.EXPAND | wx.ALL | wx.ALIGN_RIGHT, border=5)

        self.btnExport = wx.Button(self, wx.ID_OK)
        self.btnExport.SetLabel(_("Export"))
        self.btnCancel = wx.Button(self, wx.ID_CANCEL)
        self.btnExport.SetDefault()

        self.btnExport.Bind(wx.EVT_BUTTON, self.OnExport)

        # button sizer
        btnStdSizer = wx.StdDialogButtonSizer()
        btnStdSizer.AddButton(self.btnExport)
        btnStdSizer.AddButton(self.btnCancel)
        btnStdSizer.Realize()

        mainSizer.Add(btnStdSizer, proportion=0,
                      flag=wx.EXPAND | wx.ALL | wx.ALIGN_RIGHT, border=5)
        self.SetSizer(mainSizer)

        # set the longest option to fit
        self.hidevbox.Show(self.fontBox, True)
        self.hidevbox.Show(self.imageBox, False)
        self.hidevbox.Show(self.textBox, True)
        self.hidevbox.Show(self.posBox, True)
        self.hidevbox.Show(self.informBox, False)
        mainSizer.Fit(self)

    def _createDecorationsPanel(self, notebook):
        panel = wx.Panel(notebook, id=wx.ID_ANY)
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(
            self._createDecorationsList(panel),
            proportion=0,
            flag=wx.ALL | wx.EXPAND,
            border=10)
        sizer.Add(
            self._createDecorationsProperties(panel),
            proportion=0,
            flag=wx.ALL | wx.EXPAND,
            border=10)
        panel.SetSizer(sizer)
        sizer.Fit(panel)
        return panel

    def _createDecorationsList(self, panel):
        gridBagSizer = wx.GridBagSizer(hgap=5, vgap=5)

        gridBagSizer.AddGrowableCol(0)

        self.listbox = wx.ListBox(panel, id=wx.ID_ANY, choices=[],
                                  style=wx.LB_SINGLE | wx.LB_NEEDED_SB)
        self.listbox.Bind(wx.EVT_LISTBOX, self.OnSelectionChanged)

        gridBagSizer.Add(self.listbox, pos=(0, 0), span=(4, 1),
                         flag=wx.ALIGN_CENTER_VERTICAL | wx.EXPAND, border=0)

        buttonNames = ['time', 'image', 'text']
        buttonLabels = [_("Add time stamp"), _("Add image"), _("Add text")]
        i = 0
        for buttonName, buttonLabel in zip(buttonNames, buttonLabels):
            if buttonName == 'time' and self.temporal == TemporalMode.NONTEMPORAL:
                continue
            btn = wx.Button(
                panel,
                id=wx.ID_ANY,
                name=buttonName,
                label=buttonLabel)
            btn.Bind(
                wx.EVT_BUTTON,
                lambda evt,
                temp=buttonName: self.OnAddDecoration(
                    evt,
                    temp))
            gridBagSizer.Add(
                btn,
                pos=(
                    i,
                    1),
                flag=wx.ALIGN_CENTER_VERTICAL | wx.EXPAND,
                border=0)
            i += 1
        removeButton = wx.Button(panel, id=wx.ID_ANY, label=_("Remove"))
        removeButton.Bind(wx.EVT_BUTTON, self.OnRemove)
        gridBagSizer.Add(
            removeButton,
            pos=(
                i,
                1),
            flag=wx.ALIGN_CENTER_VERTICAL | wx.EXPAND,
            border=0)

        return gridBagSizer

    def _createDecorationsProperties(self, panel):
        self.hidevbox = wx.BoxSizer(wx.VERTICAL)
        # inform label
        self.informBox = wx.BoxSizer(wx.HORIZONTAL)
        if self.temporal == TemporalMode.TEMPORAL:
            label = _(
                "Add time stamp, image or text decoration by one of the buttons above.")
        else:
            label = _("Add image or text decoration by one of the buttons above.")

        label = wx.StaticText(panel, id=wx.ID_ANY, label=label)
        label.Wrap(400)
        self.informBox.Add(
            label,
            proportion=1,
            flag=wx.ALIGN_CENTER_VERTICAL | wx.RIGHT,
            border=5)
        self.hidevbox.Add(
            self.informBox,
            proportion=0,
            flag=wx.EXPAND | wx.BOTTOM,
            border=5)

        # font
        self.fontBox = wx.BoxSizer(wx.HORIZONTAL)
        self.fontBox.Add(
            wx.StaticText(
                panel,
                id=wx.ID_ANY,
                label=_("Font settings:")),
            proportion=0,
            flag=wx.ALIGN_CENTER_VERTICAL | wx.RIGHT,
            border=5)
        self.sampleLabel = wx.StaticText(
            panel, id=wx.ID_ANY, label=_("Sample text"))
        self.fontBox.Add(self.sampleLabel, proportion=1,
                         flag=wx.ALIGN_CENTER | wx.RIGHT | wx.LEFT, border=5)
        fontButton = wx.Button(panel, id=wx.ID_ANY, label=_("Set font"))
        fontButton.Bind(wx.EVT_BUTTON, self.OnFont)
        self.fontBox.Add(
            fontButton,
            proportion=0,
            flag=wx.ALIGN_CENTER_VERTICAL)
        self.hidevbox.Add(
            self.fontBox,
            proportion=0,
            flag=wx.EXPAND | wx.BOTTOM,
            border=5)

        # image
        self.imageBox = wx.BoxSizer(wx.HORIZONTAL)
        filetype, ltype = GetImageHandlers(wx.EmptyImage(10, 10))
        self.browse = filebrowse.FileBrowseButton(
            parent=panel, id=wx.ID_ANY, fileMask=filetype,
            labelText=_("Image file:"),
            dialogTitle=_('Choose image file'),
            buttonText=_('Browse'),
            startDirectory=os.getcwd(),
            fileMode=wx.FD_OPEN, changeCallback=self.OnSetImage)
        self.imageBox.Add(self.browse, proportion=1, flag=wx.EXPAND)
        self.hidevbox.Add(
            self.imageBox,
            proportion=0,
            flag=wx.EXPAND | wx.BOTTOM,
            border=5)
        # text
        self.textBox = wx.BoxSizer(wx.HORIZONTAL)
        self.textBox.Add(
            wx.StaticText(
                panel,
                id=wx.ID_ANY,
                label=_("Text:")),
            proportion=0,
            flag=wx.ALIGN_CENTER_VERTICAL | wx.RIGHT,
            border=5)
        self.textCtrl = wx.TextCtrl(panel, id=wx.ID_ANY)
        self.textCtrl.Bind(wx.EVT_TEXT, self.OnText)
        self.textBox.Add(self.textCtrl, proportion=1, flag=wx.EXPAND)
        self.hidevbox.Add(self.textBox, proportion=0, flag=wx.EXPAND)

        self.posBox = self._positionWidget(panel)
        self.hidevbox.Add(
            self.posBox,
            proportion=0,
            flag=wx.EXPAND | wx.TOP,
            border=5)
        return self.hidevbox

    def _positionWidget(self, panel):
        grid = wx.GridBagSizer(vgap=5, hgap=5)
        label = wx.StaticText(
            panel, id=wx.ID_ANY, label=_(
                "Placement as percentage of"
                " screen coordinates (X: 0, Y: 0 is top left):"))
        label.Wrap(400)
        self.spinX = SpinCtrl(
            panel, id=wx.ID_ANY, min=0, max=100, initial=10)
        self.spinY = SpinCtrl(
            panel, id=wx.ID_ANY, min=0, max=100, initial=10)
        self.spinX.Bind(
            wx.EVT_SPINCTRL,
            lambda evt,
            temp='X': self.OnPosition(
                evt,
                temp))
        self.spinY.Bind(
            wx.EVT_SPINCTRL,
            lambda evt,
            temp='Y': self.OnPosition(
                evt,
                temp))

        grid.Add(label, pos=(0, 0), span=(1, 4), flag=wx.EXPAND)
        grid.Add(wx.StaticText(panel, id=wx.ID_ANY, label=_("X:")), pos=(1, 0),
                 flag=wx.ALIGN_CENTER_VERTICAL)
        grid.Add(wx.StaticText(panel, id=wx.ID_ANY, label=_("Y:")), pos=(1, 2),
                 flag=wx.ALIGN_CENTER_VERTICAL)
        grid.Add(self.spinX, pos=(1, 1))
        grid.Add(self.spinY, pos=(1, 3))

        return grid

    def _createExportFormatPanel(self, notebook):
        panel = wx.Panel(notebook, id=wx.ID_ANY)
        borderSizer = wx.BoxSizer(wx.VERTICAL)

        hSizer = wx.BoxSizer(wx.HORIZONTAL)
        choices = [_("image sequence"), _("animated GIF"), _("SWF"), _("AVI")]
        self.formatChoice = wx.Choice(parent=panel, id=wx.ID_ANY,
                                      choices=choices)
        self.formatChoice.Bind(
            wx.EVT_CHOICE,
            lambda event: self.ChangeFormat(
                event.GetSelection()))
        hSizer.Add(
            wx.StaticText(
                panel,
                id=wx.ID_ANY,
                label=_("Export to:")),
            proportion=0,
            flag=wx.ALIGN_CENTER_VERTICAL | wx.ALL,
            border=2)
        hSizer.Add(
            self.formatChoice,
            proportion=1,
            flag=wx.ALIGN_CENTER_VERTICAL | wx.EXPAND | wx.ALL,
            border=2)
        borderSizer.Add(
            hSizer,
            proportion=0,
            flag=wx.EXPAND | wx.ALL,
            border=3)

        helpSizer = wx.BoxSizer(wx.HORIZONTAL)
        helpSizer.AddStretchSpacer(1)
        self.formatPanelSizer = wx.BoxSizer(wx.VERTICAL)
        helpSizer.Add(self.formatPanelSizer, proportion=5, flag=wx.EXPAND)
        borderSizer.Add(helpSizer, proportion=1, flag=wx.EXPAND)
        self.formatPanels = []

        # panel for image sequence
        imSeqPanel = wx.Panel(parent=panel, id=wx.ID_ANY)
        prefixLabel = wx.StaticText(
            imSeqPanel, id=wx.ID_ANY, label=_("File prefix:"))
        self.prefixCtrl = wx.TextCtrl(
            imSeqPanel, id=wx.ID_ANY, value=_("animation_"))
        formatLabel = wx.StaticText(
            imSeqPanel, id=wx.ID_ANY, label=_("File format:"))
        imageTypes = ['PNG', 'JPEG', 'GIF', 'TIFF', 'PPM', 'BMP']
        self.imSeqFormatChoice = wx.Choice(imSeqPanel, choices=imageTypes)
        self.imSeqFormatChoice.SetSelection(0)
        self.dirBrowse = filebrowse.DirBrowseButton(
            parent=imSeqPanel, id=wx.ID_ANY, labelText=_("Directory:"),
            dialogTitle=_("Choose directory for export"),
            buttonText=_("Browse"),
            startDirectory=os.getcwd())

        dirGridSizer = wx.GridBagSizer(hgap=5, vgap=5)
        dirGridSizer.Add(
            prefixLabel, pos=(0, 0),
            flag=wx.ALIGN_CENTER_VERTICAL)
        dirGridSizer.Add(self.prefixCtrl, pos=(0, 1), flag=wx.EXPAND)
        dirGridSizer.Add(
            formatLabel, pos=(1, 0),
            flag=wx.ALIGN_CENTER_VERTICAL)
        dirGridSizer.Add(self.imSeqFormatChoice, pos=(1, 1), flag=wx.EXPAND)
        dirGridSizer.Add(
            self.dirBrowse, pos=(
                2, 0), flag=wx.EXPAND, span=(
                1, 2))
        dirGridSizer.AddGrowableCol(1)
        imSeqPanel.SetSizer(dirGridSizer)
        dirGridSizer.Fit(imSeqPanel)

        self.formatPanelSizer.Add(
            imSeqPanel,
            proportion=1,
            flag=wx.EXPAND | wx.ALL,
            border=5)
        self.formatPanels.append(imSeqPanel)

        # panel for gif
        gifPanel = wx.Panel(parent=panel, id=wx.ID_ANY)

        self.gifBrowse = filebrowse.FileBrowseButton(
            parent=gifPanel,
            id=wx.ID_ANY,
            fileMask="GIF file (*.gif)|*.gif",
            labelText=_("GIF file:"),
            dialogTitle=_("Choose file to save animation"),
            buttonText=_("Browse"),
            startDirectory=os.getcwd(),
            fileMode=wx.FD_SAVE)
        gifGridSizer = wx.GridBagSizer(hgap=5, vgap=5)
        gifGridSizer.AddGrowableCol(0)
        gifGridSizer.Add(self.gifBrowse, pos=(0, 0), flag=wx.EXPAND)
        gifPanel.SetSizer(gifGridSizer)
        gifGridSizer.Fit(gifPanel)

        self.formatPanelSizer.Add(
            gifPanel,
            proportion=1,
            flag=wx.EXPAND | wx.ALL,
            border=5)
        self.formatPanels.append(gifPanel)

        # panel for swf
        swfPanel = wx.Panel(parent=panel, id=wx.ID_ANY)
        self.swfBrowse = filebrowse.FileBrowseButton(
            parent=swfPanel,
            id=wx.ID_ANY,
            fileMask="SWF file (*.swf)|*.swf",
            labelText=_("SWF file:"),
            dialogTitle=_("Choose file to save animation"),
            buttonText=_("Browse"),
            startDirectory=os.getcwd(),
            fileMode=wx.FD_SAVE)
        swfGridSizer = wx.GridBagSizer(hgap=5, vgap=5)
        swfGridSizer.AddGrowableCol(0)
        swfGridSizer.Add(self.swfBrowse, pos=(0, 0), flag=wx.EXPAND)
        swfPanel.SetSizer(swfGridSizer)
        swfGridSizer.Fit(swfPanel)

        self.formatPanelSizer.Add(
            swfPanel,
            proportion=1,
            flag=wx.EXPAND | wx.ALL,
            border=5)
        self.formatPanels.append(swfPanel)

        # panel for avi
        aviPanel = wx.Panel(parent=panel, id=wx.ID_ANY)
        ffmpeg = gcore.find_program('ffmpeg', '--help')
        if not ffmpeg:
            warning = _(
                "Program 'ffmpeg' was not found.\nPlease install it first "
                "and make sure\nit's in the PATH variable.")
            warningLabel = wx.StaticText(parent=aviPanel, label=warning)
            warningLabel.SetForegroundColour(wx.RED)
        self.aviBrowse = filebrowse.FileBrowseButton(
            parent=aviPanel,
            id=wx.ID_ANY,
            fileMask="AVI file (*.avi)|*.avi",
            labelText=_("AVI file:"),
            dialogTitle=_("Choose file to save animation"),
            buttonText=_("Browse"),
            startDirectory=os.getcwd(),
            fileMode=wx.FD_SAVE)
        encodingLabel = wx.StaticText(
            parent=aviPanel,
            id=wx.ID_ANY,
            label=_("Video codec:"))
        self.encodingText = wx.TextCtrl(
            parent=aviPanel, id=wx.ID_ANY, value='mpeg4')
        optionsLabel = wx.StaticText(
            parent=aviPanel, label=_("Additional options:"))
        self.optionsText = wx.TextCtrl(parent=aviPanel)
        self.optionsText.SetToolTipString(
            _(
                "Consider adding '-sameq' or '-qscale 1' "
                "if not satisfied with video quality. "
                "Options depend on ffmpeg version."))
        aviGridSizer = wx.GridBagSizer(hgap=5, vgap=5)
        aviGridSizer.Add(
            self.aviBrowse, pos=(
                0, 0), span=(
                1, 2), flag=wx.EXPAND)
        aviGridSizer.Add(
            encodingLabel, pos=(1, 0),
            flag=wx.ALIGN_CENTER_VERTICAL)
        aviGridSizer.Add(self.encodingText, pos=(1, 1), flag=wx.EXPAND)
        aviGridSizer.Add(
            optionsLabel, pos=(2, 0),
            flag=wx.ALIGN_CENTER_VERTICAL)
        aviGridSizer.Add(self.optionsText, pos=(2, 1), flag=wx.EXPAND)
        if not ffmpeg:
            aviGridSizer.Add(warningLabel, pos=(3, 0), span=(1, 2),
                             flag=wx.ALIGN_CENTER_VERTICAL | wx.EXPAND)

        aviGridSizer.AddGrowableCol(1)
        aviPanel.SetSizer(aviGridSizer)
        aviGridSizer.Fit(aviPanel)

        self.formatPanelSizer.Add(
            aviPanel,
            proportion=1,
            flag=wx.EXPAND | wx.ALL,
            border=5)
        self.formatPanels.append(aviPanel)

        fpsSizer = wx.BoxSizer(wx.HORIZONTAL)
        fps = 1000 / self.timeTick
        fpsSizer.Add(
            wx.StaticText(
                panel,
                id=wx.ID_ANY,
                label=_("Current frame rate: %.2f fps") %
                fps),
            proportion=1,
            flag=wx.EXPAND)
        borderSizer.Add(
            fpsSizer,
            proportion=0,
            flag=wx.ALIGN_CENTER_VERTICAL | wx.ALL,
            border=5)

        panel.SetSizer(borderSizer)
        borderSizer.Fit(panel)
        self.ChangeFormat(index=0)

        return panel

    def ChangeFormat(self, index):
        for i, panel in enumerate(self.formatPanels):
            self.formatPanelSizer.Show(item=panel, show=(i == index))
        self.formatPanelSizer.Layout()

    def OnFont(self, event):
        index = self.listbox.GetSelection()
        # should not happen
        if index == wx.NOT_FOUND:
            return
        cdata = self.listbox.GetClientData(index)
        font = cdata['font']

        fontdata = wx.FontData()
        fontdata.EnableEffects(True)
        fontdata.SetColour('black')
        fontdata.SetInitialFont(font)

        dlg = wx.FontDialog(self, fontdata)
        dlg.CenterOnParent()
        if dlg.ShowModal() == wx.ID_OK:
            newfontdata = dlg.GetFontData()
            font = newfontdata.GetChosenFont()
            self.sampleLabel.SetFont(font)
            cdata['font'] = font
            self.Layout()

    def OnPosition(self, event, coord):
        index = self.listbox.GetSelection()
        # should not happen
        if index == wx.NOT_FOUND:
            return
        cdata = self.listbox.GetClientData(index)
        cdata['pos'][coord == 'Y'] = event.GetInt()

    def OnSetImage(self, event):
        index = self.listbox.GetSelection()
        # should not happen
        if index == wx.NOT_FOUND:
            return
        cdata = self.listbox.GetClientData(index)
        cdata['file'] = event.GetString()

    def OnAddDecoration(self, event, name):
        if name == 'time':
            timeInfo = {'name': name, 'font': self.GetFont(), 'pos': [10, 10]}
            self.decorations.append(timeInfo)
        elif name == 'image':
            imageInfo = {'name': name, 'file': '', 'pos': [10, 10]}
            self.decorations.append(imageInfo)
        elif name == 'text':
            textInfo = {
                'name': name,
                'font': self.GetFont(),
                'text': '',
                'pos': [
                    10,
                    10]}
            self.decorations.append(textInfo)

        self._updateListBox()
        self.listbox.SetSelection(self.listbox.GetCount() - 1)
        self.OnSelectionChanged(event=None)

    def OnSelectionChanged(self, event):
        index = self.listbox.GetSelection()
        if index == wx.NOT_FOUND:
            self._hideAll()
            return
        cdata = self.listbox.GetClientData(index)
        self.hidevbox.Show(self.fontBox, (cdata['name'] in ('time', 'text')))
        self.hidevbox.Show(self.imageBox, (cdata['name'] == 'image'))
        self.hidevbox.Show(self.textBox, (cdata['name'] == 'text'))
        self.hidevbox.Show(self.posBox, True)
        self.hidevbox.Show(self.informBox, False)

        self.spinX.SetValue(cdata['pos'][0])
        self.spinY.SetValue(cdata['pos'][1])
        if cdata['name'] == 'image':
            self.browse.SetValue(cdata['file'])
        elif cdata['name'] in ('time', 'text'):
            self.sampleLabel.SetFont(cdata['font'])
            if cdata['name'] == 'text':
                self.textCtrl.SetValue(cdata['text'])

        self.hidevbox.Layout()
        # self.Layout()

    def OnText(self, event):
        index = self.listbox.GetSelection()
        # should not happen
        if index == wx.NOT_FOUND:
            return
        cdata = self.listbox.GetClientData(index)
        cdata['text'] = event.GetString()

    def OnRemove(self, event):
        index = self.listbox.GetSelection()
        if index == wx.NOT_FOUND:
            return

        decData = self.listbox.GetClientData(index)
        self.decorations.remove(decData)

        self._updateListBox()
        if self.listbox.GetCount():
            self.listbox.SetSelection(0)
            self.OnSelectionChanged(event=None)

    def OnExport(self, event):
        for decor in self.decorations:
            if decor['name'] == 'image':
                if not os.path.exists(decor['file']):
                    if decor['file']:
                        GError(
                            parent=self,
                            message=_("File %s not found.") %
                            decor['file'])
                    else:
                        GError(parent=self,
                               message=_("Decoration image file is missing."))
                    return

        if self.formatChoice.GetSelection() == 0:
            name = self.dirBrowse.GetValue()
            if not os.path.exists(name):
                if name:
                    GError(
                        parent=self,
                        message=_("Directory %s not found.") %
                        name)
                else:
                    GError(parent=self, message=_(
                        "Export directory is missing."))
                return
        elif self.formatChoice.GetSelection() == 1:
            if not self.gifBrowse.GetValue():
                GError(parent=self, message=_("Export file is missing."))
                return
        elif self.formatChoice.GetSelection() == 2:
            if not self.swfBrowse.GetValue():
                GError(parent=self, message=_("Export file is missing."))
                return

        # hide only to keep previous values
        self.Hide()
        self.doExport.emit(exportInfo=self.GetExportInformation(),
                           decorations=self.GetDecorations())

    def GetDecorations(self):
        return self.decorations

    def GetExportInformation(self):
        info = {}
        if self.formatChoice.GetSelection() == 0:
            info['method'] = 'sequence'
            info['directory'] = self.dirBrowse.GetValue()
            info['prefix'] = self.prefixCtrl.GetValue()
            info['format'] = self.imSeqFormatChoice.GetStringSelection()

        elif self.formatChoice.GetSelection() == 1:
            info['method'] = 'gif'
            info['file'] = self.gifBrowse.GetValue()

        elif self.formatChoice.GetSelection() == 2:
            info['method'] = 'swf'
            info['file'] = self.swfBrowse.GetValue()

        elif self.formatChoice.GetSelection() == 3:
            info['method'] = 'avi'
            info['file'] = self.aviBrowse.GetValue()
            info['encoding'] = self.encodingText.GetValue()
            info['options'] = self.optionsText.GetValue()

        return info

    def _updateListBox(self):
        self.listbox.Clear()
        names = {
            'time': _("Time stamp"),
            'image': _("Image"),
            'text': _("Text")}
        for decor in self.decorations:
            self.listbox.Append(names[decor['name']], clientData=decor)

    def _hideAll(self):
        self.hidevbox.Show(self.fontBox, False)
        self.hidevbox.Show(self.imageBox, False)
        self.hidevbox.Show(self.textBox, False)
        self.hidevbox.Show(self.posBox, False)
        self.hidevbox.Show(self.informBox, True)
        self.hidevbox.Layout()


class AnimSimpleLayerManager(SimpleLayerManager):
    """Simple layer manager for animation tool.
    Allows adding space-time dataset or series of maps.
    """

    def __init__(self, parent, layerList,
                 lmgrStyle=SIMPLE_LMGR_RASTER | SIMPLE_LMGR_VECTOR |
                 SIMPLE_LMGR_TB_TOP | SIMPLE_LMGR_STDS,
                 toolbarCls=AnimSimpleLmgrToolbar, modal=True):
        SimpleLayerManager.__init__(
            self, parent, layerList, lmgrStyle, toolbarCls, modal)
        self._3dActivated = False

    def OnAddStds(self, event):
        """Opens dialog for specifying temporal dataset.
        Dummy layer is added first."""
        layer = AnimLayer()
        layer.hidden = True
        self._layerList.AddLayer(layer)
        self.SetStdsProperties(layer)
        event.Skip()

    def SetStdsProperties(self, layer):
        dlg = AddTemporalLayerDialog(
            parent=self, layer=layer, volume=self._3dActivated)
        # first get hidden property, it's altered afterwards
        hidden = layer.hidden
        dlg.CenterOnParent()
        if dlg.ShowModal() == wx.ID_OK:
            layer = dlg.GetLayer()
            if hidden:
                signal = self.layerAdded
            else:
                signal = self.cmdChanged
            signal.emit(
                index=self._layerList.GetLayerIndex(layer),
                layer=layer)
        else:
            if hidden:
                self._layerList.RemoveLayer(layer)
        dlg.Destroy()
        self._update()
        self.anyChange.emit()

    def _layerChangeProperties(self, layer):
        """Opens new module dialog or recycles it."""
        if not hasattr(layer, 'maps'):
            GUI(parent=self, giface=None, modal=self._modal).ParseCommand(
                cmd=layer.cmd, completed=(self.GetOptData, layer, ''))
        else:
            self.SetStdsProperties(layer)

    def Activate3D(self, activate=True):
        """Activates/deactivates certain tool depending on 2D/3D view."""
        self._toolbar.EnableTools(['addRaster', 'addVector',
                                   'opacity', 'up', 'down'], not activate)
        self._3dActivated = activate


class AddTemporalLayerDialog(wx.Dialog):
    """Dialog for adding space-time dataset/ map series."""

    def __init__(self, parent, layer, volume=False,
                 title=_("Add space-time dataset layer")):
        wx.Dialog.__init__(self, parent=parent, title=title)

        self.layer = layer
        self._mapType = None
        self._name = None
        self._cmd = None

        self.tselect = Select(parent=self, type='strds')
        iconTheme = UserSettings.Get(
            group='appearance',
            key='iconTheme',
            subkey='type')
        bitmapPath = os.path.join(
            globalvar.ICONDIR,
            iconTheme,
            'layer-open.png')
        if os.path.isfile(bitmapPath) and os.path.getsize(bitmapPath):
            bitmap = wx.Bitmap(name=bitmapPath)
        else:
            bitmap = wx.ArtProvider.GetBitmap(
                id=wx.ART_MISSING_IMAGE, client=wx.ART_TOOLBAR)
        self.addManyMapsButton = wx.BitmapButton(self, bitmap=bitmap)
        self.addManyMapsButton.Bind(wx.EVT_BUTTON, self._onAddMaps)

        types = [('raster', _("Multiple raster maps")),
                 ('vector', _("Multiple vector maps")),
                 ('raster_3d', _("Multiple 3D raster maps")),
                 ('strds', _("Space time raster dataset")),
                 ('stvds', _("Space time vector dataset")),
                 ('str3ds', _("Space time 3D raster dataset"))]
        if not volume:
            del types[5]
            del types[2]
        self._types = dict(types)

        self.tchoice = wx.Choice(parent=self)
        for type_, text in types:
            self.tchoice.Append(text, clientData=type_)

        self.editBtn = wx.Button(parent=self, label='Set properties')

        self.okBtn = wx.Button(parent=self, id=wx.ID_OK)
        self.cancelBtn = wx.Button(parent=self, id=wx.ID_CANCEL)

        self.okBtn.Bind(wx.EVT_BUTTON, self._onOK)
        self.editBtn.Bind(wx.EVT_BUTTON, self._onProperties)
        self.tchoice.Bind(wx.EVT_CHOICE,
                          lambda evt: self._setType())
        self.tselect.Bind(wx.EVT_TEXT,
                          lambda evt: self._datasetChanged())

        if self.layer.mapType:
            self._setType(self.layer.mapType)
        else:
            self._setType('raster')
        if self.layer.name:
            self.tselect.SetValue(self.layer.name)
        if self.layer.cmd:
            self._cmd = self.layer.cmd

        self._layout()
        self.SetSize(self.GetBestSize())

    def _layout(self):
        mainSizer = wx.BoxSizer(wx.VERTICAL)
        bodySizer = wx.BoxSizer(wx.VERTICAL)
        typeSizer = wx.BoxSizer(wx.HORIZONTAL)
        selectSizer = wx.BoxSizer(wx.HORIZONTAL)
        typeSizer.Add(wx.StaticText(self, label=_("Input data type:")),
                      flag=wx.ALIGN_CENTER_VERTICAL)
        typeSizer.AddStretchSpacer()
        typeSizer.Add(self.tchoice)
        bodySizer.Add(typeSizer, flag=wx.EXPAND | wx.BOTTOM, border=5)

        selectSizer.Add(self.tselect, flag=wx.RIGHT |
                        wx.ALIGN_CENTER_VERTICAL, border=5)
        selectSizer.Add(self.addManyMapsButton, flag=wx.EXPAND)
        bodySizer.Add(selectSizer, flag=wx.BOTTOM, border=5)
        bodySizer.Add(self.editBtn, flag=wx.BOTTOM, border=5)
        mainSizer.Add(
            bodySizer,
            proportion=1,
            flag=wx.EXPAND | wx.ALL,
            border=10)

        btnSizer = wx.StdDialogButtonSizer()
        btnSizer.AddButton(self.okBtn)
        btnSizer.AddButton(self.cancelBtn)
        btnSizer.Realize()

        mainSizer.Add(btnSizer, proportion=0,
                      flag=wx.EXPAND | wx.ALL, border=10)

        self.SetSizer(mainSizer)
        mainSizer.Fit(self)

    def _datasetChanged(self):
        if self._name != self.tselect.GetValue():
            self._name = self.tselect.GetValue()
            self._cmd = None

    def _setType(self, typeName=None):
        if typeName:
            self.tchoice.SetStringSelection(self._types[typeName])
            self.tselect.SetType(typeName)
            if typeName in ('strds', 'stvds', 'str3ds'):
                self.tselect.SetType(typeName, multiple=False)
                self.addManyMapsButton.Disable()
            else:
                self.tselect.SetType(typeName, multiple=True)
                self.addManyMapsButton.Enable()
            self._mapType = typeName
            self.tselect.SetValue('')
        else:
            typeName = self.tchoice.GetClientData(self.tchoice.GetSelection())
            if typeName in ('strds', 'stvds', 'str3ds'):
                self.tselect.SetType(typeName, multiple=False)
                self.addManyMapsButton.Disable()
            else:
                self.tselect.SetType(typeName, multiple=True)
                self.addManyMapsButton.Enable()
            if typeName != self._mapType:
                self._cmd = None
                self._mapType = typeName
                self.tselect.SetValue('')

    def _createDefaultCommand(self):
        cmd = []
        if self._mapType in ('raster', 'strds'):
            cmd.append('d.rast')
        elif self._mapType in ('vector', 'stvds'):
            cmd.append('d.vect')
        elif self._mapType in ('raster_3d', 'str3ds'):
            cmd.append('d.rast3d')
        if self._name:
            if self._mapType in ('raster', 'vector', 'raster_3d'):
                cmd.append('map={name}'.format(name=self._name.split(',')[0]))
            else:
                try:
                    maps = getRegisteredMaps(self._name, etype=self._mapType)
                    if maps:
                        mapName, mapLayer = getNameAndLayer(maps[0])
                        cmd.append('map={name}'.format(name=mapName))
                except gcore.ScriptError as e:
                    GError(parent=self, message=str(e), showTraceback=False)
                    return None
        return cmd

    def _onAddMaps(self, event):
        dlg = MapLayersDialog(self, title=_("Select raster/vector maps."))
        dlg.applyAddingMapLayers.connect(
            lambda mapLayers: self.tselect.SetValue(
                ','.join(mapLayers)))
        if self._mapType == 'raster':
            index = 0
        elif self._mapType == 'vector':
            index = 2
        else:  # rast3d
            index = 1

        dlg.layerType.SetSelection(index)
        dlg.LoadMapLayers(dlg.GetLayerType(cmd=True),
                          dlg.mapset.GetStringSelection())
        dlg.CenterOnParent()
        if dlg.ShowModal() == wx.ID_OK:
            self.tselect.SetValue(','.join(dlg.GetMapLayers()))

        dlg.Destroy()

    def _onProperties(self, event):
        self._checkInput()
        if self._cmd:
            GUI(parent=self, show=True, modal=True).ParseCommand(
                cmd=self._cmd, completed=(self._getOptData, '', ''))

    def _checkInput(self):
        if not self.tselect.GetValue():
            GMessage(parent=self, message=_(
                "Please select maps or dataset first."))
            return

        if not self._cmd:
            self._cmd = self._createDefaultCommand()

    def _getOptData(self, dcmd, layer, params, propwin):
        if dcmd:
            self._cmd = dcmd

    def _onOK(self, event):
        self._checkInput()
        if self._cmd:
            try:
                self.layer.hidden = False
                self.layer.mapType = self._mapType
                self.layer.name = self._name
                self.layer.cmd = self._cmd
                event.Skip()
            except (GException, gcore.ScriptError) as e:
                GError(parent=self, message=str(e))

    def GetLayer(self):
        return self.layer


class PreferencesDialog(PreferencesBaseDialog):
    """Animation preferences dialog"""

    def __init__(self, parent, giface, title=_("Animation Tool settings"),
                 settings=UserSettings):
        PreferencesBaseDialog.__init__(
            self, parent=parent, giface=giface, title=title, settings=settings,
            size=(-1, 270))
        self.formatChanged = Signal('PreferencesDialog.formatChanged')

        self._timeFormats = ['%Y-%m-%d %H:%M:%S',  # 2013-12-29 11:16:26
                             '%Y-%m-%d',  # 2013-12-29
                             '%c',
                             # Sun Dec 29 11:16:26 2013 (locale-dependent)
                             '%x',  # 12/29/13 (locale-dependent)
                             '%X',  # 11:16:26 (locale-dependent)
                             '%b %d, %Y',  # Dec 29, 2013
                             '%B %d, %Y',  # December 29, 2013
                             '%B, %Y',  # December 2013
                             '%I:%M %p',  # 11:16 AM
                             '%I %p',  # 11 AM
                             ]
        self._format = None
        self._initFormat = self.settings.Get(group='animation', key='temporal',
                                             subkey='format')
        # create notebook pages
        self._createGeneralPage(self.notebook)
        self._createTemporalPage(self.notebook)

        self.SetMinSize(self.GetBestSize())
        self.SetSize(self.size)

    def _createGeneralPage(self, notebook):
        """Create notebook page for general settings"""
        panel = SP.ScrolledPanel(parent=notebook)
        panel.SetupScrolling(scroll_x=False, scroll_y=True)
        notebook.AddPage(page=panel, text=_("General"))

        border = wx.BoxSizer(wx.VERTICAL)
        sizer = wx.BoxSizer(wx.VERTICAL)
        gridSizer = wx.GridBagSizer(hgap=3, vgap=3)

        row = 0
        gridSizer.Add(
            wx.StaticText(
                parent=panel,
                label=_("Background color:")),
            flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL,
            pos=(
                row,
                0))
        color = csel.ColourSelect(
            parent=panel,
            colour=UserSettings.Get(
                group='animation',
                key='bgcolor',
                subkey='color'),
            size=globalvar.DIALOG_COLOR_SIZE)
        color.SetName('GetColour')
        self.winId['animation:bgcolor:color'] = color.GetId()

        gridSizer.Add(color, pos=(row, 1), flag=wx.ALIGN_RIGHT)

        row += 1
        gridSizer.Add(
            wx.StaticText(
                parent=panel,
                label=_("Number of parallel processes:")),
            flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL,
            pos=(
                row,
                0))
        # when running for the first time, set nprocs based on the number of
        # processes
        if UserSettings.Get(group='animation', key='nprocs',
                            subkey='value') == -1:
            UserSettings.Set(
                group='animation',
                key='nprocs',
                subkey='value',
                value=getCpuCount())
        nprocs = SpinCtrl(
            parent=panel,
            initial=UserSettings.Get(
                group='animation',
                key='nprocs',
                subkey='value'))
        nprocs.SetName('GetValue')
        self.winId['animation:nprocs:value'] = nprocs.GetId()

        gridSizer.Add(nprocs, pos=(row, 1), flag=wx.ALIGN_RIGHT)

        row += 1
        gridSizer.Add(
            wx.StaticText(
                parent=panel,
                label=_("Text foreground color:")),
            flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL,
            pos=(
                row,
                0))
        color = csel.ColourSelect(
            parent=panel,
            colour=UserSettings.Get(
                group='animation',
                key='font',
                subkey='fgcolor'),
            size=globalvar.DIALOG_COLOR_SIZE)
        color.SetName('GetColour')
        self.winId['animation:font:fgcolor'] = color.GetId()

        gridSizer.Add(color, pos=(row, 1), flag=wx.ALIGN_RIGHT)

        row += 1
        gridSizer.Add(
            wx.StaticText(
                parent=panel,
                label=_("Text background color:")),
            flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL,
            pos=(
                row,
                0))
        color = csel.ColourSelect(
            parent=panel,
            colour=UserSettings.Get(
                group='animation',
                key='font',
                subkey='bgcolor'),
            size=globalvar.DIALOG_COLOR_SIZE)
        color.SetName('GetColour')
        self.winId['animation:font:bgcolor'] = color.GetId()

        gridSizer.Add(color, pos=(row, 1), flag=wx.ALIGN_RIGHT)

        gridSizer.AddGrowableCol(1)
        sizer.Add(
            gridSizer,
            proportion=1,
            flag=wx.ALL | wx.EXPAND,
            border=3)
        border.Add(sizer, proportion=0, flag=wx.ALL | wx.EXPAND, border=3)
        panel.SetSizer(border)

        return panel

    def _createTemporalPage(self, notebook):
        """Create notebook page for temporal settings"""
        panel = SP.ScrolledPanel(parent=notebook)
        panel.SetupScrolling(scroll_x=False, scroll_y=True)
        notebook.AddPage(page=panel, text=_("Time"))

        border = wx.BoxSizer(wx.VERTICAL)
        sizer = wx.BoxSizer(wx.VERTICAL)
        gridSizer = wx.GridBagSizer(hgap=5, vgap=5)

        row = 0
        gridSizer.Add(
            wx.StaticText(
                parent=panel,
                label=_("Absolute time format:")),
            flag=wx.ALIGN_LEFT | wx.ALIGN_CENTER_VERTICAL,
            pos=(
                row,
                0))
        self.tempFormat = wx.ComboBox(parent=panel, name='GetValue')
        self.tempFormat.SetItems(self._timeFormats)
        self.tempFormat.SetValue(self._initFormat)
        self.winId['animation:temporal:format'] = self.tempFormat.GetId()
        gridSizer.Add(self.tempFormat, pos=(row, 1), flag=wx.ALIGN_RIGHT)
        self.infoTimeLabel = wx.StaticText(parent=panel)
        self.tempFormat.Bind(
            wx.EVT_COMBOBOX,
            lambda evt: self._setTimeFormat(
                self.tempFormat.GetValue()))
        self.tempFormat.Bind(
            wx.EVT_TEXT, lambda evt: self._setTimeFormat(
                self.tempFormat.GetValue()))
        self.tempFormat.SetToolTipString(
            _(
                "Click and then press key up or down to preview "
                "different date and time formats. "
                "Type custom format string."))
        row += 1
        gridSizer.Add(self.infoTimeLabel, pos=(row, 0), span=(1, 2),
                      flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT)
        self._setTimeFormat(self.tempFormat.GetValue())

        row += 1
        link = wx.HyperlinkCtrl(
            panel, id=wx.ID_ANY,
            label=_("Learn more about formatting options"),
            url="http://docs.python.org/2/library/datetime.html#"
            "strftime-and-strptime-behavior")
        link.SetNormalColour(
            wx.SystemSettings.GetColour(
                wx.SYS_COLOUR_GRAYTEXT))
        link.SetVisitedColour(
            wx.SystemSettings.GetColour(
                wx.SYS_COLOUR_GRAYTEXT))
        gridSizer.Add(link, pos=(row, 0), span=(1, 2),
                      flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT)

        row += 2
        noDataCheck = wx.CheckBox(
            panel, label=_("Display instances with no data"))
        noDataCheck.SetToolTipString(
            _(
                "When animating instant-based data which have irregular timestamps "
                "you can display 'no data frame' (checked option) or "
                "keep last frame."))
        noDataCheck.SetValue(
            self.settings.Get(
                group='animation',
                key='temporal',
                subkey=[
                    'nodata',
                    'enable']))
        self.winId['animation:temporal:nodata:enable'] = noDataCheck.GetId()
        gridSizer.Add(noDataCheck, pos=(row, 0), span=(1, 2),
                      flag=wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_LEFT)

        gridSizer.AddGrowableCol(1)
        sizer.Add(
            gridSizer,
            proportion=1,
            flag=wx.ALL | wx.EXPAND,
            border=3)
        border.Add(sizer, proportion=0, flag=wx.ALL | wx.EXPAND, border=3)
        panel.SetSizer(border)

        return panel

    def _setTimeFormat(self, formatString):
        now = datetime.datetime.now()
        try:
            label = datetime.datetime.strftime(now, formatString)
            self._format = formatString
        except ValueError:
            label = _("Invalid")
        self.infoTimeLabel.SetLabel(label)
        self.infoTimeLabel.GetContainingSizer().Layout()

    def _updateSettings(self):
        self.tempFormat.SetValue(self._format)
        PreferencesBaseDialog._updateSettings(self)
        if self._format != self._initFormat:
            self.formatChanged.emit()
        return True


def test():
    import wx.lib.inspection

    app = wx.App()

#    testTemporalLayer()
#    testAnimLmgr()
    testAnimInput()
    # wx.lib.inspection.InspectionTool().Show()

    app.MainLoop()


def testAnimInput():
    anim = AnimationData()
    anim.SetDefaultValues(animationIndex=0, windowIndex=0)

    dlg = InputDialog(parent=None, mode='add', animationData=anim)
    dlg.Show()


def testAnimEdit():
    anim = AnimationData()
    anim.SetDefaultValues(animationIndex=0, windowIndex=0)

    dlg = EditDialog(parent=None, animationData=[anim])
    dlg.Show()


def testExport():
    dlg = ExportDialog(parent=None, temporal=TemporalMode.TEMPORAL,
                       timeTick=200)
    if dlg.ShowModal() == wx.ID_OK:
        print dlg.GetDecorations()
        print dlg.GetExportInformation()
        dlg.Destroy()
    else:
        dlg.Destroy()


def testTemporalLayer():
    frame = wx.Frame(None)
    frame.Show()
    layer = AnimLayer()
    dlg = AddTemporalLayerDialog(parent=frame, layer=layer)
    if dlg.ShowModal() == wx.ID_OK:
        layer = dlg.GetLayer()
        print layer.name, layer.cmd, layer.mapType
        dlg.Destroy()
    else:
        dlg.Destroy()


def testAnimLmgr():
    from core.layerlist import LayerList

    frame = wx.Frame(None)
    mgr = AnimSimpleLayerManager(parent=frame, layerList=LayerList())
    frame.mgr = mgr
    frame.Show()


if __name__ == '__main__':
    gcore.set_raise_on_error(True)
    test()
