/**
 * @file rx_motor.c
 * @brief Implementation of the motor functionality.
 *
 * This file contains the implementation of the motor functionality.
 * It provides functions to initialize the motor, drive the motor, and stop the motor.
 */

#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/devicetree.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/logging/log.h>
#include <zephyr/drivers/pwm.h>

#include "rx_motor.h"

LOG_MODULE_REGISTER(RX_MOTOR, LOG_LEVEL_INF);

#define RX_MOTOR_TICK_UP_DIVIDER 5

enum motor_dir
{
    MOTOR_DIR_CW,
    MOTOR_DIR_CCW,
};

static const struct gpio_dt_spec *phase;
static const struct pwm_dt_spec *enable;

rx_motor_t *motor;

/**
 * @brief Rotates the motor in the clockwise direction.
 */
void rx_motor_rotate_cw();

/**
 * @brief Rotates the motor in the counter-clockwise direction.
 */
void rx_motor_rotate_ccw();

/**
 * @brief Stops the motor.
 */
void rx_motor_stop();

/**
 * @brief Toggles the motor rotation direction.
 */
void rx_motor_toggle();

/**
 * @brief Sets the state of the motor.
 *
 * @param state The state to set the motor to.
 */
void rx_motor_set_state(rx_motor_state_t state);

/**
 * @brief Initializes the motor pins.
 */
static void rx_motor_init_pins();

/**
 * @brief Updates the PWM signal for the RX motor.
 *
 * This function is responsible for updating the PWM signal for the RX motor.
 * It should be called periodically to ensure smooth motor operation.
 */
void rx_motor_update_pwm();

/**
 * @brief Sets the period and duty cycle for the RX motor PWM signal.
 *
 * This function is used to set the period and duty cycle for the PWM signal
 * that controls the RX motor. The period determines the frequency of the PWM
 * signal, while the duty cycle determines the percentage of time the signal
 * is high compared to the total period.
 *
 * @param period The period of the PWM signal in microseconds.
 * @param duty_cycle The duty cycle of the PWM signal as a percentage (0-100).
 */
void rx_motor_set_pwm(uint32_t period, uint32_t duty_cycle);

/**
 * Sets the duty cycle of the RX motor.
 *
 * @param duty_cycle The duty cycle value to set.
 */
void rx_motor_set_duty_cycle(uint32_t duty_cycle);

/**
 * Sets the period of the RX motor.
 *
 * @param period The period value to set.
 */
void rx_motor_set_period(uint32_t period);

/**
 * Increases the tick count for the RX motor cycle.
 */
void rx_motor_cycle_tick_up();

/**
 * @brief Sets the name of the motor.
 *
 * @param name The name to set for the motor.
 */
void rx_motor_set_name(const char *name);

/**
 * @brief Initializes the motor.
 *
 * @param mtr The motor structure to initialize.
 * @param mtr_ph The GPIO specification for the motor phase pin.
 * @param mtr_en The PWM specification for the motor enable pin.
 */
void rx_motor_init(rx_motor_t *mtr, const struct gpio_dt_spec *mtr_ph, const struct pwm_dt_spec *mtr_en)
{
    LOG_INF("Initializing %s motor", mtr->name);
    phase = mtr_ph;
    enable = mtr_en;
    motor = mtr;
    motor->duty_cycle = 100U;
    motor->period = 500U;
    motor->set_dutycycle = rx_motor_set_duty_cycle;
    motor->set_period = rx_motor_set_period;
    motor->update_pwm = rx_motor_update_pwm;
    motor->rotate_cw = rx_motor_rotate_cw;
    motor->rotate_ccw = rx_motor_rotate_ccw;
    motor->stop = rx_motor_stop;
    motor->toggle = rx_motor_toggle;
    motor->cycle_tick_up = rx_motor_cycle_tick_up;
    // strncpy(motor->name, "RX Motor", 32);
    rx_motor_init_pins();
    LOG_INF("Initializing motor %s complete", motor->name);
}

void rx_motor_rotate_cw()
{
    rx_motor_set_state(MOTOR_RUN_CW);
}

void rx_motor_rotate_ccw()
{
    rx_motor_set_state(MOTOR_RUN_CCW);
}

void rx_motor_stop()
{
    rx_motor_set_state(MOTOR_STOP);
}

void rx_motor_toggle()
{
    switch (motor->state)
    {
    case MOTOR_STOP:
        rx_motor_rotate_cw();
        break;
    case MOTOR_RUN_CW:
        rx_motor_rotate_ccw();
        break;
    case MOTOR_RUN_CCW:
        rx_motor_stop();
        break;
    default:
        LOG_ERR("Invalid motor state");
        break;
    }
}

void rx_motor_set_state(rx_motor_state_t state)
{
    motor->state = state;
    switch (state)
    {
    case MOTOR_STOP:
        LOG_INF("%s stopped", motor->name);
        rx_motor_set_pwm(0, 0);
        break;
    case MOTOR_RUN_CW:
        LOG_INF("%s running CW", motor->name);
        gpio_pin_set_dt(phase, MOTOR_DIR_CW);
        rx_motor_update_pwm();
        break;
    case MOTOR_RUN_CCW:
        LOG_INF("%s running CCW", motor->name);
        gpio_pin_set_dt(phase, MOTOR_DIR_CCW);
        rx_motor_update_pwm();
        break;
    default:
        LOG_ERR("Invalid %s state", motor->name);
        break;
    }
}

static void rx_motor_init_pins()
{
    if (!pwm_is_ready_dt(enable))
    {
        LOG_ERR("PWM device is not ready");
    }

    if (!gpio_is_ready_dt(phase))
    {
        LOG_ERR("GPIO device is not ready");
    }

    if (gpio_pin_configure_dt(phase, GPIO_OUTPUT_HIGH))
    {
        LOG_ERR("gpio no available");
    }
}

void rx_motor_update_pwm()
{
    if (motor->state != MOTOR_STOP)
    {
        LOG_INF("setting %s pwm parameters", motor->name);
        rx_motor_set_pwm(motor->period, motor->duty_cycle);
    }
    else
    {
        LOG_WRN("%s is stopped, PWM signal not updated until motor is running", motor->name);
    }
}

void rx_motor_set_pwm(uint32_t period, uint32_t duty_cycle)
{
    if (pwm_set_dt(enable, PWM_USEC(period), PWM_USEC(duty_cycle)))
    {
        LOG_ERR("Error setting %s pwm", motor->name);
    }
}

void rx_motor_set_duty_cycle(uint32_t duty_cycle)
{
    motor->duty_cycle = duty_cycle;
    rx_motor_update_pwm();
}

void rx_motor_set_period(uint32_t period)
{
    motor->period = period;
    rx_motor_update_pwm();
}

void rx_motor_cycle_tick_up()
{
    motor->duty_cycle += motor->period / RX_MOTOR_TICK_UP_DIVIDER;
    if (motor->duty_cycle > motor->period)
    {
        motor->duty_cycle = motor->period / RX_MOTOR_TICK_UP_DIVIDER;
        LOG_WRN("Duty cycle is greater than period, setting %s duty cycle to %duS",motor->name, motor->duty_cycle);
    }
    rx_motor_update_pwm();
}
