use super::super::{
    constants::{END_BYTE, SERIAL_BUFFER_SIZE, START_BYTE},
    helpers::calculate_check_sum_up,
    types::{HPSendingFrame, SerialBuffer},
};
use crate::serial_buffer;
use serde::{Deserialize, Serialize};

// hysteresis -> difference between the current temperature and the desired temperature

#[derive(Debug, Serialize, Deserialize, Copy, Clone)]
pub struct HPConfig {
    // --- 0x81 first config ---
    /// A.1.1 -> 0 for off
    pub mode: u8,
    /// A.1.2 -> 1~7 -> the model of the heat pump (what modes it supports etc.) Fixed to 7 (Water tank plus room heating and cooling models)
    pub model: u8,
    /// A.1.3 -> Outdoor ambient temperature is too low, the shutdown protection temperature setting, transmission value = °C * 2 + 60. Electric heating start cycle temperature setting value(°C*2+60)
    pub shutdown_protection_temperature: i8,
    /// A.1.4 -> Water tank temperature setting, transmission value = °C.
    pub water_tank_temperature: u8,
    /// A.1.5 -> Fix as 0. Room heating type, 0 for the floor heating, 1 for the heat sink.
    pub heating_type: u8,
    /// A.1.6 -> Room floor set temperature, transmission value = °C.
    pub heating_floor_temperature: u8,
    /// A.1.7, Fix as 0. Electric heating water tank starting temperature, transmission value = °C.
    pub electric_heating_water_tank_starting_temperature: u8,
    /// A.1.8 -> Cooling mode Water temperature setting, transmission value = °C.
    pub cooling_temperature: u8,
    /// A.1.9 -> Water tank heating start hysteresis, transmission value = °C;
    pub water_tank_hysteresis: u8,
    /// A.1.10 -> Room heating start hysteresis, transmission value = °C.
    pub heating_hysteresis: u8,
    /// A.1.11 -> Fix as 60. Room heating floor heating water maximum temperature, transmission value = °C.
    pub heating_water_maximum_temperature: u8,
    /// A.1.12 -> Fix as 30. Room heating heat sink mode set temperature, transmission value = °C.
    pub heating_heat_sink_temperature: u8,
    /// A.1.13 -> Electric heating does not increase the temperature of the start-up time, transmission value = min (minutes).
    pub start_time_that_electric_heating_does_not_increase_the_water_temperature: u8,
    /// A.1.14 -> 0: automatic control of electric heating in room mode, 1: electric heating in room mode. Delete, fix at 0.
    pub room_electric_heating_mode: u8,

    // --- 0x82 second config ---
    /// A.2.1 -> Automatic defrost: defrost into the set temperature, transmission value = °C * 2 +60;
    pub defrost_into_temperature: i8,
    /// A.2.4 -> 0: automatic mode; 1: manual mode;
    pub defrost_mode: u8,
    /// A.2.5 -> Defrost Exit the set temperature, transfer value = °C * 2 + 60;
    pub defrost_exit_temperature: i8,
    /// A.2.6 -> Defrost maximum time, transfer value = min (minutes)
    pub defrost_maximum_time: u8,
    /// A.2.7 -> Defrost entry cycle set time, transfer value = min (minutes)
    pub defrost_entry_cycle_time: u8,
    /// A.2.8 -> Transmission value = °C;
    pub solar_water_pump_open_temperature_difference: u8,
    /// A.2.9 -> Off pump cycle transmission value = min (minutes)
    pub water_pump_off_cycle_time: u8,
    /// A.2.10 -> Transmission value = °C
    pub constant_temperature_difference: u8,
    /// A.2.11 -> Fixed sending 60. Water tank temperature correction: -5 °C ~ 5 °C, transmission value = °C +60.
    pub water_tank_temperature_correction: i8,
    /// A.2.12 -> Fixed sending 60. Water tank temperature correction: -5 °C ~ 5 °C, transmission value = °C +60.
    pub water_supply_temperature_correction: i8,
    /// A.2.13 -> Function flags
    pub function_flags: FunctionFlags,

