from reportlab.graphics.charts.barcharts import HorizontalBarChart, VerticalBarChart
from reportlab.graphics.charts.doughnut import Doughnut
from reportlab.graphics.charts.linecharts import HorizontalLineChart
from reportlab.graphics.charts.piecharts import LegendedPie, Pie
from reportlab.graphics.widgets.markers import makeMarker

from xhtml2pdf.util import getColor


def set_properties(obj, data, prop_map):
    for key, fnc in prop_map:
        if key in data:
            try:
                value = fnc(data[key])

                if value is not None:
                    obj.__setattr__(key, value)
            except Exception:
                continue


class Props:
    def __init__(self, instance) -> None:
        self.prop_map = [
            ("x", int),
            ("y", int),
            ("width", int),
            ("height", int),
            ("data", lambda x: x),
            ("labels", lambda x: instance.assign_labels(x)),
        ]
        self.prop_map_title = [("x", int), ("y", int), ("_text", str)]
        self.prop_map_legend = [
            ("x", int),
            ("y", int),
            ("deltax", int),
            ("alignment", str),
            ("boxAnchor", str),
            ("fontSize", int),
            ("strokeWidth", int),
            ("dy", int),
            ("dx", int),
            ("dxTextSpace", int),
            ("deltay", int),
            ("columnMaximum", int),
            ("variColumn", int),
            ("deltax", int),
            ("fontName", str),
            ("colorNamePairs", list),
        ]
        self.prop_map_legend1 = [("x", int), ("y", int)]
        self.prop_map_bars = [("strokeWidth", int)]
        self.prop_map_barLabels = [("nudge", int), ("fontSize", int), ("fontName", str)]
        self.prop_map_categoryAxis = [
            ("visibleTicks", int),
            ("strokeWidth", int),
            ("tickShift", int),
            ("labelAxisMode", str),
        ]
        self.prop_map_categoryAxis_labels = [
            ("angle", int),
            ("dy", int),
            ("fontSize", int),
            ("boxAnchor", str),
            ("fontName", str),
            ("textAnchor", str),
        ]
        self.prop_map_slices = [
            ("strokeWidth", int),
            ("labelRadius", float),
            ("poput", int),
            ("fontName", str),
            ("fontSize", int),
            ("strokeDashArray", str),
        ]

    @staticmethod
    def add_prop(prop_map, data):
        prop_map += data


class BaseChart:
    def set_legend(self, data, legend, props=None):
        if props is None:
            props = Props(self)
        set_properties(legend, data, props.prop_map_legend)
        return legend

    def load_data_legend(self, data, legend):
        legend.colorNamePairs = []
        color = self.get_colors()

        for x, obj in enumerate(data["data"]):
            if isinstance(obj, list):
                for y, value in enumerate(obj):
                    if color:
                        if data["type"] == "doughnut":
                            legend.colorNamePairs.append(
                                (color[x], (data["labels"][y], " ", str(value)))
                            )
                        else:
                            legend.colorNamePairs.append(
                                (color[y], (data["labels"][y], " ", str(value)))
                            )
            elif color:
                legend.colorNamePairs.append(
                    (color[x], (data["labels"][x], " ", str(obj)))
                )

    def set_title_properties(self, data, title, props=None):
        if props is None:
            props = Props(self)
        set_properties(title, data, props.prop_map_title)
        return title

    def set_properties(self, data, props=None):
        if props is None:
            props = Props(self)
        set_properties(self, data, props.prop_map)

    @staticmethod
    def get_colors():
        return []


