from ._private._field_api import FieldAPI
from ._private._well_dto import PlotInstanceDto, PlotInstancePaneDto
from ._private.dto_converters._plot_dto_converter import PlotDtoConverter
from .data import Data
from .vector import Vector
from typing import Union, List, Optional, Tuple
from .plot_channel import PlotChannel
from .plots_enum import PlotDataTypesEnum, PlotProperties
from datetime import datetime
from .measure_enum import MeasureEnum
from .datetime_utils import datetime_to_str
from .field_data_types_catalog import FieldDataTypesCatalog
[docs]
class Plot:
    """ Plot object.
        Presents a KAPPA Automate Plot object.
    """
[docs]
    def __init__(self, field_id: str, well_id: str, plot_id: str, plot_name: str, field_api: FieldAPI, data_types_catalog: FieldDataTypesCatalog, dto_converter: PlotDtoConverter) -> None:
        self.__field_api: FieldAPI = field_api
        self.__field_id: str = field_id
        self.__well_id: str = well_id
        self.__id: str = plot_id
        self.__name: str = plot_name
        self.__data_types_catalog: FieldDataTypesCatalog = data_types_catalog
        self.__channels: List[PlotChannel] = list()
        self.__pane: List[PlotInstancePaneDto] = list()
        self.__dto_converter: PlotDtoConverter = dto_converter 
[docs]
    def rename(self, name: str) -> None:
        """
        Rename the :class:`Plot` object.
        Parameters
        ----------
        name:
            Name of the plot.
        """
        dto = {'name': name}
        self.__name = self.__field_api.rename_plot(self.__field_id, self.__well_id, self.__id, dto) 
[docs]
    def add_existing_data(self, data: Data, channel_name: Optional[str] = None, pane_name: Optional[str] = None,
                          show_symbols: Optional[bool] = None, show_lines: bool = True, is_y_log: bool = False,
                          hide_in_legend: bool = False, is_raw: bool = False) -> PlotChannel:
        """
        Add data to the :class:`Plot` object.
        Parameters
        ----------
        data:
            Data to plot.
        channel_name:
            Name of the data.
        pane_name:
            Name of the pane.
        show_symbols:
            Show the point on the plot.
        show_lines:
            Show the lines on the plot.
        is_y_log:
            Show Y axis as log
        use_elapsed:
            use elapsed times instead of datetime values
        hide_in_legend:
            Whether this channel should be hidden or not on the legend
        is_raw:
            Whether to display raw data, as opposed to preview (default: false)
        Returns
        -------
        :class:`PlotChannel`:
            A new Channel added to the :class:`Plot` object
        """
        properties = PlotProperties(show_symbols=show_symbols, show_lines=show_lines, channel_name=channel_name, is_y_log=is_y_log, use_elapsed=False,
                                    hide_in_legend=hide_in_legend, is_raw=is_raw)
        channel = PlotChannel(self.__field_id, self.__data_types_catalog, pane_name)
        channel.build_time_series(self.__well_id, data, properties)
        self.__channels.append(channel)
        return channel 
[docs]
    def add_embedded_data(self, data: Union[Vector, Tuple[List[float], List[float]]], channel_name: Optional[str] = None, pane_name: Optional[str] = None,
                          show_symbols: Optional[bool] = None, show_lines: bool = True, data_type: Optional[Union[PlotDataTypesEnum, str]] = None,
                          is_by_step: bool = False, x_measure: Optional[MeasureEnum] = None, y_measure: Optional[MeasureEnum] = None, is_y_log: bool = False,
                          first_x: Optional[datetime] = None, use_elapsed: bool = False, hide_in_legend: bool = False) -> PlotChannel:
        """
        Add data to the :class:`Plot` object.
        Parameters
        ----------
        data:
            Data to plot.
        channel_name:
            Name of the data.
        pane_name:
            Name of the pane.
        show_symbols:
            Show the point on the plot.
        show_lines:
            Show the lines on the plot.
        data_type:
            Type of the data.
        is_by_step:
            True if it is step data.
        x_measure:
            Unit of the x_axis of the Channel (only for scatter plot).
        y_measure:
            Unit of the y_axis of the Channel.
        is_y_log:
            Show Y axis as log
        first_x:
            specify the first x of step data
        use_elapsed:
            use elapsed times instead of datetime values
        hide_in_legend:
            Whether this channel should be hidden or not on the legend
        Returns
        -------
        :class:`PlotChannel`:
            A new Channel added to the :class:`Plot` object
        """
        properties = PlotProperties(show_symbols=show_symbols, show_lines=show_lines, channel_name=channel_name, is_by_step=is_by_step, x_measure=x_measure,
                                    y_measure=y_measure, first_x=datetime_to_str(first_x), use_elapsed=use_elapsed, hide_in_legend=hide_in_legend,
                                    is_y_log=is_y_log)
        channel = PlotChannel(self.__field_id, self.__data_types_catalog, pane_name)
        channel.build_embedded(self.__well_id, data, data_type, properties)
        self.__channels.append(channel)
        return channel 