    // --- 0x83 third config ---
    /// A.3.1 -> Set the heating into the defrost ambient temperature
    pub heating_into_the_defrost_ambient_temperature: u8,
    /// A.3.2 -> Start the defrosting ambient temperature and coil temperature difference
    pub defrosting_start_ambient_coil_temperature_difference: u8,
    /// A.3.3 -> Air conditioning mode water pump running mode
    pub ac_mode_water_pump_running_mode: u8,
    /// A.3.4 -> The ambient temperature corresponding to the outdoor fan
    pub ambient_temperature_to_outdoor_fan: u8,
    /// A.3.5 -> Frequency converter compressor maximum operating frequency
    pub converter_compressor_maximum_frequency: u8,
    /// A.3.6 -> Electronic expansion valve maximum opening degree
    pub expansion_valve_maximum_opening_degree: u8,
    /// A.3.7 -> Electronic expansion valve minimum opening degree
    pub expansion_valve_minimum_opening_degree: u8,
    /// A.3.8 -> The actual water temperature is greater than 26 parameters, each additional 1 degree, measured in the measured water temperature correction and then increase 0.5 degrees
    pub water_temperature_greater_26_value: u8,
    /// A.3.9 -> The ambient temperature is greater than 30 degrees, the measured water temperature correction value
    pub ambient_temperature_greater_30_water_temperature_correction_value: u8,
    /// A.3.10 -> The ambient temperature is greater than 23, less than 30 degrees, the measured water temperature correction value
    pub ambient_temperature_greater_23_and_less_30_water_temperature_correction_value: u8,
    /// A.3.11 -> The ambient temperature is less than 23 degrees, the measured water temperature correction value
    pub ambient_temperature_less_23_water_temperature_correction_value: u8,
}

impl HPConfig {
    /// default config in case the device is not configured and file not found
    pub fn new() -> Self {
        HPConfig {
            mode: 0,  // api
            model: 7, // fixed 7 -> model of the heat pump
            shutdown_protection_temperature: 0,
            water_tank_temperature: 0,                           // api
            heating_type: 0,                                     // api // fixed 0
            heating_floor_temperature: 0,                        // api
            electric_heating_water_tank_starting_temperature: 0, // fixed 0
            cooling_temperature: 0,                              // api
            water_tank_hysteresis: 0,                            // api
            heating_hysteresis: 0,                               // api
            heating_water_maximum_temperature: 60,               // api // fixed 60
            heating_heat_sink_temperature: 30,                   // api // fixed 30
            start_time_that_electric_heating_does_not_increase_the_water_temperature: 0,
            room_electric_heating_mode: 0,
            defrost_into_temperature: 0, // api
            defrost_mode: 0,             // api
            defrost_exit_temperature: 0, // api
            defrost_maximum_time: 0,     // api
            defrost_entry_cycle_time: 0, // api
            solar_water_pump_open_temperature_difference: 0,
            water_pump_off_cycle_time: 0,
            constant_temperature_difference: 0,
            water_tank_temperature_correction: 0, // fixed sending 60 (but it will add 60 at converting to sending frame, so correction is 0)
            water_supply_temperature_correction: 0, // fixed sending 60 (but it will add 60 at converting to sending frame, so correction is 0)
            function_flags: FunctionFlags::new(),
            heating_into_the_defrost_ambient_temperature: 0,
            defrosting_start_ambient_coil_temperature_difference: 0,
            ac_mode_water_pump_running_mode: 0,
            ambient_temperature_to_outdoor_fan: 0,
            converter_compressor_maximum_frequency: 0,
            expansion_valve_maximum_opening_degree: 0,
            expansion_valve_minimum_opening_degree: 0,
            water_temperature_greater_26_value: 0,
            ambient_temperature_greater_30_water_temperature_correction_value: 0,
            ambient_temperature_greater_23_and_less_30_water_temperature_correction_value: 0,
            ambient_temperature_less_23_water_temperature_correction_value: 0,
        }
    }

    /// Create the byte array for the first frame with function code 0x81
    pub fn create_first_frame(self) -> SerialBuffer {
        let mut buffer: SerialBuffer = serial_buffer!(HPSendingFrame::First as u8);
        buffer[1] = self.mode;
        buffer[2] = self.model;
        buffer[3] = (self.shutdown_protection_temperature * 2 + 60) as u8;
        buffer[4] = self.water_tank_temperature;
        buffer[5] = self.heating_type;
        buffer[6] = self.heating_floor_temperature;
        buffer[7] = self.electric_heating_water_tank_starting_temperature;
        buffer[8] = self.cooling_temperature;
        buffer[9] = self.water_tank_hysteresis;
        buffer[10] = self.heating_hysteresis;
        buffer[11] = self.heating_water_maximum_temperature;
        buffer[12] = self.heating_heat_sink_temperature;
        buffer[13] = self.start_time_that_electric_heating_does_not_increase_the_water_temperature;
        buffer[14] = self.room_electric_heating_mode;
        buffer[16] = calculate_check_sum_up(&buffer);
        buffer
    }

