from math import ceil

from helix.calculators.bom_helper import add_parts_to_list
from helix.constants import ebom_parts
from helix.constants.ebom_parts import *
from helix.constants.parts import wire_clip_large, cable_support, cable_support_lid, channel_nut, sunshade
from helix.constants.system_type import SystemType
from helix.constants.inverter_brand import InverterBrand


class EbomCalculator(object):
    def __init__(self, user_values, row_count, column_count, modules_count = None, panels = None):
        self.values = user_values
        self.row_count = row_count
        self.column_count = column_count
        self.modules_count = modules_count
        self.panels = panels

    def resolve_power_monitor_type(self):
        module_type = self.values.module_type()

        thresholds = {
            ModuleType.Cell96: 306,
            ModuleType.Cell128: 230,
            ModuleType.PSeries: 286
        }

        if (not self.modules_count) or self.modules_count >= thresholds[module_type]:
            return monitor_controller_480_v
        else:
            return monitor_controller_240_v

    def resolve_is_delta(self):
      if len(self.values.inverter_brands()) > 0:
        return self.values.inverter_brands()[0]['inverter_brand_id'] == InverterBrand.DELTA.value
      if len(self.values.standalone_inverters()) > 0:
        return self.values.standalone_inverters()[0]['model'].get_type == InverterBrand.DELTA.label
      # can't determine without data
      return False

    def compute_ebom(self):
        part_list = {}

        power_stations = self.values.power_stations()
        standalone_inverters = self.values.standalone_inverters()
        monitors = self.values.power_monitors()
        module_type = self.values.module_type()
        system_type = self.values.system_type()

        is_delta = self.resolve_is_delta()

        inverter_count = 0
        total_ac_run_length = 0
        panel_board_counts = [0, 0]
        proper_monitor_controller = self.resolve_power_monitor_type()

        for power_station in power_stations:
            power_station_count = power_station['power_station_quantity']
            total_ac_run_length += power_station['ac_run_length']
            inverter_quantity = power_station['inverter_quantity'] + self.get_standalone_inverters(power_station)
            if inverter_quantity <= 2:
                panel_board_counts[0] += power_station_count
            else:
                panel_board_counts[1] += power_station_count

            if self.power_station_has_monitor(power_station, monitors):
                panel_board_parts_to_use = panel_board_parts_with_monitor(inverter_quantity, proper_monitor_controller)
            else:
                panel_board_parts_to_use = panel_board_parts(inverter_quantity, with_aux=False)
            add_parts_to_list(part_list, panel_board_parts_to_use, power_station_count)
            add_parts_to_list(part_list, shared_panel_board_parts(module_type, system_type), power_station_count)
            add_parts_to_list(part_list, {channel_nut: 4}, power_station_count)

            for inverter in power_station['inverters']:
                inverter_count += power_station_count
                self.add_parts_for_inverter(part_list, inverter, power_station_count)
                add_parts_to_list(part_list, inverter_parts(inverter, module_type), power_station_count)

        for inverter in standalone_inverters:
            inverter_count += 1
            total_ac_run_length += inverter['ac_run_length']
            self.add_parts_for_inverter(part_list, inverter)
            add_parts_to_list(part_list, standalone_inverter_parts(inverter, system_type, module_type), 1)
            add_parts_to_list(part_list, inverter_parts(inverter, module_type), 1)
            if inverter['attachment_point'][1]:
                add_parts_to_list(part_list, standalone_inverter_attached_to_panel_board_parts, 1)

        for monitor in monitors:
            if monitor['power_source'][0] == 'Switch Gear/External':
                add_parts_to_list(part_list, {proper_monitor_controller: 1}, 1)

        if is_delta:
            clips_amount = inverter_count * self.row_count * 1.15
            clips_rounded = int(ceil(clips_amount / 10.0)) * 10
            add_parts_to_list(part_list, {wire_clip_large: clips_rounded})
        else:
            add_parts_to_list(part_list, {wire_clip_large: inverter_count}, self.row_count)

        add_parts_to_list(part_list, {stump: 1}, ceil(total_ac_run_length / 4.0))
        cable_supports = self.calculate_cable_supports(panel_board_counts, len(standalone_inverters), is_delta)
        add_parts_to_list(part_list, {cable_support: 1, cable_support_lid: 1}, cable_supports)
        add_parts_to_list(part_list, {rear_skirt_1_1: -1}, ceil(cable_supports*.38))

        dependent_part_list = {}

        for part, quantity in part_list.items():
            dependent_parts = ebom_parts.dependent_parts(module_type, system_type,is_delta).get(part)
            if dependent_parts:
                add_parts_to_list(dependent_part_list, dependent_parts, quantity)

        add_parts_to_list(part_list, dependent_part_list)

        return part_list

    def add_parts_for_inverter(self, part_list, inverter, multiplier=1):
        strings_per_inverter = inverter_strings_parts.get(inverter['strings_per_inverter'], {})
        add_parts_to_list(part_list, inverter_model_parts[inverter['model']], multiplier)
        add_parts_to_list(part_list, strings_per_inverter, multiplier)
        if inverter['sunshade']:
            add_parts_to_list(part_list, {sunshade: 1, sunshade_bolt: 2, sunshade_washer: 2}, multiplier)
        if inverter['dc_switch']:
            add_parts_to_list(part_list, dc_switch_parts, multiplier)

    def calculate_cable_supports(self, panel_board_counts, standalone_inverter_count, is_delta):
        # There are some tests that don't have panels
        if is_delta and self.panels is None:
            return 0
        if is_delta:
            panels_count = len(self.panels)
            if panels_count == 0:
                return 0
            if self.values.system_type() == SystemType.dualTilt:
                Avg_Columns = self.column_count / panels_count
                Col2Row_Ratio = self.column_count / self.row_count
                Col2Row_Ratio = 1 if Col2Row_Ratio < 1 else Col2Row_Ratio
                return ceil(standalone_inverter_count * 1.25 * Avg_Columns * Col2Row_Ratio + Avg_Columns)
            else:
                Avg_Rows = self.row_count / panels_count
                Row2Col_Ratio = self.row_count / self.column_count
                Row2Col_Ratio = 1 if Row2Col_Ratio < 1 else Row2Col_Ratio
                return ceil(standalone_inverter_count * 1.25 * Avg_Rows * Row2Col_Ratio + Avg_Rows)
        else:
            if sum(panel_board_counts) == 0:
                return 0
            if self.values.system_type() == SystemType.dualTilt:
                dimension1 = self.column_count
                dimension2 = self.row_count
            else:
                dimension1 = self.row_count
                dimension2 = self.column_count
            result = (standalone_inverter_count + panel_board_counts[0] + (2 * panel_board_counts[1])) / sum(panel_board_counts)
            result *= dimension1 * max(dimension1 / dimension2, 1)
            result += dimension1
            return ceil(result)

    def get_standalone_inverters(self, power_station):
        standalone_inverters = self.values.standalone_inverters()
        count = 0
        for inverter in standalone_inverters:
            if inverter['attachment_point'][1] == power_station['power_station_id']:
                count += 1
        return count

    def power_station_has_monitor(self, power_station, monitors):
        for monitor in monitors:
            if monitor['power_source'][1] == power_station['power_station_id']:
                return True
        return False
