#include "modbus_handler.h"

#include <string.h>

#include "debug.h"
#include "systick.h"

static struct {
  modbus_context_t* ctx;
  modbus_value_cb value_cb;
  uint8_t last_device_idx;
  char last_property[16];
} handler;

static void on_modbus_response(uint8_t* buf __attribute__((unused)),
                               uint16_t len __attribute__((unused)),
                               uint16_t value) {
  if (handler.last_device_idx >= RS485_DEVICE_COUNT || !handler.value_cb) {
    return;
  }

  // call the value callback with parsed data
  handler.value_cb(handler.last_device_idx, handler.last_property, value);

  // clear the context
  handler.last_device_idx = 0xFF;
  handler.last_property[0] = '\0';
}

static void on_modbus_error(uint8_t error_code __attribute__((unused))) {
  // clear the context on error
  handler.last_device_idx = 0xFF;
  handler.last_property[0] = '\0';
}

void modbus_handler_init(modbus_context_t* ctx,
                         modbus_value_cb value_callback) {
  handler.ctx = ctx;
  handler.value_cb = value_callback;
  handler.last_device_idx = 0xFF;
  handler.last_property[0] = '\0';

  // Initialize modbus with our callbacks
  modbus_init(ctx, on_modbus_response, on_modbus_error);
  modbus_set_timeout(ctx, 200);
}

void modbus_handler_process(void) { modbus_process(handler.ctx); }

// Get Modbus register for specific device type and property
static uint16_t get_register_address(device_type_t type, const char* property) {
  if (type == DEVICE_RELAY && strcmp(property, "state") == 0) {
    return 0x0000;
  }

  return 0xFFFF;
}
uint8_t modbus_handler_send_request(uint8_t device_idx, const char* property,
                                    uint8_t is_write, uint16_t value) {
  if (device_idx >= RS485_DEVICE_COUNT) {
    return 0;
  }

  uint16_t reg = get_register_address(RS485_DEVICES[device_idx].type, property);
  if (reg == 0xFFFF) {
    return 0;
  }

  uint8_t fc = is_write ? MODBUS_FC_WRITE_SINGLE_REGISTER
                        : MODBUS_FC_READ_HOLDING_REGISTERS;

  if (modbus_send_request(handler.ctx, RS485_DEVICES[device_idx].slave_id, fc,
                          reg, value)) {
    handler.last_device_idx = device_idx;
    strncpy(handler.last_property, property, sizeof(handler.last_property) - 1);
    handler.last_property[sizeof(handler.last_property) - 1] = '\0';
    return 1;
  }

  return 0;
}