[docs]
    def add_cross_plot_data(self, x_data: Data, y_data: Data, channel_name: Optional[str] = None, pane_name: Optional[str] = None,
                            show_symbols: Optional[bool] = None, show_lines: bool = True, data_type: Optional[Union[PlotDataTypesEnum, str]] = None,
                            is_y_log: bool = False, hide_in_legend: bool = False) -> PlotChannel:
        """
        Adds cross plot data to the visualization, enabling plotting of X versus Y data points
        with customizable display options and metadata. This function is used to define how
        data is represented in a cross plot channel and associated visualization components.
        Parameters
        ----------
        x_data : Data
            The dataset representing the X-axis values for the cross plot.
        y_data : Data
            The dataset representing the Y-axis values for the cross plot.
        channel_name : Optional[str], optional
            An optional name associated with the created plot channel. Defaults to None.
        pane_name : Optional[str], optional
            An optional pane name where the cross plot is defined. Defaults to None.
        show_symbols : Optional[bool], optional
            Indicates if symbols should be displayed on the plot. Defaults to None.
        show_lines : bool, optional
            Determines whether lines should be displayed connecting the data points.
            Defaults to True.
        data_type : Optional[Union[PlotDataTypesEnum, str]], optional
            Indicates the data type of the plot, allowing for specific type categorization.
            Defaults to None.
        is_y_log : bool, optional
            Specifies whether the Y-axis of the plot should use a logarithmic scale.
            Defaults to False.
        hide_in_legend : bool, optional
            Determines if the plot should be hidden from the legend in the visualization.
            Defaults to False.
        Returns
        -------
        PlotChannel
            The constructed and configured plot channel that represents the cross plot
            data and associated properties.
        """
        properties = PlotProperties(show_symbols=show_symbols, show_lines=show_lines, channel_name=channel_name, hide_in_legend=hide_in_legend,
                                    is_y_log=is_y_log)
        channel = PlotChannel(self.__field_id, self.__data_types_catalog, pane_name)
        channel.build_cross_plot_channel(self.__well_id, x_data.vector_id, y_data.vector_id, data_type, properties)
        self.__channels.append(channel)
        return channel 
[docs]
    def update_plot(self) -> None:
        """ Update the plot in Kappa Automate"""
        plot_dto = self.__field_api.get_plot_dto(self.__field_id, self.__well_id, self.__id)
        updated_plot_dto = self.__dto_converter.get_plot_update_dto(plot_dto, self.__channels, self.__pane)
        try:
            self.__field_api.update_plot(self.__field_id, self.__well_id, self.__id, updated_plot_dto)
            self.__pane = list()
            self.__channels = list()
        except ConnectionError as error:
            if error.args[0].split(" ")[1] == str(400):
                raise ValueError("You probably have nan or None values in your data, you need to remove them")
            self.delete()
            raise error 
[docs]
    def get_plot_dto(self) -> PlotInstanceDto:
        """
        Get the plot dto from Kappa Automate
        Returns
        -------
        :class:`PlotInstanceDto`:
            The Plot instance dto from Kappa Automate
        """
        return self.__field_api.get_plot_dto(self.__field_id, self.__well_id, self.__id) 
[docs]
    def delete(self) -> None:
        """
        Delete the current plot
        """
        self.__field_api.delete_plot(self.__field_id, self.__well_id, self.__id) 
    @property
    def id(self) -> str:
        """
        Gets the id of the :class:`Plot` object.
        """
        return self.__id
    @property
    def name(self) -> str:
        """
        Gets the name of the :class:`Plot` object.
        """
        return self.__name
[docs]
    def add_sub_plot(self, name: str, square_log_cycles: bool = False, stacked_bars: bool = False) -> None:
        """
        Add a new subplot or pane to the :class:`Plot` object
        Parameters
        ----------
        name:
            The name of the subplot
        square_log_cycles:
            Whether or not to use squared logarithmic cycles
        stacked_bars:
            Whether or not to use stacked bars
        """
        pane = PlotInstancePaneDto(name=name,
                                   squareLogCycles=square_log_cycles,
                                   stackedBars=stacked_bars,
                                   channels=[])
        self.__pane.append(pane)