"""
@package rlisetup.sampling_frame

@brief r.li.setup - draw sample frame

Classes:
 - sampling_frame::RLiSetupMapPanel
 - sampling_frame::RLiSetupToolbar
 - sampling_frame::GraphicsSetItem

(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 wx.aui


# start new import
import tempfile
from core.gcmd import RunCommand
import grass.script.core as grass
from core import gcmd

from core.giface import StandaloneGrassInterface
from mapwin.base import MapWindowProperties
from mapwin.buffered import BufferedMapWindow
from core.render import Map
from gui_core.toolbars import BaseToolbar, BaseIcons, ToolSwitcher
from icons.icon import MetaIcon
from core.gcmd import GMessage
from grass.pydispatch.signal import Signal
from grass.pydispatch.errors import DispatcherKeyError

from .functions import SamplingType, checkMapExists


class Circle:
    def __init__(self, pt, r):
        self.point = pt
        self.radius = r


class MaskedArea:
    def __init__(self, region, raster, radius):
        self.region = region
        self.raster = raster
        self.radius = radius


class RLiSetupMapPanel(wx.Panel):
    """Panel with mapwindow used in r.li.setup"""

    def __init__(self, parent, samplingType, icon=None, map_=None):
        wx.Panel.__init__(self, parent=parent)

        self.mapWindowProperties = MapWindowProperties()
        self.mapWindowProperties.setValuesFromUserSettings()
        giface = StandaloneGrassInterface()
        self.samplingtype = samplingType
        self.parent = parent

        if map_:
            self.map_ = map_
        else:
            self.map_ = Map()
        self.map_.region = self.map_.GetRegion()

        self._mgr = wx.aui.AuiManager(self)
        self.mapWindow = BufferedMapWindow(
            parent=self,
            giface=giface,
            Map=self.map_,
            properties=self.mapWindowProperties,
        )
        self._mgr.AddPane(
            self.mapWindow,
            wx.aui.AuiPaneInfo()
            .CentrePane()
            .Dockable(True)
            .BestSize((-1, -1))
            .Name("mapwindow")
            .CloseButton(False)
            .DestroyOnClose(True)
            .Layer(0),
        )
        self._toolSwitcher = ToolSwitcher()
        self._toolSwitcher.toggleToolChanged.connect(self._onToolChanged)
        self.toolbar = RLiSetupToolbar(self, self._toolSwitcher)

        self.catId = 1

        self._mgr.AddPane(
            self.toolbar,
            wx.aui.AuiPaneInfo()
            .Name("maptoolbar")
            .Caption(_("Map Toolbar"))
            .ToolbarPane()
            .Left()
            .Name("mapToolbar")
            .CloseButton(False)
            .Layer(1)
            .Gripper(False)
            .BestSize(self.toolbar.GetBestSize()),
        )
        self._mgr.Update()

        if self.samplingtype == SamplingType.REGIONS:
            self.afterRegionDrawn = Signal("RLiSetupMapPanel.afterRegionDrawn")
            self._registeredGraphics = self.mapWindow.RegisterGraphicsToDraw(
                graphicsType="line"
            )
        elif self.samplingtype in {SamplingType.MUNITSR, SamplingType.MMVWINR}:
            self.sampleFrameChanged = Signal("RLiSetupMapPanel.sampleFrameChanged")
            self._registeredGraphics = self.mapWindow.RegisterGraphicsToDraw(
                graphicsType="rectangle"
            )
        elif self.samplingtype in {SamplingType.MUNITSC, SamplingType.MMVWINC}:
            self.afterCircleDrawn = Signal("RLiSetupMapPanel.afterCircleDrawn")
            self._registeredGraphics = self.mapWindow.RegisterGraphicsToDraw(
                graphicsType="line"
            )
        else:
            self.sampleFrameChanged = Signal("RLiSetupMapPanel.sampleFrameChanged")
            self._registeredGraphics = self.mapWindow.RegisterGraphicsToDraw(
                graphicsType="rectangle"
            )

        self._registeredGraphics.AddPen(
            "rlisetup", wx.Pen(wx.GREEN, width=2, style=wx.SOLID)
        )
        self._registeredGraphics.AddItem(
            coords=[[0, 0], [0, 0]], penName="rlisetup", hide=True
        )

        if self.samplingtype != SamplingType.VECT:
            self.toolbar.SelectDefault()

    def GetMap(self):
        return self.map_

    def OnPan(self, event):
        """Panning, set mouse to drag."""
        self.mapWindow.SetModePan()

    def OnZoomIn(self, event):
        """Zoom in the map."""
        self.mapWindow.SetModeZoomIn()

    def OnZoomOut(self, event):
        """Zoom out the map."""
        self.mapWindow.SetModeZoomOut()

    def OnZoomToMap(self, event):
        layers = self.map_.GetListOfLayers()
        self.mapWindow.ZoomToMap(layers=layers, ignoreNulls=False, render=True)

    def OnDrawRadius(self, event):
        """Start draw mode"""
        self.mapWindow.mouse["use"] = "None"
        self.mapWindow.mouse["box"] = "line"
        self.mapWindow.pen = wx.Pen(colour=wx.RED, width=1, style=wx.SHORT_DASH)
        self.mapWindow.SetNamedCursor("cross")
        self.mapWindow.mouseLeftUp.connect(self._radiusDrawn)

    def OnDigitizeRegion(self, event):
        """Start draw mode"""
        self.mapWindow.mouse["use"] = "None"
        self.mapWindow.mouse["box"] = "line"
        self.mapWindow.pen = wx.Pen(colour=wx.RED, width=1, style=wx.SHORT_DASH)
        self.mapWindow.SetNamedCursor("cross")
        self.mapWindow.mouseLeftUp.connect(self._lineSegmentDrawn)
        self.mapWindow.mouseDClick.connect(self._mouseDbClick)

        self._registeredGraphics.GetItem(0).SetCoords([])

    def OnDraw(self, event):
        """Start draw mode"""
        self.mapWindow.mouse["use"] = "None"
        self.mapWindow.mouse["box"] = "box"
        self.mapWindow.pen = wx.Pen(colour=wx.RED, width=2, style=wx.SHORT_DASH)
        self.mapWindow.SetNamedCursor("cross")
        self.mapWindow.mouseLeftUp.connect(self._rectangleDrawn)

    def _lineSegmentDrawn(self, x, y):
        item = self._registeredGraphics.GetItem(0)
        coords = item.GetCoords()
        if len(coords) == 0:
            coords.extend([self.mapWindow.Pixel2Cell(self.mapWindow.mouse["begin"])])
        coords.extend([[x, y]])

        item.SetCoords(coords)
        item.SetPropertyVal("hide", False)
        self.mapWindow.ClearLines()
        self._registeredGraphics.Draw()

    def _mouseDbClick(self, x, y):
        item = self._registeredGraphics.GetItem(0)
        coords = item.GetCoords()
        coords.extend([[x, y]])
        item.SetCoords(coords)
        item.SetPropertyVal("hide", False)
        self.mapWindow.ClearLines()
        self._registeredGraphics.Draw()
        self.createRegion()

    def createRegion(self):
        dlg = wx.TextEntryDialog(
            None, "Name of sample region", "Create region", "region" + str(self.catId)
        )
        ret = dlg.ShowModal()
        while True:
            if ret != wx.ID_OK:
                self.nextRegion(next=False)
                break

            raster = dlg.GetValue()
            if not checkMapExists(raster):
                dlg.Destroy()
                marea = self.writeArea(
                    self._registeredGraphics.GetItem(0).GetCoords(), raster
                )
                self.nextRegion(next=True, area=marea)
                break

            GMessage(
                parent=self,
                message=_("The raster file %s already exists, please change name")
                % raster,
            )
            ret = dlg.ShowModal()

    def nextRegion(self, next=True, area=None):
        self.mapWindow.ClearLines()
        item = self._registeredGraphics.GetItem(0)
        item.SetCoords([])
        item.SetPropertyVal("hide", True)

        layers = self.map_.GetListOfLayers()
        self.mapWindow.ZoomToMap(layers=layers, ignoreNulls=False, render=True)
        if next is True:
            self.afterRegionDrawn.emit(marea=area)
        else:
            gcmd.GMessage(
                parent=self.parent,
                message=_("Raster map not created. Please redraw region."),
            )

    def writeArea(self, coords, rasterName):
        with tempfile.NamedTemporaryFile(delete=False) as polyfile:
            polyfile.write("AREA\n")
            for coor in coords:
                east, north = coor
                point = " %s %s\n" % (east, north)
                polyfile.write(point)
            catbuf = "=%d a\n" % self.catId
            polyfile.write(catbuf)
            self.catId += 1

        region_settings = grass.parse_command("g.region", flags="p", delimiter=":")
        pname = polyfile.name.split("/")[-1]
        tmpraster = "rast_" + pname
        tmpvector = "vect_" + pname
        wx.BeginBusyCursor()
        wx.GetApp().Yield()
        RunCommand(
            "r.in.poly",
            input=polyfile.name,
            output=tmpraster,
            rows=region_settings["rows"],
            overwrite=True,
        )

        RunCommand(
            "r.to.vect", input=tmpraster, output=tmpvector, type="area", overwrite=True
        )

        RunCommand(
            "v.to.rast", input=tmpvector, output=rasterName, value=1, use="value"
        )
        wx.EndBusyCursor()
        grass.use_temp_region()
        grass.run_command("g.region", vector=tmpvector)
        region = grass.region()

        marea = MaskedArea(region, rasterName)

        RunCommand("g.remove", flags="f", type="raster", name=tmpraster)
        RunCommand("g.remove", flags="f", type="vector", name=tmpvector)

        os.unlink(polyfile.name)
        return marea

    def _onToolChanged(self):
        """Helper function to disconnect drawing"""
        try:
            self.mapWindow.mouseLeftUp.disconnect(self._rectangleDrawn)
            self.mapWindow.mouseLeftUp.disconnect(self._radiusDrawn)
            self.mapWindow.mouseMoving.disconnect(self._mouseMoving)
            self.mapWindow.mouseLeftDown.disconnect(self._mouseLeftDown)
            self.mapWindow.mouseDClick.disconnect(self._mouseDbClick)
        except DispatcherKeyError:
            pass

    def _radiusDrawn(self, x, y):
        """When drawing finished, get region values"""
        mouse = self.mapWindow.mouse
        p1 = mouse["begin"]
        p2 = mouse["end"]
        dist, (north, east) = self.mapWindow.Distance(p1, p2, False)
        circle = Circle(p1, dist)
        self.mapWindow.ClearLines()
        self.mapWindow.pdcTmp.SetBrush(wx.Brush(wx.CYAN, wx.TRANSPARENT))
        pen = wx.Pen(colour=wx.RED, width=2)
        self.mapWindow.pdcTmp.SetPen(pen)
        self.mapWindow.pdcTmp.DrawCircle(
            circle.point[0], circle.point[1], circle.radius
        )
        self._registeredGraphics.Draw()
        self.createCircle(circle)

    def createCircle(self, c):
        dlg = wx.TextEntryDialog(
            None,
            "Name of sample circle region",
            "Create circle region",
            "circle" + str(self.catId),
        )
        ret = dlg.ShowModal()
        while True:
            if ret != wx.ID_OK:
                self.nextCircle(next=False)
                break

            raster = dlg.GetValue()
            if not checkMapExists(raster):
                dlg.Destroy()
                circle = self.writeCircle(c, raster)
                self.nextCircle(next=True, circle=circle)
                break

            GMessage(
                parent=self,
                message=_("The raster file %s already exists, please change name")
                % raster,
            )
            ret = dlg.ShowModal()

    def nextCircle(self, next=True, circle=None):
        self.mapWindow.ClearLines()
        item = self._registeredGraphics.GetItem(0)
        item.SetPropertyVal("hide", True)
        layers = self.map_.GetListOfLayers()
        self.mapWindow.ZoomToMap(layers=layers, ignoreNulls=False, render=True)
        if next is True:
            self.afterCircleDrawn.emit(region=circle)
        else:
            gcmd.GMessage(
                parent=self.parent,
                message=_("Raster map not created. redraw region again."),
            )

    def writeCircle(self, circle, rasterName):
        coords = self.mapWindow.Pixel2Cell(circle.point)
        RunCommand(
            "r.circle",
            output=rasterName,
            max=circle.radius,
            coordinate=coords,
            flags="b",
        )
        grass.use_temp_region()
        grass.run_command("g.region", zoom=rasterName)
        region = grass.region()
        return MaskedArea(region, rasterName, circle.radius)

    def _rectangleDrawn(self):
        """When drawing finished, get region values"""
        mouse = self.mapWindow.mouse
        item = self._registeredGraphics.GetItem(0)
        p1 = self.mapWindow.Pixel2Cell(mouse["begin"])
        p2 = self.mapWindow.Pixel2Cell(mouse["end"])
        item.SetCoords([p1, p2])
        region = {
            "n": max(p1[1], p2[1]),
            "s": min(p1[1], p2[1]),
            "w": min(p1[0], p2[0]),
            "e": max(p1[0], p2[0]),
        }
        item.SetPropertyVal("hide", False)
        self.mapWindow.ClearLines()
        self._registeredGraphics.Draw()
        if self.samplingtype in {SamplingType.MUNITSR, SamplingType.MMVWINR}:
            dlg = wx.MessageDialog(
                self,
                "Is this area ok?",
                "select sampling unit",
                wx.YES_NO | wx.ICON_QUESTION,
            )
            ret = dlg.ShowModal()
            if ret == wx.ID_YES:
                grass.use_temp_region()
                grass.run_command(
                    "g.region",
                    n=region["n"],
                    s=region["s"],
                    e=region["e"],
                    w=region["w"],
                )
                tregion = grass.region()
                self.sampleFrameChanged.emit(region=tregion)
                self.mapWindow.ClearLines()
                item = self._registeredGraphics.GetItem(0)
                item.SetPropertyVal("hide", True)
                layers = self.map_.GetListOfLayers()
                self.mapWindow.ZoomToMap(layers=layers, ignoreNulls=False, render=True)
            else:
                self.nextRegion(next=False)
            dlg.Destroy()

        elif self.samplingtype != SamplingType.WHOLE:
            # When drawing finished, get region values
            self.sampleFrameChanged.emit(region=region)


icons = {
    "draw": MetaIcon(
        img="edit",
        label=_("Draw sampling frame"),
        desc=_("Draw sampling frame by clicking and dragging"),
    ),
    "digitizeunit": MetaIcon(
        img="edit",
        label=_("Draw sampling rectangle"),
        desc=_("Draw sampling rectangle by clicking and dragging"),
    ),
    "digitizeunitc": MetaIcon(
        img="line-create",
        label=_("Draw sampling circle"),
        desc=_("Draw sampling circle radius by clicking and dragging"),
    ),
    "digitizeregion": MetaIcon(
        img="polygon-create",
        label=_("Draw sampling region"),
        desc=_("Draw sampling region by polygon. Right Double click to end drawing"),
    ),
}


class RLiSetupToolbar(BaseToolbar):
    """IClass toolbar"""

    def __init__(self, parent, toolSwitcher):
        """RLiSetup toolbar constructor"""

        BaseToolbar.__init__(
            self, parent, toolSwitcher, style=wx.NO_BORDER | wx.TB_VERTICAL
        )

        self.InitToolbar(self._toolbarData())

        if self.parent.samplingtype == SamplingType.REGIONS:
            self._default = self.digitizeregion
        elif self.parent.samplingtype in {SamplingType.MUNITSR, SamplingType.MMVWINR}:
            self._default = self.digitizeunit
        elif self.parent.samplingtype in {SamplingType.MUNITSC, SamplingType.MMVWINC}:
            self._default = self.digitizeunitc
        elif self.parent.samplingtype == SamplingType.VECT:
            self._default = None
        else:
            self._default = self.draw

        for tool in (self._default, self.pan, self.zoomIn, self.zoomOut):
            if tool:
                self.toolSwitcher.AddToolToGroup(
                    group="mouseUse", toolbar=self, tool=tool
                )

        # realize the toolbar
        self.Realize()

    def _toolbarData(self):
        """Toolbar data"""
        if self.parent.samplingtype == SamplingType.REGIONS:
            drawTool = (
                ("digitizeregion", icons["digitizeregion"].label),
                icons["digitizeregion"],
                self.parent.OnDigitizeRegion,
                wx.ITEM_CHECK,
            )
        elif self.parent.samplingtype in {SamplingType.MUNITSR, SamplingType.MMVWINR}:
            drawTool = (
                ("digitizeunit", icons["digitizeunit"].label),
                icons["digitizeunit"],
                self.parent.OnDraw,
                wx.ITEM_CHECK,
            )
        elif self.parent.samplingtype in {SamplingType.MUNITSC, SamplingType.MMVWINC}:
            drawTool = (
                ("digitizeunitc", icons["digitizeunitc"].label),
                icons["digitizeunitc"],
                self.parent.OnDrawRadius,
                wx.ITEM_CHECK,
            )
        else:
            drawTool = (
                ("draw", icons["draw"].label),
                icons["draw"],
                self.parent.OnDraw,
                wx.ITEM_CHECK,
            )
        if self.parent.samplingtype == SamplingType.VECT:
            return self._getToolbarData(
                (
                    (
                        ("pan", BaseIcons["pan"].label),
                        BaseIcons["pan"],
                        self.parent.OnPan,
                        wx.ITEM_CHECK,
                    ),
                    (
                        ("zoomIn", BaseIcons["zoomIn"].label),
                        BaseIcons["zoomIn"],
                        self.parent.OnZoomIn,
                        wx.ITEM_CHECK,
                    ),
                    (
                        ("zoomOut", BaseIcons["zoomOut"].label),
                        BaseIcons["zoomOut"],
                        self.parent.OnZoomOut,
                        wx.ITEM_CHECK,
                    ),
                    (
                        ("zoomExtent", BaseIcons["zoomExtent"].label),
                        BaseIcons["zoomExtent"],
                        self.parent.OnZoomToMap,
                    ),
                )
            )
        return self._getToolbarData(
            (
                drawTool,
                (None,),
                (
                    ("pan", BaseIcons["pan"].label),
                    BaseIcons["pan"],
                    self.parent.OnPan,
                    wx.ITEM_CHECK,
                ),
                (
                    ("zoomIn", BaseIcons["zoomIn"].label),
                    BaseIcons["zoomIn"],
                    self.parent.OnZoomIn,
                    wx.ITEM_CHECK,
                ),
                (
                    ("zoomOut", BaseIcons["zoomOut"].label),
                    BaseIcons["zoomOut"],
                    self.parent.OnZoomOut,
                    wx.ITEM_CHECK,
                ),
                (
                    ("zoomExtent", BaseIcons["zoomExtent"].label),
                    BaseIcons["zoomExtent"],
                    self.parent.OnZoomToMap,
                ),
            )
        )
