#ifndef __RigState_h__
#define __RigState_h__

#include <Arduino.h>

#define UBITX_VFOA_UPDATE  0x00000001
#define UBITX_VFOB_UPDATE  0x00000002
#define UBITX_RIT_UPDATE   0x00000004
#define UBITX_XIT_UPDATE   0x00000008
#define UBITX_FLAGS_UPDATE 0x00000010

#define UBITX_VFOB_FLAG   0x00000001
#define UBITX_SPLIT_FLAG  0x00000002
#define UBITX_RIT_FLAG    0x00000004
#define UBITX_XIT_FLAG    0x00000008
#define UBITX_CW_FLAG     0x00000010
#define UBITX_USB_FLAG    0x00000020
#define UBITX_TX_FLAG     0x00000040

struct UBitxRigState {
  uint32_t header = 0;
  uint32_t vfo[2];
  int32_t rit;
  int32_t xit;
  uint32_t flags = 0;
};

/**********************************************************************/

template<typename T, int ID>
struct Field {
  byte id = ID;
  bool dirty;
  T data;

  inline size_t sizeOfWrite() { return dirty ? sizeof(byte) + sizeof(T) : 0; }
  
  template<typename STREAM> void writeChanges() {
    if (dirty) {
      STREAM().write(id);
      STREAM().write((byte*)&data, sizeof(T));
    }
  }

  template<typename STREAM> int read() {
    size_t len = 0;
    byte* ptr = (byte*)&data;
    while (STREAM().available() && len < sizeof(T)) {
      ptr[len++] = STREAM().read();
    }
    return len;
  }

  inline void merge(Field<T,ID>& f) {
    if (dirty) {
      f.data = data;
      f.dirty = true;
    } else if (f.dirty) {
      data = f.data;
      dirty = true;
    }
  }

  inline void markClean() { dirty = false; }
};

struct RigState {
  Field<uint32_t, 0>  vfoA;
  Field<uint32_t, 1>  vfoB;
  Field<int32_t, 2>   rit;
  Field<int32_t, 3>   xit;
  Field<uint32_t, 4>  flags;

  inline size_t sizeOfWrite() {
    size_t size = 0;
    size += vfoA.sizeOfWrite();
    size += vfoB.sizeOfWrite();
    size += rit.sizeOfWrite();
    size += xit.sizeOfWrite();
    size += flags.sizeOfWrite();
    return size;
  }

  template<typename STREAM> void writeChanges() {
    vfoA.writeChanges<STREAM>();
    vfoB.writeChanges<STREAM>();
    rit.writeChanges<STREAM>();
    xit.writeChanges<STREAM>();
    flags.writeChanges<STREAM>();
  }

  template<typename STREAM> void readChanges(size_t size) {
    size_t len = 0;
    while (STREAM().available() && len < size) {
      switch(STREAM().read()) {
        case 0:
        len += vfoA.read<STREAM>();
        break;
        
        case 1:
        len += vfoB.read<STREAM>();
        break;
        
        case 2:
        len += rit.read<STREAM>();
        break;
        
        case 3:
        len += xit.read<STREAM>();
        break;

        case 4:
        len += flags.read<STREAM>();
        break;

        default:
        ;        
      }
    }
  }

  inline void merge(RigState& r) {
    vfoA.merge(r.vfoA);
    vfoB.merge(r.vfoB);
    rit.merge(r.rit);
    xit.merge(r.xit);
    flags.merge(r.flags);
  }

  inline void markClean(RigState& r) {
    vfoA.markClean();
    vfoB.markClean();
    rit.markClean();
    xit.markClean();
    flags.markClean();
  }
};

/*
Protocol discussion:
- I2C master: Raduino
- I2C slave:  TeensyDSP

Raduino state:
  - Baseline uBITX variables
  - I2C buffer
    - On I2C transmit:  make updates based on current variables
    - On I2C receive:
      - Update based on received I2C responses
      - Update associated variables
    
TeensyDSP state:
  - CAT buffer
    - Used to receive command from CAT (when commands arrive via Serial)
    - Used to transmit state to Raduino (when requested via Wire1)
  - Raduino buffer
    - Used to receive state from Raduino (when received via Wire1)
    - Used to transmit responses to CAT (over Serial)
  - Questions
    - How can these be synchronized?
      - At the tail end of an I2C request handler.  Before sending the response to the Raduino via I2C:
        - Copy updated CAT buffer items to the Raduino buffer.
        - Copy updated Raduino buffer items to the CAT buffer.
        - In the case of conflicts, CAT wins.
        - Transmit the CAT buffer state to the Raduino.
- TeensyDSP updates 'outgoing' state based on CAT inputs.
  - Make change to data.
  - Mark data as dirty, if different than incoming state.
- When requested, Teensy DSP sends 'outgoing' state to Raduino.
  - Send dirty data over I2C.
  - Mark data as clean.  
 */
 
#endif
