"""
@package wxplot.histogram

@brief Histogramming using PyPlot

Classes:
 - histogram::HistogramPlotFrame
 - histogram::HistogramPlotToolbar

(C) 2011-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 Michael Barton, Arizona State University
"""

import sys

import wx

import grass.script as gs
from wx.lib import plot
from gui_core.wrap import StockCursor
from gui_core.toolbars import BaseToolbar, BaseIcons
from wxplot.base import BasePlotFrame, PlotIcons
from wxplot.dialogs import HistRasterDialog, PlotStatsFrame
from core.gcmd import RunCommand, GException, GError


class HistogramPlotFrame(BasePlotFrame):
    """Mainframe for displaying histogram of raster map. Uses wx.lib.plot."""

    def __init__(
        self,
        parent,
        giface,
        id=wx.ID_ANY,
        style=wx.DEFAULT_FRAME_STYLE,
        size=wx.Size(700, 400),
        rasterList=[],
        **kwargs,
    ):
        BasePlotFrame.__init__(self, parent, giface=giface, size=size, **kwargs)

        self.toolbar = HistogramPlotToolbar(parent=self)
        # workaround for http://trac.wxwidgets.org/ticket/13888
        if sys.platform != "darwin":
            self.SetToolBar(self.toolbar)
        self.SetTitle(_("Histogram Tool"))

        #
        # Init variables
        #
        self.rasterList = rasterList
        self.plottype = "histogram"
        self.group = ""
        self.ptitle = _("Histogram of")  # title of window
        self.xlabel = _("Raster cell values")  # default X-axis label
        self.ylabel = _("Cell counts")  # default Y-axis label
        self.maptype = "raster"  # default type of histogram to plot
        self.histtype = "count"
        self.bins = 255
        self.colorList = [
            "blue",
            "green",
            "red",
            "yellow",
            "magenta",
            "cyan",
            "aqua",
            "black",
            "grey",
            "orange",
            "brown",
            "purple",
            "violet",
            "indigo",
        ]

        self._initOpts()

        if (
            len(self.rasterList) > 0
        ):  # set raster name(s) from layer manager if a map is selected
            self.raster = self.InitRasterOpts(self.rasterList, self.plottype)
            wx.CallAfter(self.OnCreateHist, None)
        else:
            self.raster = {}

    def _initOpts(self):
        """Initialize plot options"""
        self.InitPlotOpts("histogram")

    def OnCreateHist(self, event):
        """Main routine for creating a histogram. Uses r.stats to
        create a list of cell value and count/percent/area pairs. This is passed to
        plot to create a line graph of the histogram.
        """
        self.SetCursor(StockCursor(wx.CURSOR_ARROW))

        self.SetGraphStyle()
        wx.BeginBusyCursor()
        wx.SafeYield()
        self.SetupHistogram()
        p = self.CreatePlotList()
        self.DrawPlot(p)
        wx.EndBusyCursor()

    def OnSelectRaster(self, event):
        """Select raster map(s) to profile"""
        dlg = HistRasterDialog(parent=self)

        if dlg.ShowModal() == wx.ID_OK:
            self.rasterList = dlg.rasterList
            self.group = dlg.group
            self.bins = dlg.bins
            self.histtype = dlg.histtype
            self.maptype = dlg.maptype
            self.raster = self.InitRasterOpts(self.rasterList, self.plottype)

            # plot histogram
            if len(self.rasterList) > 0:
                self.OnCreateHist(event=None)

        dlg.Destroy()

    def SetupHistogram(self):
        """Build data list for plotting each raster"""

        #
        # populate raster dictionary
        #
        if len(self.rasterList) == 0:
            return  # nothing selected

        for r in self.rasterList:
            self.raster[r]["datalist"] = self.CreateDatalist(r)

        #
        # update title
        #
        if self.maptype == "group":
            self.ptitle = _("Histogram of image group <%s>") % self.group
        elif len(self.rasterList) == 1:
            self.ptitle = _("Histogram of raster map <%s>") % self.rasterList[0]
        else:
            self.ptitle = _("Histogram of selected raster maps")

        #
        # set xlabel based on first raster map in list to be histogrammed
        #
        units = self.raster[self.rasterList[0]]["units"]
        if units not in {"", "(none)"} and units is not None:
            self.xlabel = _("Raster cell values %s") % units
        else:
            self.xlabel = _("Raster cell values")

        #
        # set ylabel from self.histtype
        #
        if self.histtype == "count":
            self.ylabel = _("Cell counts")
        if self.histtype == "percent":
            self.ylabel = _("Percent of total cells")
        if self.histtype == "area":
            self.ylabel = _("Area")

    def CreateDatalist(self, raster):
        """Build a list of cell value, frequency pairs for histogram
        frequency can be in cell counts, percents, or area
        """
        datalist = []

        if self.histtype == "count":
            freqflag = "cn"
        if self.histtype == "percent":
            freqflag = "pn"
        if self.histtype == "area":
            freqflag = "an"

        try:
            ret = RunCommand(
                "r.stats",
                parent=self,
                input=raster,
                flags=freqflag,
                nsteps=self.bins,
                sep=",",
                quiet=True,
                read=True,
            )

            if not ret:
                return datalist

            for line in ret.splitlines():
                cellval, histval = line.strip().split(",")
                histval = histval.strip()
                if self.raster[raster]["datatype"] != "CELL":
                    if cellval[0] == "-":
                        cellval = "-" + cellval.split("-")[1]
                    else:
                        cellval = cellval.split("-")[0]

                if self.histtype == "percent":
                    histval = histval.rstrip("%")

                datalist.append((cellval, histval))

            return datalist
        except GException as e:
            GError(parent=self, message=e.value)
            return None

    def CreatePlotList(self):
        """Make list of elements to plot"""

        # graph the cell value, frequency pairs for the histogram
        self.plotlist = []

        for r in self.rasterList:
            if len(self.raster[r]["datalist"]) > 0:
                col = wx.Colour(
                    self.raster[r]["pcolor"][0],
                    self.raster[r]["pcolor"][1],
                    self.raster[r]["pcolor"][2],
                    255,
                )

                self.raster[r]["pline"] = plot.PolyLine(
                    self.raster[r]["datalist"],
                    colour=col,
                    width=self.raster[r]["pwidth"],
                    style=self.linestyledict[self.raster[r]["pstyle"]],
                    legend=self.raster[r]["plegend"],
                )

                self.plotlist.append(self.raster[r]["pline"])

        if len(self.plotlist) > 0:
            return self.plotlist
        return None

    def Update(self):
        """Update histogram after changing options"""
        self.SetGraphStyle()
        p = self.CreatePlotList()
        self.DrawPlot(p)

    def OnStats(self, event):
        """Displays regression information in messagebox"""
        message = []
        title = _("Statistics for Map(s) Histogrammed")

        for rast in self.rasterList:
            ret = gs.read_command("r.univar", map=rast, flags="e", quiet=True)
            stats = _("Statistics for raster map <%s>") % rast + ":\n%s\n" % ret
            message.append(stats)

        stats = PlotStatsFrame(self, id=wx.ID_ANY, message=message, title=title)

        if stats.Show() == wx.ID_CLOSE:
            stats.Destroy()


