#ifndef MEYRULE_H
#define MEYRULE_H

#include <Arduino.h>
#include <mcp2515.h>;

struct TargetPin{
  uint16_t devId;
  byte meyPinId;
  bool lastPinState = false;

};

struct Rule {
  uint16_t sourceDevId;
  byte sourceMeyPinId;
  TargetPin target = TargetPin();
  bool toggle;
  bool inverse;
  Rule *nextRule = NULL;
  bool triggeredOnce = false;

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

  void Traverse(uint16_t deviceId, uint8_t dt, uint8_t state, void (*handle)(uint16_t, uint8_t, uint8_t, Rule *)) {

    if (this->sourceDevId == deviceId && this->sourceMeyPinId == state)
      handle(deviceId, dt, state, this);

    if (this->nextRule != NULL)
      this->nextRule->Traverse(deviceId, dt, state, handle);
  }
};


typedef struct RemotePinInfo {
  const byte MAX_REMOTE_PIN_COUNT = 64;
  uint16_t DeviceId = 0;  //  the id of the device
  uint8_t pinState = 0;   // bitmap of 8 MeyPin states of the device. 0000 0100, MeyPin #3 is HIGH in this example
  RemotePinInfo *next = NULL;

  bool getPinState(byte meyPin) {
    return (this->pinState >> (meyPin - 1)) & 1;
  }

  void setPinState(byte meyPin, bool state) {
    if (state)
      this->pinState = this->pinState | (1 << (meyPin - 1));  // 0001 0000
    else
      this->pinState = this->pinState & (~(1 << (meyPin - 1)));  // 1110 1111 -> not
  }


  int16_t Count() {
    if (this->next == NULL) return 1;
    return this->next->Count() + 1;
  }

  RemotePinInfo *FindOrAdd(uint16_t deviceId, byte count = 0) {
    if (count > MAX_REMOTE_PIN_COUNT)
      return NULL;


    if (this->DeviceId == 0 && this->pinState == 0) {
      this->DeviceId = deviceId;
      this->pinState = 0;
    }

    if (this->DeviceId == deviceId) {
      //ToggleDebug();
      return this;
    }

    if (next != NULL) {
      return next->FindOrAdd(deviceId, count + 1);
    }

    RemotePinInfo *theNext = new RemotePinInfo;
    theNext->DeviceId = deviceId;
    theNext->pinState = 0;
    theNext->next = NULL;

    this->next = theNext;
    return this->next;
  }
};

extern RemotePinInfo remotePinInfo;
extern Rule *rules;

void SetLocalPinState(can_frame *frame);

void AddSimple(uint16_t sourceDevId, byte sourceMeyPinId, uint16_t targetDevId, byte targetMeyPinId);
void AddToggle(uint16_t sourceDevId, byte sourceMeyPinId, uint16_t targetDevId, byte targetMeyPinId);
void AddToggleInverse(uint16_t sourceDevId, byte sourceMeyPinId, uint16_t targetDevId, byte targetMeyPinId);
void HandleRules(can_frame *frame);
void PutRule(Rule *rule);
void CheckRule(uint16_t deviceId, uint8_t dt, uint8_t state, Rule *rule);

#endif
