# -*- coding: utf-8 -*-

from __future__ import annotations

import warnings
from typing import TYPE_CHECKING

from guidata.dataset import (
    BeginGroup,
    ChoiceItem,
    ColorItem,
    DataSet,
    EndGroup,
    FloatItem,
    ObjectItem,
    StringItem,
)
from guidata.dataset.qtitemwidgets import DataSetWidget
from guidata.dataset.qtwidgets import DataSetEditLayout
from qwt import QwtPlot

from plotpy.config import _
from plotpy.constants import X_BOTTOM, Y_LEFT, Y_RIGHT
from plotpy.styles.base import FontItem

if TYPE_CHECKING:
    from qwt import QwtPlotItem, QwtScaleDiv

    from plotpy.items import BaseImageItem
    from plotpy.plot import BasePlot


class AxeStyleParam(DataSet):
    """Style parameters for an axis."""

    title = StringItem(_("Title"), default="")
    unit = StringItem(_("Unit"), default="")
    color = ColorItem(_("Color"), default="black").set_pos(col=1)
    title_font = FontItem(_("Title font"))
    ticks_font = FontItem(_("Values font"))


class AxisParam(DataSet):
    """Scale parameters for an axis."""

    scale = ChoiceItem(
        _("Scale"), [("lin", _("linear")), ("log", _("logarithmic"))], default="lin"
    )
    vmin = FloatItem("Min", help=_("Lower axis limit"))
    vmax = FloatItem("Max", help=_("Upper axis limit"))

    def update_param(self, plot: BasePlot, axis_id: int) -> None:
        """
        Update the parameters of the axis.

        Args:
            plot: The plot from which to update the parameters.
            axis_id: The axis ID to update.
        """
        self.scale = plot.get_axis_scale(axis_id)
        axis: QwtScaleDiv = plot.axisScaleDiv(axis_id)
        self.vmin = axis.lowerBound()
        self.vmax = axis.upperBound()

    def update_axis(self, plot: BasePlot, axis_id: int) -> None:
        """
        Update the axis with the parameters.

        Args:
            plot: The plot to update.
            axis_id: The axis ID to update.
        """
        plot.enableAxis(axis_id, True)
        plot.set_axis_scale(axis_id, self.scale, autoscale=False)
        plot.setAxisScale(axis_id, self.vmin, self.vmax)
        plot.disable_unused_axes()
        plot.SIG_AXIS_PARAMETERS_CHANGED.emit(axis_id)


class AxisItemWidget(DataSetWidget):
    """Widget for the axis item."""

    klass = AxisParam


class AxisItem(ObjectItem):
    """Item for an axis."""

    klass = AxisParam


DataSetEditLayout.register(AxisItem, AxisItemWidget)


class AxesParam(DataSet):
    """Parameters for the axes of a plot."""

    xaxis_id = ChoiceItem(
        _("X-axis position"),
        [(QwtPlot.xBottom, _("bottom")), (QwtPlot.xTop, _("top"))],
        default=QwtPlot.xBottom,
    )
    xaxis = AxisItem(_("X Axis"))
    yaxis_id = ChoiceItem(
        _("Y-axis position"),
        [(QwtPlot.yLeft, _("left")), (QwtPlot.yRight, _("right"))],
        default=QwtPlot.yLeft,
    )
    yaxis = AxisItem(_("Y Axis"))

    def update_param(self, item: QwtPlotItem) -> None:
        """
        Update the parameters of the axes.

        Args:
            item: The plot item from which to update the parameters.
        """
        plot: BasePlot = item.plot()
        self.xaxis: AxisParam
        self.yaxis: AxisParam
        self.xaxis_id = item.xAxis()
        self.xaxis.update_param(plot, self.xaxis_id)
        self.yaxis_id = item.yAxis()
        self.yaxis.update_param(plot, self.yaxis_id)

    def update_item(self, item: QwtPlotItem) -> None:
        """
        Update the axes with the parameters.

        Args:
            item: The plot item to update.
        """
        plot: BasePlot = item.plot()
        self.xaxis: AxisParam
        self.yaxis: AxisParam
        plot.grid.setAxes(self.xaxis_id, self.yaxis_id)
        item.setXAxis(self.xaxis_id)
        self.xaxis.update_axis(plot, self.xaxis_id)
        item.setYAxis(self.yaxis_id)
        self.yaxis.update_axis(plot, self.yaxis_id)

    # TODO: remove this method in a future release
    def update_axes(self, obj: QwtPlotItem) -> None:
        """Update object from parameters. Deprecated, use update_item instead.

        Args:
            item: The plot item to update.
        """
        warnings.warn(
            "`AxesParam.update_axes` method is deprecated and will be removed "
            "in a future release. Please use `update_item` instead.",
            DeprecationWarning,
            stacklevel=2,
        )
        self.update_item(obj)


class ImageAxesParam(DataSet):
    """Parameters for the axes of an image plot."""

    xparams = BeginGroup(_("X Axis"))
    xmin = FloatItem("x|min", help=_("Lower x-axis limit"))
    xmax = FloatItem("x|max", help=_("Upper x-axis limit"))
    _xparams = EndGroup("end X")
    yparams = BeginGroup(_("Y Axis"))
    ymin = FloatItem("y|min", help=_("Lower y-axis limit"))
    ymax = FloatItem("y|max", help=_("Upper y-axis limit"))
    _yparams = EndGroup("end Y")
    zparams = BeginGroup(_("Z Axis"))
    zmin = FloatItem("z|min", help=_("Lower z-axis limit"))
    zmax = FloatItem("z|max", help=_("Upper z-axis limit"))
    _zparams = EndGroup("end Z")

    def update_param(self, item: BaseImageItem) -> None:
        """
        Update the parameters of the axes associated with the image item.

        Args:
            item: The image item from which to update the parameters.
        """
        plot: BasePlot = item.plot()
        xaxis: QwtScaleDiv = plot.axisScaleDiv(item.xAxis())
        self.xmin = xaxis.lowerBound()
        self.xmax = xaxis.upperBound()
        yaxis: QwtScaleDiv = plot.axisScaleDiv(item.yAxis())
        self.ymin = yaxis.lowerBound()
        self.ymax = yaxis.upperBound()
        self.zmin, self.zmax = item.min, item.max

    def update_item(self, item: BaseImageItem) -> None:
        """
        Update the axes with the parameters associated with the image item.

        Args:
            item: The image item to update.
        """
        plot: BasePlot = item.plot()
        plot.set_plot_limits(self.xmin, self.xmax, self.ymin, self.ymax)
        item.set_lut_range([self.zmin, self.zmax])
        plot.update_colormap_axis(item)
        for axis_id in (X_BOTTOM, Y_LEFT, Y_RIGHT):
            plot.SIG_AXIS_PARAMETERS_CHANGED.emit(axis_id)

    # TODO: remove this method in a future release
    def update_axes(self, obj: BaseImageItem) -> None:
        """Update object from parameters. Deprecated, use update_item instead.

        Args:
            obj: The image item to update.
        """
        warnings.warn(
            "`ImageAxesParam.update_axes` method is deprecated and will be removed "
            "in a future release. Please use `update_item` instead.",
            DeprecationWarning,
            stacklevel=2,
        )
        self.update_item(obj)
