# MicroPython implementation of fuzzy temperature control
# Converted from original C implementation (fuzzy.C)
# Original author: xukaiming
# Original date: 2020.09.01

# Constants
PWM_PERIOD = 4095  # PWM period
PWM_PITCED = 0  # PWM minimum

# Control constants
KC = 1  # Control difference
Ki = 2  # Integral coefficient
KI = 80  # KI value (can be 60 or 80 depending on temperature)
Kp = 1  # Proportional coefficient
MAXNEGINTERG = -1000  # Maximum negative integral


class TestPara:
    """Test parameters structure"""

    def __init__(self):
        self.PID_XISHU = 0
        self.PID_KP = 0
        self.PID_TD = 0
        self.GR_Tempr = 0
        self.ConPower = 0
        self.n = 0
        self.GR_Time = 0
        self.JG_TIme = 0


# Global test parameters
A_Test = TestPara()


class AI_Control:
    """AI control structure for fuzzy temperature control"""

    def __init__(self):
        self.WarmFlag = 0  # Heating flag
        self.iDestTemp = 0  # Target temperature
        self.gTemp = None  # Current temperature pointer
        self.CTEMP = 0  # Initial control temperature
        self.speed = 0  # Target speed

        self.Error = [0, 0]  # Error values (current and previous)
        self.dErr = 0  # Error change rate

        self.SumErrLimit = 0  # Integral value
        self.PreOut = 0  # Previous output
        self.CurOut = 0  # Current output


# Global AI control instance
Ai_CTRL = AI_CONTROL()


def pid_out_start(pid_output_data):
    """
    Function to start PID output with given data
    This function should be implemented according to the hardware requirements
    """
    # Implementation depends on hardware
    # In MicroPython, this might involve setting a PWM pin
    # For example:
    # from machine import Pin, PWM
    # pwm = PWM(Pin(0))  # PWM on pin 0
    # pwm.duty(pid_output_data)
    pass


def set_p_out(power):
    """Set power output"""
    databuff = 0
    data_f_buff = 0.0

    if power > 0:
        data_f_buff = power
        data_f_buff /= 11.38
        databuff = int(data_f_buff)
    else:
        databuff = 0

    pid_out_start(databuff)


def task_fuzzy():
    """Fuzzy control task"""
    global Ai_CTRL, KI

    if Ai_CTRL.WarmFlag:
        Ai_CTRL.Error[1] = Ai_CTRL.Error[0]
        temp = Ai_CTRL.gTemp[0]  # Assuming gTemp is now a list or array
        Ai_CTRL.Error[0] = ((Ai_CTRL.iDestTemp - temp) / KC)  # Calculate error
        Ai_CTRL.dErr = Ai_CTRL.Error[0] - Ai_CTRL.Error[1]  # Calculate error change

        if Ai_CTRL.Error[0] < Ai_CTRL.CTEMP:
            # Error is within control range
            if ((Ai_CTRL.Error[0] * Ai_CTRL.dErr) > 0) or ((Ai_CTRL.Error[0] == 0) and (Ai_CTRL.dErr != 0)):
                # Error is increasing
                Ai_CTRL.SumErrLimit += Ai_CTRL.Error[0]  # Accumulate error
                if Ai_CTRL.SumErrLimit < MAXNEGINTERG:
                    Ai_CTRL.SumErrLimit = MAXNEGINTERG

                # Calculate output - proportional + integral
                Ai_CTRL.CurOut = Ai_CTRL.Error[0] * Kp + Ai_CTRL.SumErrLimit * Ki * Kp / KI
            else:
                # Error is decreasing or stable
                if ((Ai_CTRL.Error[0] * Ai_CTRL.dErr) < 0) or (Ai_CTRL.dErr == 0):
                    # Error is decreasing
                    Ai_CTRL.CurOut = Ai_CTRL.SumErrLimit * Ki * Kp / KI
                else:
                    Ai_CTRL.CurOut = Ai_CTRL.PreOut
        else:
            # Error is outside control range - use direct calculation
            Ai_CTRL.CurOut = int(Ai_CTRL.iDestTemp / 10.0 / float(A_Test.PID_XISHU) * PWM_PERIOD / 4.0)

        # Limit output range
        if Ai_CTRL.CurOut < 0:
            Ai_CTRL.CurOut = 0
        if Ai_CTRL.CurOut > PWM_PERIOD:
            Ai_CTRL.CurOut = PWM_PERIOD

        # Set output and save previous value
        set_p_out(Ai_CTRL.CurOut)
        Ai_CTRL.PreOut = Ai_CTRL.CurOut
    else:
        set_p_out(PWM_PITCED)


def init_fuzzy():
    """Initialize fuzzy control"""
    global Ai_CTRL

    # Reset all values in Ai_CTRL to zero
    Ai_CTRL = AI_CONTROL()
    stop_heat()


def stop_heat():
    """Stop heating"""
    global Ai_CTRL

    Ai_CTRL.WarmFlag = 0
    set_p_out(PWM_PITCED)


def ctrl_stove(c_open, cur_temp, l_dest_temp, speed):
    """
    Control stove function

    Args:
        c_open: Open control flag
        cur_temp: Pointer to current temperature
        l_dest_temp: Target temperature
        speed: Control speed
    """
    global Ai_CTRL, KI

    init_fuzzy()
    Ai_CTRL.WarmFlag = c_open

    if c_open:
        # Start heating
        Ai_CTRL.gTemp = cur_temp  # Current temperature
        Ai_CTRL.iDestTemp = l_dest_temp  # Target temperature
        Ai_CTRL.speed = speed  # Control speed

        # Pre-set to 380°C
        Ai_CTRL.CTEMP = 3800
        Ai_CTRL.SumErrLimit = 0

        # Adjust KI based on target temperature
        if l_dest_temp > 400:
            KI = 80
        else:
            KI = 60
    else:
        stop_heat()

# Example of how to use the fuzzy controller:
#
# # Initialize the controller
# init_fuzzy()
#
# # Set up temperature reference
# current_temp = [250]  # Current temperature in tenths of a degree
# target_temp = 3000    # Target temperature (300.0°C)
#
# # Start heating
# ctrl_stove(1, current_temp, target_temp, 1)
#
# # In a loop, update current temperature and call task_fuzzy()
# # while True:
# #     # Update current_temp based on sensor readings
# #     current_temp[0] = read_temperature()
# #     task_fuzzy()
# #     time.sleep_ms(500)  # Run every 500ms
