#  Copyright (c) 2020. JetBrains s.r.o.
#  Use of this source code is governed by the MIT license that can be found in the LICENSE file.

# noinspection PyUnresolvedReferences
from typing import Dict, List

import lets_plot_kotlin_bridge

from ._global_settings import get_js_cdn_url
from ._type_utils import standardize_dict


def _generate_dynamic_display_html(plot_spec: Dict) -> str:
    plot_spec = _standardize_plot_spec(plot_spec)
    # Old implementation (deprecated):
    # return lets_plot_kotlin_bridge.generate_html(plot_spec)

    # New implementation using get_display_html_for_raw_spec with default parameters
    return lets_plot_kotlin_bridge.get_display_html_for_raw_spec(
        plot_spec,
        {},  # empty sizing_options -> defaults to notebookCell sizing (MIN width, SCALED height)
        True,  # dynamic_script_loading
        False,  # force_immediate_render
        False  # responsive
    )


def _generate_svg(plot_spec: Dict, w: float = None, h: float = None, unit: str = None) -> str:
    plot_spec = _standardize_plot_spec(plot_spec)
    w = -1.0 if w is None else float(w)
    h = -1.0 if h is None else float(h)
    unit = '' if unit is None else str(unit)  # None is not a valid value for str type - PyArg_ParseTuple will fail
    return lets_plot_kotlin_bridge.export_svg(plot_spec, w, h, unit)


def _generate_png(bytestring: Dict, output_width: float, output_height: float, unit: str, dpi: int,
                  scale: float) -> str:
    """
    Export a plot to PNG format. Returns base64 encoded string of the PNG image.
    """
    plot_spec = _standardize_plot_spec(bytestring)
    output_width = -1.0 if output_width is None else float(output_width)
    output_height = -1.0 if output_height is None else float(output_height)
    unit = '' if unit is None else str(unit)  # None is not a valid value for str type - PyArg_ParseTuple will fail
    dpi = -1 if dpi is None else int(dpi)
    scale = -1.0 if scale is None else float(scale)
    return lets_plot_kotlin_bridge.export_png(plot_spec, output_width, output_height, unit, dpi, scale)


def _generate_mvg(bytestring: Dict, output_width: float, output_height: float, unit: str, dpi: int,
                  scale: float) -> str:
    """
    Export a plot to MVG format. For internal use.
    """
    plot_spec = _standardize_plot_spec(bytestring)
    output_width = -1.0 if output_width is None else float(output_width)
    output_height = -1.0 if output_height is None else float(output_height)
    unit = '' if unit is None else str(unit)  # None is not a valid value for str type - PyArg_ParseTuple will fail
    dpi = -1 if dpi is None else int(dpi)
    scale = -1.0 if scale is None else float(scale)
    return lets_plot_kotlin_bridge.export_mvg(plot_spec, output_width, output_height, unit, dpi, scale)


def _generate_static_html_page(plot_spec: Dict, iframe: bool) -> str:
    plot_spec = _standardize_plot_spec(plot_spec)
    scriptUrl = get_js_cdn_url()
    return lets_plot_kotlin_bridge.export_html(plot_spec, scriptUrl, iframe)


def _generate_static_html_page_for_raw_spec(
        plot_spec: Dict,
        sizing_options: Dict,
        dynamic_script_loading: bool = False,
        force_immediate_render: bool = False,
        responsive: bool = False,
        height100pct: bool = False
) -> str:
    plot_spec = _standardize_plot_spec(plot_spec)
    sizing_options = standardize_dict(sizing_options)
    scriptUrl = get_js_cdn_url()
    return lets_plot_kotlin_bridge.get_static_html_page_for_raw_spec(
        plot_spec,
        scriptUrl,
        sizing_options,
        dynamic_script_loading,
        force_immediate_render,
        responsive,
        height100pct
    )


def _standardize_plot_spec(plot_spec: Dict) -> Dict:
    """
    :param plot_spec: dict
    """
    if not isinstance(plot_spec, dict):
        raise ValueError("dict expected but was {}".format(type(plot_spec)))

    return standardize_dict(plot_spec)


def _generate_static_configure_html() -> str:
    """
    Generate static HTML configuration.

    Returns
    -------
    str
        HTML string containing the static configuration with the script URL from global settings.
    """
    scriptUrl = get_js_cdn_url()
    return lets_plot_kotlin_bridge.get_static_configure_html(scriptUrl)


def _generate_display_html_for_raw_spec(
        plot_spec: Dict,
        sizing_options: Dict,
        *,
        dynamic_script_loading: bool = False,
        force_immediate_render: bool = False,
        responsive: bool = False,
        height100pct: bool = False
) -> str:
    """
    Generate HTML for displaying a plot from 'raw' specification (not processed by plot backend)
    with customizable options.

    Parameters
    ----------
    plot_spec : Dict
        Dict containing the plot specification.
    sizing_options : Dict
        Dict containing sizing policy options (width_mode, height_mode, width, height).
    dynamic_script_loading : bool, default=False
        Controls how the generated JS code interacts with the lets-plot.js library.
        If True, assumes the library loads dynamically (asynchronously).
        If False, assumes the library loads synchronously via a <script> tag in the page header.
    force_immediate_render : bool, default=False
        Controls the timing of plot rendering.
        If True, renders the plot immediately.
        If False, waits for the ResizeObserver event to ensure proper DOM layout.
    responsive : bool, default=False
        If True, makes the plot responsive to container size changes.
    height100pct : bool, default=False
        If True, sets the plot container div height to 100%.

    Returns
    -------
    str
        HTML string containing the plot with specified options.

    Notes
    -----
    The sizing_options dict supports the following structure:
    {
        'width_mode': str,     # 'fixed', 'min', 'fit', 'scaled' (case-insensitive)
        'height_mode': str,    # 'fixed', 'min', 'fit', 'scaled' (case-insensitive)
        'width': number,       # optional
        'height': number       # optional
    }

    Sizing modes determine how the plot dimensions are calculated:

    1. FIXED mode:
       - Uses the explicitly provided width/height values
       - Falls back to the default figure size if no values are provided
       - Not responsive to container size

    2. MIN mode:
       Applies the smallest dimension among:
       - The default figure size
       - The specified width/height (if provided)
       - The container size (if available)

    3. FIT mode:
       Uses either:
       - The specified width/height if provided
       - Otherwise uses container size if available
       - Falls back to default figure size if neither is available

    4. SCALED mode:
       - Always preserves the figure's aspect ratio
       - Typical usage: one dimension (usually width) uses FIXED/MIN/FIT mode,
         and SCALED height adjusts to maintain aspect ratio
       - Special case: when both width and height are SCALED:
         * Requires container size to be available
         * Fits a figure within container while preserving the aspect ratio
         * Neither dimension is predetermined

    """
    plot_spec = _standardize_plot_spec(plot_spec)
    sizing_options = standardize_dict(sizing_options)
    return lets_plot_kotlin_bridge.get_display_html_for_raw_spec(
        plot_spec,
        sizing_options,
        dynamic_script_loading,
        force_immediate_render,
        responsive,
        height100pct
    )


def _generate_palette_from_color_scale_spec(scale_spec: Dict, n: int) -> List[str]:
    """
    Generate a list of hex color codes from a color scale specification.

    Parameters
    ----------
    scale_spec : Dict
        Dictionary containing the scale specification.
    n : int
        Number of colors to generate.

    Returns
    -------
    List of hex color codes.

    """
    scale_spec = standardize_dict(scale_spec)
    return lets_plot_kotlin_bridge.get_palette_from_color_scale_spec(scale_spec, n)
