# Copyright (c) OpenMMLab. All rights reserved.
import math
from typing import List, Optional, Sequence, Union

import numpy as np
import torch
from matplotlib.font_manager import FontProperties
from mmengine.visualization import Visualizer

from mmocr.registry import VISUALIZERS


@VISUALIZERS.register_module()
class BaseLocalVisualizer(Visualizer):
    """The MMOCR Text Detection Local Visualizer.

    Args:
        name (str): Name of the instance. Defaults to 'visualizer'.
        image (np.ndarray, optional): the origin image to draw. The format
            should be RGB. Defaults to None.
        vis_backends (list, optional): Visual backend config list.
            Default to None.
        save_dir (str, optional): Save file dir for all storage backends.
            If it is None, the backend storage will not save any data.
        fig_save_cfg (dict): Keyword parameters of figure for saving.
            Defaults to empty dict.
        fig_show_cfg (dict): Keyword parameters of figure for showing.
            Defaults to empty dict.
        is_openset (bool, optional): Whether the visualizer is used in
            OpenSet. Defaults to False.
        font_families (Union[str, List[str]]): The font families of labels.
            Defaults to 'sans-serif'.
        font_properties (Union[str, FontProperties], optional):
            The font properties of texts. The format should be a path str
            to font file or a `font_manager.FontProperties()` object.
            If you want to draw Chinese texts, you need to prepare
            a font file that can show Chinese characters properly.
            For example: `simhei.ttf`,`simsun.ttc`,`simkai.ttf` and so on.
            Then set font_properties=matplotlib.font_manager.FontProperties
            (fname='path/to/font_file') or font_properties='path/to/font_file'
            This function need mmengine version >=0.6.0.
            Defaults to None.
    """
    PALETTE = [(220, 20, 60), (119, 11, 32), (0, 0, 142), (0, 0, 230),
               (106, 0, 228), (0, 60, 100), (0, 80, 100), (0, 0, 70),
               (0, 0, 192), (250, 170, 30), (100, 170, 30), (220, 220, 0),
               (175, 116, 175), (250, 0, 30), (165, 42, 42), (255, 77, 255),
               (0, 226, 252), (182, 182, 255), (0, 82, 0), (120, 166, 157),
               (110, 76, 0), (174, 57, 255), (199, 100, 0), (72, 0, 118),
               (255, 179, 240), (0, 125, 92), (209, 0, 151), (188, 208, 182),
               (0, 220, 176), (255, 99, 164), (92, 0, 73), (133, 129, 255),
               (78, 180, 255), (0, 228, 0), (174, 255, 243), (45, 89, 255),
               (134, 134, 103), (145, 148, 174), (255, 208, 186),
               (197, 226, 255), (171, 134, 1), (109, 63, 54), (207, 138, 255),
               (151, 0, 95), (9, 80, 61), (84, 105, 51), (74, 65, 105),
               (166, 196, 102), (208, 195, 210), (255, 109, 65), (0, 143, 149),
               (179, 0, 194), (209, 99, 106), (5, 121, 0), (227, 255, 205),
               (147, 186, 208), (153, 69, 1), (3, 95, 161), (163, 255, 0),
               (119, 0, 170), (0, 182, 199), (0, 165, 120), (183, 130, 88),
               (95, 32, 0), (130, 114, 135), (110, 129, 133), (166, 74, 118),
               (219, 142, 185), (79, 210, 114), (178, 90, 62), (65, 70, 15),
               (127, 167, 115), (59, 105, 106), (142, 108, 45), (196, 172, 0),
               (95, 54, 80), (128, 76, 255), (201, 57, 1), (246, 0, 122),
               (191, 162, 208)]

    def __init__(self,
                 name: str = 'visualizer',
                 font_families: Union[str, List[str]] = 'sans-serif',
                 font_properties: Optional[Union[str, FontProperties]] = None,
                 **kwargs) -> None:
        super().__init__(name=name, **kwargs)
        self.font_families = font_families
        self.font_properties = self._set_font_properties(font_properties)

    def _set_font_properties(self,
                             fp: Optional[Union[str, FontProperties]] = None):
        if fp is None:
            return None
        elif isinstance(fp, str):
            return FontProperties(fname=fp)
        elif isinstance(fp, FontProperties):
            return fp
        else:
            raise ValueError(
                'font_properties argument type should be'
                ' `str` or `matplotlib.font_manager.FontProperties`')

    def get_labels_image(
        self,
        image: np.ndarray,
        labels: Union[np.ndarray, torch.Tensor],
        bboxes: Union[np.ndarray, torch.Tensor],
        colors: Union[str, Sequence[str]] = 'k',
        font_size: Union[int, float] = 10,
        auto_font_size: bool = False,
        font_families: Union[str, List[str]] = 'sans-serif',
        font_properties: Optional[Union[str, FontProperties]] = None
    ) -> np.ndarray:
        """Draw labels on image.

        Args:
            image (np.ndarray): The origin image to draw. The format
                should be RGB.
            labels (Union[np.ndarray, torch.Tensor]): The labels to draw.
            bboxes (Union[np.ndarray, torch.Tensor]): The bboxes to draw.
            colors (Union[str, Sequence[str]]): The colors of labels.
                ``colors`` can have the same length with labels or just single
                value. If ``colors`` is single value, all the labels will have
                the same colors. Refer to `matplotlib.colors` for full list of
                formats that are accepted. Defaults to 'k'.
            font_size (Union[int, float]): The font size of labels. Defaults
                to 10.
            auto_font_size (bool): Whether to automatically adjust font size.
                Defaults to False.
            font_families (Union[str, List[str]]): The font families of labels.
                Defaults to 'sans-serif'.
            font_properties (Union[str, FontProperties], optional):
                The font properties of texts. The format should be a path str
                to font file or a `font_manager.FontProperties()` object.
                If you want to draw Chinese texts, you need to prepare
                a font file that can show Chinese characters properly.
                For example: `simhei.ttf`,`simsun.ttc`,`simkai.ttf` and so on.
                Then set font_properties=matplotlib.font_manager.FontProperties
                (fname='path/to/font_file') or
                font_properties='path/to/font_file'.
                This function need mmengine version >=0.6.0.
                Defaults to None.
        """
        if not labels and not bboxes:
            return image
        if colors is not None and isinstance(colors, (list, tuple)):
            size = math.ceil(len(labels) / len(colors))
            colors = (colors * size)[:len(labels)]
        if auto_font_size:
            assert font_size is not None and isinstance(
                font_size, (int, float))
            font_size = (bboxes[:, 2:] - bboxes[:, :2]).min(-1) * font_size
            font_size = font_size.tolist()
        self.set_image(image)
        self.draw_texts(
            labels, (bboxes[:, :2] + bboxes[:, 2:]) / 2,
            vertical_alignments='center',
            horizontal_alignments='center',
            colors='k',
            font_sizes=font_size,
            font_families=font_families,
            font_properties=font_properties)
        return self.get_image()

    def get_polygons_image(self,
                           image: np.ndarray,
                           polygons: Sequence[np.ndarray],
                           colors: Union[str, Sequence[str]] = 'g',
                           filling: bool = False,
                           line_width: Union[int, float] = 0.5,
                           alpha: float = 0.5) -> np.ndarray:
        """Draw polygons on image.

        Args:
            image (np.ndarray): The origin image to draw. The format
                should be RGB.
            polygons (Sequence[np.ndarray]): The polygons to draw. The shape
                should be (N, 2).
            colors (Union[str, Sequence[str]]): The colors of polygons.
                ``colors`` can have the same length with polygons or just
                single value. If ``colors`` is single value, all the polygons
                will have the same colors. Refer to `matplotlib.colors` for
                full list of formats that are accepted. Defaults to 'g'.
            filling (bool): Whether to fill the polygons. Defaults to False.
            line_width (Union[int, float]): The line width of polygons.
                Defaults to 0.5.
            alpha (float): The alpha of polygons. Defaults to 0.5.

        Returns:
            np.ndarray: The image with polygons drawn.
        """
        if colors is not None and isinstance(colors, (list, tuple)):
            size = math.ceil(len(polygons) / len(colors))
            colors = (colors * size)[:len(polygons)]
        self.set_image(image)
        if filling:
            self.draw_polygons(
                polygons,
                face_colors=colors,
                edge_colors=colors,
                line_widths=line_width,
                alpha=alpha)
        else:
            self.draw_polygons(
                polygons,
                edge_colors=colors,
                line_widths=line_width,
                alpha=alpha)
        return self.get_image()

    def get_bboxes_image(self: Visualizer,
                         image: np.ndarray,
                         bboxes: Union[np.ndarray, torch.Tensor],
                         colors: Union[str, Sequence[str]] = 'g',
                         filling: bool = False,
                         line_width: Union[int, float] = 0.5,
                         alpha: float = 0.5) -> np.ndarray:
        """Draw bboxes on image.

        Args:
            image (np.ndarray): The origin image to draw. The format
                should be RGB.
            bboxes (Union[np.ndarray, torch.Tensor]): The bboxes to draw.
            colors (Union[str, Sequence[str]]): The colors of bboxes.
                ``colors`` can have the same length with bboxes or just single
                value. If ``colors`` is single value, all the bboxes will have
                the same colors. Refer to `matplotlib.colors` for full list of
                formats that are accepted. Defaults to 'g'.
            filling (bool): Whether to fill the bboxes. Defaults to False.
            line_width (Union[int, float]): The line width of bboxes.
                Defaults to 0.5.
            alpha (float): The alpha of bboxes. Defaults to 0.5.

        Returns:
            np.ndarray: The image with bboxes drawn.
        """
        if colors is not None and isinstance(colors, (list, tuple)):
            size = math.ceil(len(bboxes) / len(colors))
            colors = (colors * size)[:len(bboxes)]
        self.set_image(image)
        if filling:
            self.draw_bboxes(
                bboxes,
                face_colors=colors,
                edge_colors=colors,
                line_widths=line_width,
                alpha=alpha)
        else:
            self.draw_bboxes(
                bboxes,
                edge_colors=colors,
                line_widths=line_width,
                alpha=alpha)
        return self.get_image()

    def _draw_instances(self) -> np.ndarray:
        raise NotImplementedError

    def _cat_image(self, imgs: Sequence[np.ndarray], axis: int) -> np.ndarray:
        """Concatenate images.

        Args:
            imgs (Sequence[np.ndarray]): The images to concatenate.
            axis (int): The axis to concatenate.

        Returns:
            np.ndarray: The concatenated image.
        """
        cat_image = list()
        for img in imgs:
            if img is not None:
                cat_image.append(img)
        if len(cat_image):
            return np.concatenate(cat_image, axis=axis)
        else:
            return None