    /// Create the byte array for the second frame with function code 0x82
    pub fn create_second_frame(self) -> SerialBuffer {
        let mut buffer: SerialBuffer = serial_buffer!(HPSendingFrame::Second as u8);
        buffer[1] = (self.defrost_into_temperature * 2 + 60) as u8;
        buffer[4] = self.defrost_mode;
        buffer[5] = (self.defrost_exit_temperature * 2 + 60) as u8;
        buffer[6] = self.defrost_maximum_time;
        buffer[7] = self.defrost_entry_cycle_time;
        buffer[8] = self.solar_water_pump_open_temperature_difference;
        buffer[9] = self.water_pump_off_cycle_time;
        buffer[10] = self.constant_temperature_difference;
        buffer[11] = (self.water_tank_temperature_correction + 60) as u8;
        buffer[12] = (self.water_supply_temperature_correction + 60) as u8;
        buffer[13] = self.function_flags.transform_to_byte();
        buffer[16] = calculate_check_sum_up(&buffer);
        buffer
    }

    /// Create the byte array for the second frame with function code 0x82
    pub fn create_third_frame(self) -> SerialBuffer {
        let mut buffer: SerialBuffer = serial_buffer!(HPSendingFrame::Third as u8);
        buffer[1] = self.heating_into_the_defrost_ambient_temperature;
        buffer[2] = self.defrosting_start_ambient_coil_temperature_difference;
        buffer[3] = self.ac_mode_water_pump_running_mode;
        buffer[4] = self.ambient_temperature_to_outdoor_fan;
        buffer[5] = self.converter_compressor_maximum_frequency;
        buffer[6] = self.expansion_valve_maximum_opening_degree;
        buffer[7] = self.expansion_valve_minimum_opening_degree;
        buffer[8] = self.water_temperature_greater_26_value;
        buffer[9] = self.ambient_temperature_greater_30_water_temperature_correction_value;
        buffer[10] =
            self.ambient_temperature_greater_23_and_less_30_water_temperature_correction_value;
        buffer[11] = self.ambient_temperature_less_23_water_temperature_correction_value;
        buffer[16] = calculate_check_sum_up(&buffer);
        buffer
    }
}

impl Default for HPConfig {
    fn default() -> Self {
        Self::new()
    }
}

#[derive(Debug, Serialize, Deserialize, Copy, Clone)]
pub struct FunctionFlags {
    /// The linkage switch is selected, 0 is invalid, 1 is valid
    pub linkage_switch: u8,
    /// Electronic expansion valve adjustment mode, 0 automatic; 1 manually
    pub electronic_expansion_valve_adjustment_mode: u8,
    /// Compressor frequency adjustment mode, 0 automatic; 1 manually
    pub compressor_frequency_adjustment_mode: u8,
    /// Manual defrost flag, 0 invalid; 1 active
    pub manual_defrost: u8,
    /// Antifreeze switch, 0 invalid; 1 valid   
    pub antifreeze_switch: u8,
}

impl FunctionFlags {
    pub fn new() -> Self {
        FunctionFlags {
            linkage_switch: 0,
            electronic_expansion_valve_adjustment_mode: 0,
            compressor_frequency_adjustment_mode: 0,
            manual_defrost: 0,
            antifreeze_switch: 0,
        }
    }

    pub fn transform_to_byte(self) -> u8 {
        let mut byte = 0;
        byte |= self.linkage_switch;
        byte |= self.electronic_expansion_valve_adjustment_mode << 1;
        byte |= self.compressor_frequency_adjustment_mode << 2;
        byte |= self.manual_defrost << 3;
        byte |= self.antifreeze_switch << 4;
        byte
    }
}

impl Default for FunctionFlags {
    fn default() -> Self {
        Self::new()
    }
}