class HistogramPlotToolbar(BaseToolbar):
    """Toolbar for histogramming raster map"""

    def __init__(self, parent):
        BaseToolbar.__init__(self, parent)

        # workaround for http://trac.wxwidgets.org/ticket/13888
        if sys.platform == "darwin":
            parent.SetToolBar(self)

        self.InitToolbar(self._toolbarData())

        # realize the toolbar
        self.Realize()

    def _toolbarData(self):
        """Toolbar data"""
        return self._getToolbarData(
            (
                (
                    ("addraster", BaseIcons["addRast"].label),
                    BaseIcons["addRast"],
                    self.parent.OnSelectRaster,
                ),
                (None,),
                (
                    ("draw", PlotIcons["draw"].label),
                    PlotIcons["draw"],
                    self.parent.OnCreateHist,
                ),
                (
                    ("erase", BaseIcons["erase"].label),
                    BaseIcons["erase"],
                    self.parent.OnErase,
                ),
                (
                    ("drag", BaseIcons["pan"].label),
                    BaseIcons["pan"],
                    self.parent.OnDrag,
                ),
                (
                    ("zoom", BaseIcons["zoomIn"].label),
                    BaseIcons["zoomIn"],
                    self.parent.OnZoom,
                ),
                (
                    ("unzoom", BaseIcons["zoomExtent"].label),
                    BaseIcons["zoomExtent"],
                    self.parent.OnRedraw,
                ),
                (None,),
                (
                    ("statistics", PlotIcons["statistics"].label),
                    PlotIcons["statistics"],
                    self.parent.OnStats,
                ),
                (
                    ("image", BaseIcons["saveFile"].label),
                    BaseIcons["saveFile"],
                    self.parent.SaveToFile,
                ),
                (
                    ("print", BaseIcons["print"].label),
                    BaseIcons["print"],
                    self.parent.PrintMenu,
                ),
                (None,),
                (
                    ("settings", PlotIcons["options"].label),
                    PlotIcons["options"],
                    self.parent.PlotOptionsMenu,
                ),
                (
                    ("quit", PlotIcons["quit"].label),
                    PlotIcons["quit"],
                    self.parent.OnQuit,
                ),
            )
        )