class BaseBarChart(BaseChart):
    def __init__(self) -> None:
        super().__init__()

    def set_properties(self, data, props=None):
        props = Props(self)
        props.add_prop(props.prop_map, [("barWidth", str)])
        props.add_prop(props.prop_map, [("barSpacing", str)])
        props.add_prop(props.prop_map, [("barLabelFormat", str)])
        props.add_prop(props.prop_map, [("strokeColor", getColor)])
        props.add_prop(props.prop_map, [("groupSpacing", int)])
        super().set_properties(data, props=props)

        if "bars" in data:
            self.set_bars(data["bars"], props=props)

        if "barLabels" in data:
            self.set_barLabels(data["barLabels"], props=props)

        if "categoryAxis" in data:
            self.set_categoryAxis(data["categoryAxis"], props=props)

            if "labels" in data["categoryAxis"]:
                self.set_categoryAxis_labels(
                    data["categoryAxis"]["labels"], props=props
                )

    def assign_labels(self, labels):
        self.categoryAxis.categoryNames = labels

    def set_bars(self, data, props=None):
        if props is None:
            props = Props(self)
        props.add_prop(props.prop_map_bars, [("strokeColor", getColor)])
        set_properties(self.bars, data, props.prop_map_bars)

    def set_barLabels(self, data, props=None):
        if props is None:
            props = Props(self)
        set_properties(self.barLabels, data, props.prop_map_barLabels)

    def set_categoryAxis(self, data, props=None):
        if props is None:
            props = Props(self)
        props.add_prop(props.prop_map_categoryAxis, [("strokeColor", getColor)])
        set_properties(self.categoryAxis, data, props.prop_map_categoryAxis)

    def set_categoryAxis_labels(self, data, props=None):
        if props is None:
            props = Props(self)
        props.add_prop(props.prop_map_categoryAxis_labels, [("fillColor", getColor)])
        set_properties(
            self.categoryAxis.labels, data, props.prop_map_categoryAxis_labels
        )


class HorizontalBar(HorizontalBarChart, BaseBarChart):
    pass


class VerticalBar(VerticalBarChart, BaseBarChart):
    pass


class HorizontalLine(HorizontalLineChart, BaseChart):
    def __init__(self) -> None:
        super().__init__()

    def assign_labels(self, labels):
        self.categoryAxis.categoryNames = labels

    def set_properties(self, data, props=None):
        props = Props(self)
        props.add_prop(props.prop_map, [("fillColor", getColor)])
        props.add_prop(props.prop_map, [("lineLabelFormat", str)])
        props.add_prop(props.prop_map, [("strokeColor", int)])
        props.add_prop(props.prop_map, [("joinedLines", int)])
        props.add_prop(props.prop_map, [("marker", self.fill_marker)])
        super().set_properties(data, props=props)

    def fill_marker(self, fill_type):
        for x in range(len(self.data)):
            self.lines[x].symbol = makeMarker(fill_type)

    @staticmethod
    def get_colors():
        return []


class PieChart(Pie, BaseChart):
    def __init__(self) -> None:
        super().__init__()

    def set_properties(self, data, props=None):
        props = Props(self)
        props.add_prop(props.prop_map, [("sideLabels", int)])
        props.add_prop(props.prop_map, [("simpleLabels", int)])
        props.add_prop(props.prop_map, [("sideLabelsOffset", int)])
        props.add_prop(props.prop_map, [("startAngle", int)])
        props.add_prop(props.prop_map, [("orderMode", str)])
        props.add_prop(props.prop_map, [("direction", str)])
        super().set_properties(data, props=props)

        if "slices" in data:
            self.set_slices(data["slices"], props=props)

    def assign_labels(self, labels):
        self.labels = labels

    def set_slices(self, data, props=None):
        if props is None:
            props = Props(self)
        props.add_prop(props.prop_map_slices, [("strokeColor", getColor)])
        props.add_prop(props.prop_map_slices, [("fillColor", getColor)])
        set_properties(self.slices, data, props.prop_map_slices)

    def get_colors(self):
        colors_list = []
        for x, _obj in enumerate(self.data):
            colors_list.append(self.slices[x].fillColor)
        return colors_list


class LegendedPieChart(LegendedPie, BaseChart):
    def __init__(self) -> None:
        super().__init__()
        self.legend1.x = 350
        self.legend1.y = 150

    def set_properties(self, data, props=None):
        props = Props(self)
        props.add_prop(props.prop_map, [("legend_data", list)])
        super().set_properties(data, props=props)

        if "legend1" in data:
            self.set_legend1(self.legend1, data["legend1"], props=props)

    def set_legend1(self, obj, data, props=None):
        if props is None:
            props = Props(self)
        set_properties(obj, data, props.prop_map_legend1)

    def assign_labels(self, labels):
        self.legend_names = labels


class DoughnutChart(Doughnut, BaseChart):
    def __init__(self) -> None:
        super().__init__()

    def assign_labels(self, labels):
        self.labels = labels

    def get_colors(self):
        colors = []
        for x, _obj in enumerate(self.data):
            colors.append(self.slices[x].fillColor)
        return colors
