#ifndef MEYCAN_H
#define MEYCAN_H

#include <SPI.h>
#include <mcp2515.h>

typedef struct CanInterface
{
  MCP2515 *interface;
  CanInterface *next = NULL;

  void ForEachInterface(void (*handle)( MCP2515 *interface))
  {
     if (this->interface != NULL)
        handle(this->interface);

    if (this->next != NULL)
      this->next->ForEachInterface(handle);
  }
  
  void ForEach(void (*handle)( MCP2515 *interface, can_frame *frame), can_frame *frame, MCP2515* exclude = NULL)
  {
    if (this->interface != NULL && this->interface != exclude)
        handle(this->interface, frame);

    if (this->next != NULL)
      this->next->ForEach(handle, frame, exclude);
  }

  void AddCanInterface(CanInterface *newCanInterface)
  {
    if (next == NULL)
    {
      this->next = newCanInterface;
      newCanInterface->next = NULL;
    } else {
      next->AddCanInterface(newCanInterface);
    }
  }
};

typedef struct PinState
{
  int pin_id;
  bool pin_state;
  bool is_input;
  byte meyPinId;
  PinState *next = NULL;

  PinState() {}
  void Init(int pin_id, byte meyPinId)
  {
    this->pin_id = pin_id;
    this->pin_state = true;
    this->is_input = true;
    this->meyPinId = meyPinId;
  }

  void AddPinState(PinState *nextPinState)
  {
    if (next == NULL)
    {
      this->next = nextPinState;
      nextPinState->next = NULL;
    } else {
      next->AddPinState(nextPinState);
    }
  }

  PinState* Find(byte meyPinId)
  {
    if (this->meyPinId == meyPinId)
      return this;

    if (this->next != NULL)
      return this->next->Find(meyPinId);

    return NULL;
  }

  void ForEach(void (*handle)(PinState *theState))
  {
    handle(this);
    if (this->next != NULL)
      this->next->ForEach(handle);
  }
};


const byte SOFTWARE_VERSION_HIGH = 5;
const byte SOFTWARE_VERSION_LOW = 0;
const byte HARDWARE_VERSION_HIGH = 7;
const byte HARDWARE_VERSION_LOW = 0;

const uint16_t HELP_PACKAGE_CAN_ID = 0x0FFFUL;
const uint16_t SWITCH_TRIGGERED_CAN_ID = 0x0050;
const uint16_t TRIGGER_SWITCH_CAN_ID = 0x0055;
const uint16_t DEBUG_PKG_CAN_ID = 0x0009;

void SetDevicedId(byte high, byte low);
void SetMeyPin(byte meyPinId, byte pinId);

// we need to put the local change to the rules engine. otherwise local changes on a pin will not come to the rules state.
void AddLocalPinChangedHandler(void (*localPinChangedHandler)(can_frame *frame));


void AddCanInterface(byte pinId);

void SetupMeyCan(byte majorHardwareVersion, byte minorHardwareVersion, byte deviceTypeId);

void ForEachCanInterface(void (*handle)(MCP2515 *canInterace));
bool ReadPin(PinState *state);
bool CheckPinStatus(PinState * state);
void CheckMeyPinsTriggered(); /* checks weather a meypin triggered and sends a can pkg is neccessary */
void HandleFrame(can_frame *frame, MCP2515 *source);
void HandleTriggerMeypinCanPackage(can_frame *frame);
byte CircularShift(byte b);
uint16_t GetDeviceId(uint32_t canFrameId);
uint16_t GetPackageType(uint32_t canFrameId);
byte GetDeviceIdLow();

byte GetDeviceIdHigh();

uint32_t CreateCanId(uint16_t commandId);
void SendVersionPackage(MCP2515 *interface);

void SendSwitchedTriggeredCanPackage(byte pinId, int state);


void BroadcastDebugPackage(byte b0, byte b1);
void BroadcastDebugPackage(byte b0, byte b1, byte b2);
void BroadcastTriggerMeyPinCanPackage(uint16_t targetCanId, byte pinId, byte state);

void DoSendCanPkg(can_frame *frame);
void DoSendCanPkg(MCP2515 *interface, can_frame *frame);

void CalculateMyDeviceId();

#endif
