from svgwrite.container import Group
import svgwrite
from svgwrite.shapes import Rect, Polygon
from svgwrite.text import Text
import wand
from wand.api import library
from wand.image import Image
from helix.calculators.subarray_helper import extract_subarray
from helix.constants.color import Color
from helix.constants.system_type import SystemType


class ImagePresenter(object):
    def __init__(self, system_type, module_type):
        self.system_type = system_type
        self.module_type = module_type

    def generate_image(self, panels, subarrays):
        image_dimensions = (620, 710)
        if len(panels) < 3000:
            svg_string = self.generate_array_svg(image_dimensions, panels, subarrays)
        else:
            svg_string = ImagePresenter.svg_too_large_string(image_dimensions)
        with Image(blob=svg_string.encode('utf-8'), format='svg') as img:
            with wand.color.Color('transparent') as background_color:
                library.MagickSetBackgroundColor(img.wand, background_color.resource)
            png_image = img.make_blob('png')
        return png_image

    @staticmethod
    def svg_too_large_string(image_dimensions):
        dwg = svgwrite.Drawing(size=image_dimensions)
        dwg.add(Text("Array may be too large, please try again or break up array into smaller components",
                     x=[image_dimensions[0] / 2],
                     y=[image_dimensions[1] / 2],
                     text_anchor="middle"))
        return dwg.tostring()

    def generate_array_svg(self, image_dimensions, panels, subarrays):
        dwg = svgwrite.Drawing(size=image_dimensions)
        array_dimensions = self.calculate_max_array_dimensions(panels, subarrays)

        module_constants = self.system_type.module_constants(self.module_type)

        array_width, array_height = array_dimensions
        spacing_x, spacing_y = module_constants.presenter_spacing
        spacing_x *= 10
        spacing_y *= 10

        scaling_x = image_dimensions[0] / (array_width * spacing_x)
        scaling_y = image_dimensions[1] / (array_height * spacing_y)
        scaling_factor = min(scaling_x, scaling_y)

        for subarray in subarrays:
            dwg.add(self.draw_subarray(panels, subarray, array_dimensions, scaling_factor))
        return dwg.tostring()

    def draw_subarray(self, all_panels, subarray, array_dimensions, scaling_factor):
        module_constants = self.system_type.module_constants(self.module_type)
        array_width, array_height = array_dimensions
        spacing_x, spacing_y = module_constants.presenter_spacing
        spacing_x *= 10
        spacing_y *= 10

        panels = extract_subarray(all_panels, subarray.subarray_number)

        subarray_group = Group(transform="scale(%f, %f)" % (scaling_factor, scaling_factor))

        for panel in panels:
            group = self.draw_panel(panel, subarray, array_height - 1, (spacing_x, spacing_y))
            subarray_group.add(group)
        return subarray_group

    def draw_panel(self, panel, subarray, max_height, panel_dimensions):
        origin = subarray.origin
        width, height = panel_dimensions

        border_width = 0.5

        inner_width = width - border_width
        inner_height = height - border_width
        inner_dimensions = (inner_width - border_width, inner_height - border_width)

        x = (panel.coordinate.x + origin.x) * width
        y = (max_height - (panel.coordinate.y + origin.y)) * height

        group = Group(stroke_width=border_width, transform="translate(%f, %f)" % (x, y))  # FIXME: look into not having to do this.

        fill_color, secondary_fill_color, border_color, text_color = self.colors(panel)

        # draw the background, with a white border around everything
        background_rect = Rect(insert=(0, 0), size=panel_dimensions, fill=fill_color.value, stroke=Color.array_background.value)
        group.add(background_rect)

        # IFF we have both seismic and wind anchors, draw the panel as the "half wind, half seismic" thing.
        # This means we add a triangle that divides the panel.
        if secondary_fill_color is not None:
            points = [
                (border_width, border_width),
                (inner_width, inner_height),
                (inner_width, border_width)
            ]
            dual_anchor_indicator = Polygon(points, fill=secondary_fill_color.value, stroke=secondary_fill_color.value)
            group.add(dual_anchor_indicator)

        # draw a border rect.
        # This doesn't need to be drawn if we don't have any wind or seismic anchors.
        if border_color != fill_color:
            border_rect = Rect(insert=(border_width, border_width), size=inner_dimensions, stroke=border_color.value, fill_opacity=0)
            group.add(border_rect)

        # If dual-tilt, draw the little triangle on the right side of the panel
        # Use a hardcoded border value, because this won't change.
        if self.system_type == SystemType.dualTilt:
            points = [
                (width / 2, border_width),
                (width / 2, inner_height),
                (inner_width, height / 2),
            ]
            dual_anchor_indicator = Polygon(points, fill_opacity=0, stroke=Color.border.value, stroke_width=border_width / 2)
            group.add(dual_anchor_indicator)

        # add all the text!
        ballast_text = self.text_overlay(str(panel.ballast), panel_dimensions, 0.3, 0.55, "2.5px", text_color)
        group.add(ballast_text)

        anchors_text = self.text_overlay(str(panel.wind_anchors + panel.seismic_anchors), panel_dimensions, 0.7, 0.55, "2.5px", text_color)
        group.add(anchors_text)

        return group

    @staticmethod
    def text_overlay(text, dimensions, x, y, font_size, color):
        return Text(
            text,
            x=[dimensions[0] * x],
            y=[dimensions[1] * y],
            font_size=font_size,
            font_family='sans-serif',
            text_anchor="middle",
            fill=color.value
        )

    @staticmethod
    def calculate_max_array_dimensions(all_panels, subarrays):
        actual_y_coordinates = []
        actual_x_coordinates = []
        for subarray in subarrays:
            panels = extract_subarray(all_panels, subarray.subarray_number)
            actual_y_coordinates += [panel.coordinate.y + subarray.origin.y for panel in panels]
            actual_x_coordinates += [panel.coordinate.x + subarray.origin.x for panel in panels]
        return max(actual_x_coordinates) + 1, max(actual_y_coordinates) + 1

    @staticmethod
    def colors(panel):
        fill = Color.default_panel_background
        secondary = None
        border = Color.border
        text = Color.dark_text

        if panel.seismic_anchors > 0 and panel.wind_anchors > 0:
            fill = Color.seismic_background
            secondary = Color.wind_background
        elif panel.seismic_anchors > 0:
            fill = Color.seismic_background
        elif panel.wind_anchors > 0:
            fill = Color.wind_background
        else:
            border = Color.default_panel_background
            text = Color.light_text

        return fill, secondary, border, text
