#include "MeyRule.h"
#include "MeyCan.h"
#include <Arduino.h>
#include <mcp2515.h>;

RemotePinInfo remotePinInfo = RemotePinInfo();
Rule *rules = NULL;

void PutRule(Rule *rule) {
  if (rules == NULL)
    rules = rule;
  else
    rules->AddRule(rule);
}

void AddSimple(uint16_t sourceDevId, byte sourceMeyPinId, uint16_t targetDevId, byte targetMeyPinId) {
  Rule *rule = new Rule();

  rule->sourceDevId = sourceDevId;
  rule->sourceMeyPinId = sourceMeyPinId;
  rule->target.devId = targetDevId;
  rule->target.meyPinId = targetMeyPinId;
  rule->target.lastPinState = false;
  rule->toggle = false;
  rule->inverse = false;

  PutRule(rule);
}

void AddToggle(uint16_t sourceDevId, byte sourceMeyPinId, uint16_t targetDevId, byte targetMeyPinId) {
  Rule *rule = new Rule();

  rule->sourceDevId = sourceDevId;
  rule->sourceMeyPinId = sourceMeyPinId;
  rule->target.devId = targetDevId;
  rule->target.meyPinId = targetMeyPinId;
  rule->target.lastPinState = false;
  rule->toggle = true;
  rule->inverse = false;

  PutRule(rule);
}

void AddToggleInverse(uint16_t sourceDevId, byte sourceMeyPinId, uint16_t targetDevId, byte targetMeyPinId) {
  Rule *rule = new Rule();

  rule->sourceDevId = sourceDevId;
  rule->sourceMeyPinId = sourceMeyPinId;
  rule->target.devId = targetDevId;
  rule->target.meyPinId = targetMeyPinId;
  rule->target.lastPinState = false;
  rule->toggle = true;
  rule->inverse = true;

  PutRule(rule);
}



void CheckRule(uint16_t deviceId, uint8_t dt, uint8_t state, Rule *rule) {


  RemotePinInfo *currentRemotePinInfo = remotePinInfo.FindOrAdd(rule->target.devId);

  if (currentRemotePinInfo == NULL) return;

  // check the new state with the last one. Needs to be done, as the UP switch might
  // send it multiple times like "01 01" "01 01" for a single tip.(antibeat)
  bool lastPinState = rule->target.lastPinState;
  bool pinState = dt > 0;

  if (rule->triggeredOnce && lastPinState == pinState) return;
  
  rule->target.lastPinState = pinState;
  rule->triggeredOnce = true;

  if (rule->inverse)
    pinState = !pinState;

  if (rule->toggle)
    pinState = (currentRemotePinInfo->getPinState(rule->target.meyPinId) ^ true);

  currentRemotePinInfo->setPinState(rule->target.meyPinId, pinState);
  BroadcastTriggerMeyPinCanPackage(rule->target.devId, rule->target.meyPinId, pinState);
}

void HandleTriggered(can_frame *frame) {
  if (GetPackageType(frame->can_id) == SWITCH_TRIGGERED_CAN_ID) {
    RemotePinInfo *currentPinState = remotePinInfo.FindOrAdd(GetDeviceId(frame->can_id));

    if (currentPinState == NULL) {

      return;
    }

    currentPinState->setPinState(frame->data[0], frame->data[1]);
  }
}

void SetLocalPinState(can_frame *frame)
{
  HandleTriggered(frame);
}

void HandleRules(can_frame *frame) {

  if (rules != NULL) {
    if (GetPackageType(frame->can_id) == SWITCH_TRIGGERED_CAN_ID) {
      uint16_t deviceId = GetDeviceId(frame->can_id);
      uint8_t dt = frame->data[1];
      uint8_t state = frame->data[0];

      rules->Traverse(deviceId, dt, state, CheckRule);
    }
  }
  HandleTriggered(frame);
}
