import numpy as np


class DG:
    """simulate a simple diesel generator"""

    def __init__(self, parameters):
        self.current_output = None
        self.name = parameters.keys()
        self.a_factor = parameters['a']
        self.b_factor = parameters['b']
        self.c_factor = parameters['c']
        self.power_output_max = parameters['power_output_max']
        self.power_output_min = parameters['power_output_min']
        self.ramping_up = parameters['ramping_up']
        self.ramping_down = parameters['ramping_down']
        self.last_step_output = None

    def step(self, action_gen):
        output_change = action_gen * self.ramping_up  # constrain the output_change with ramping up boundary
        output = self.current_output + output_change
        if output > 0:
            output = max(self.power_output_min, min(self.power_output_max, output))  # meet the constraint
        else:
            output = 0
        self.current_output = output

    def get_cost(self, output):
        if output <= 0:
            cost = 0
        else:
            cost = (self.a_factor * pow(output, 2) + self.b_factor * output + self.c_factor)
        return cost

    def reset(self):
        self.current_output = 0


class Battery:
    """simulate a simple battery"""

    def __init__(self, parameters):
        self.current_capacity = None
        self.energy_change = None
        self.capacity = parameters['capacity']
        self.max_soc = parameters['max_soc']
        self.initial_capacity = parameters['initial_capacity']
        self.min_soc = parameters['min_soc']
        self.degradation = parameters['degradation']  # degradation cost 1.2
        self.max_charge = parameters['max_charge']  # max charge ability
        self.max_discharge = parameters['max_discharge']
        self.efficiency = parameters['efficiency']

    def step(self, action_battery):
        energy = action_battery * self.max_charge
        updated_capacity = max(self.min_soc,
                               min(self.max_soc, (self.current_capacity * self.capacity + energy) / self.capacity))
        # if charge, positive, if discharge, negative
        self.energy_change = (updated_capacity - self.current_capacity) * self.capacity
        self.current_capacity = updated_capacity  # update capacity to current codition

    def get_cost(self, energy):  # cost depends on the energy change
        cost = energy ** 2 * self.degradation
        return cost

    def SOC(self):
        return self.current_capacity

    def reset(self):
        self.current_capacity = np.random.uniform(0.2, 0.8)


class Solar:
    def __init__(self, parameters):
        self.current_power = None
        self.base_voltage = parameters['V_b']
        self.sc_current = parameters['I_sc0']
        self.oc_voltage = parameters['V_oc0']
        self.s_resistance = parameters['R_s']
        self.sh_resistance = parameters['R_sh']
        self.temper_coefficient = parameters['k_v']
        self.opex_cofficient = parameters['k_o']
        self.refer_irradiance = parameters['G_ref']
        self.refer_temperature = parameters['T_ref']

    def step(self, temperature, irradiance, action_voltage=0):
        I_sc = self.sc_current * (irradiance / self.refer_irradiance)
        V_oc = self.oc_voltage + self.temper_coefficient * (temperature - self.refer_temperature)

        current = I_sc - (V_oc / self.sh_resistance)
        # current = I_sc
        # for _ in range(10):  # 迭代次数
        #     new_current = I_sc - (V_oc + current * self.s_resistance) / self.sh_resistance
        #     if abs(new_current - current) < 1e-6:  # 收敛条件
        #         break
        #     current = new_current
        self.current_power = max((1 + action_voltage) * self.base_voltage * current, 0)
        return self.current_power

    def get_cost(self, current_power):
        cost = current_power * self.opex_cofficient
        return cost

    def reset(self):
        self.current_power = 0


class Wind:
    def __init__(self, parameters):
        self.current_power = None
        self.cutin_speed = parameters['cutin_speed']
        self.cutout_speed = parameters['cutout_speed']
        self.rated_speed = parameters['rated_speed']
        self.air_density = parameters['air_density']
        self.rotor_radius = parameters['rotor_radius']
        self.power_coefficient = parameters['power_coefficient']
        self.generator_efficiency = parameters['generator_efficiency']
        self.opex_cofficient = parameters['opex_cofficient']

    def step(self, wind_speed):
        if self.cutin_speed <= wind_speed < self.rated_speed:
            self.current_power = (0.5 * self.air_density * self.rotor_radius ** 2 * wind_speed ** 3 *
                                  self.power_coefficient * self.generator_efficiency) / 1e3
        elif self.rated_speed <= wind_speed < self.cutout_speed:
            self.current_power = (0.5 * self.air_density * self.rotor_radius ** 2 * self.rated_speed ** 3 *
                                  self.power_coefficient * self.generator_efficiency) / 1e3
        else:
            self.current_power = 0

        return self.current_power

    def gen_cost(self, current_power):
        cost = current_power * self.opex_cofficient
        return cost

    def reset(self):
        self.current_power = 0


class Grid:
    def __init__(self):
        self.on = True
        self.delta = 1
        if self.on:
            self.exchange_ability = 100
        else:
            self.exchange_ability = 0

    def get_cost(self, current_price, energy_exchange):
        return current_price * energy_exchange * self.delta

    def retrieve_past_price(self):
        result = []
        # 过去24小时的价格起始、结束索引
        start_index = max(0, 24 * (self.day - 1) + self.time - 24)
        end_index = 24 * (self.day - 1) + self.time
        past_price = self.price[start_index:end_index]
        result.extend(past_price)
        # current_day_price = self.price[24 * self.day:24 * self.day + self.time]
        # result.extend(current_day_price)
        return result

    # def retrive_past_price(self):
    #     result = []
    #     if self.day < 1:
    #         past_price = self.past_price
    #     else:
    #         past_price = self.price[24 * (self.day - 1):24 * self.day]
    #     for item in past_price[(self.time - 24)::]:
    #         result.append(item)
    #     for item in self.price[24 * self.day:(24 * self.day + self.time)]:
    #         result.append(item)
    #     return result
