#ifndef __INVERTERS_H
#define __INVERTERS_H

// Ausgabe von Debug Infos auf der seriellen Console

#include "Settings.h"
#include "Debug.h"


typedef struct _NRF24_packet_t {
  uint32_t timestamp;
  uint8_t  packetsLost;
  uint8_t  rcvChannel;
  uint8_t  packet[MAX_RF_PAYLOAD_SIZE];
} NRF24_packet_t;


typedef struct _Serial_header_t {
  unsigned long timestamp;
  uint8_t  packetsLost;
  uint8_t  address[RF_MAX_ADDR_WIDTH];    // MSB first, always RF_MAX_ADDR_WIDTH bytes.
} Serial_header_t;


// structs für Inverter und Kanalwerte

// Liste der Einheiten
enum UNITS {UNIT_V = 0, UNIT_HZ, UNIT_A, UNIT_W,  UNIT_WH, UNIT_C, UNIT_KWH, UNIT_MA, UNIT_PCT};
const char* const units[] = {"V", "Hz", "A", "W", "Wh", "°C", "KWh", "mA", "%"};

// CH0 is default channel (freq, ac, temp)
enum CHANNELS {CH0 = 0, CH1, CH2, CH3, CH4};
enum CMDS     {CMD01 = 0x01, CMD02, CMD03, CMD83 = 0x83, CMD84};
enum DIVS     {DIV1 = 0, DIV10, DIV100, DIV1000};

#define BYTES2        2
#define BYTES4        4

const char UDC[]      PROGMEM = "Udc";
const char IDC[]      PROGMEM = "Idc";
const char PDC[]      PROGMEM = "Pdc";
const char E_WOCHE[]  PROGMEM = "E-Woche";
const char E_TOTAL[]  PROGMEM = "E-Total";
const char E_TAG[]    PROGMEM = "E-Tag";
const char UAC[]      PROGMEM = "Uac";
const char FREQ[]     PROGMEM = "Freq.ac";
const char PAC[]      PROGMEM = "Pac";
const char E_HEUTE[]  PROGMEM  = "E-heute";
const char IPV[]      PROGMEM  = "Ipv";
const char WR_TEMP[]  PROGMEM = "WR-Temp";
const char PERCNT[]   PROGMEM = "Pct";

#define IDX_UDC       0
#define IDX_IDC       1
#define IDX_PDC       2
#define IDX_E_WOCHE   3
#define IDX_E_TOTAL   4
#define IDX_E_TAG     5
#define IDX_UAC       6
#define IDX_FREQ      7
#define IDX_PAC       8
#define IDX_E_HEUTE   9
#define IDX_IPV      10
#define IDX_WR_TEMP  11
#define IDX_PERCNT   12    

const char* const NAMES[] 
  = {UDC, IDC, PDC, E_WOCHE, E_TOTAL, E_TAG, UAC, FREQ, PAC, E_HEUTE, IPV, WR_TEMP, PERCNT};

typedef float (*calcValueFunc)(float *);

struct measureDef_t {
  uint8_t     nameIdx;        //const char* name;           // Zeiger auf den Messwertnamen
  uint8_t     channel;        // 0..4, 
  uint8_t     unitIdx;        // Index in die Liste der Einheiten 'units'
  uint8_t     teleId;         // Telegramm ID, das was hinter der 2. WR Nummer im Telegramm, 02, 03, 83
  uint8_t     pos;            // ab dieser POsition beginnt der Wert (Big Endian)
  uint8_t     bytes;          // Anzahl der Bytes
  uint8_t     digits;
};

struct measureCalc_t {
  uint8_t     nameIdx;        //const char* name;           // Zeiger auf den Messwertnamen
  uint8_t     unitIdx;        // Index in die Liste der Einheiten 'units'
  uint8_t     digits;
  calcValueFunc f;            // die Funktion zur Berechnung von Werten, zb Summe von Werten
};


struct inverter_t {
  uint8_t       ID;                       // Inverter-ID = Index
  char          name[20];                 // Name des Inverters zb HM-600.1
  uint64_t      serialNo;                 // dier Seriennummer wie im Barcode auf dem WR, also 1141.....
  uint64_t      RadioId;                  // die gespiegelte (letzte 4 "Bytes") der Seriennummer
  const measureDef_t  *measureDef;              // aus Include HMxxx.h : Liste mit Definitionen der Messwerte, wie Telgramm, offset, länge, ...
  uint8_t       anzMeasures;              // Länge der Liste
  measureCalc_t *measureCalculated;       // Liste mit Defintion für berechnete Werte
  uint8_t       anzMeasureCalculated;     // Länge der Liste
  uint8_t       anzTotalMeasures;         // Gesamtanzahl Messwerte
  float         values[MAX_MEASURE_PER_INV];  // DIE Messewerte
};


char _buffer[20];

uint8_t anzInv = 0;
inverter_t inverters[MAX_ANZ_INV];
  
union longlongasbytes {
  uint64_t ull;
  uint32_t ul[2];
  uint8_t bytes[8];  
};

char *uint64toa (uint64_t s) {
//--------------------------------  
//0x1141 72607952ULL   
  sprintf(_buffer, "%lX%08lX", (unsigned long)(s>>32), (unsigned long)(s&0xFFFFFFFFULL));
  return _buffer;
}


uint64_t Serial2RadioID (uint64_t sn) {   
//----------------------------------
  longlongasbytes llsn;
  longlongasbytes res;
  llsn.ull = sn;
  res.ull = 0;
  res.bytes[4] = llsn.bytes[0];
  res.bytes[3] = llsn.bytes[1];
  res.bytes[2] = llsn.bytes[2];
  res.bytes[1] = llsn.bytes[3];
  res.bytes[0] = 0x01;
  return res.ull;
}


void addInverter (uint8_t _ID, const char * _name, uint64_t _serial, 
                  const measureDef_t * liste, int anzMeasure,
                  measureCalc_t * calcs, int anzMeasureCalculated) {
//-------------------------------------------------------------------------------------
  if (anzInv >= MAX_ANZ_INV) {
    DEBUG_OUT.println(F("ANZ_INV zu klein!"));
    return;
  }
  inverter_t *p = &(inverters[anzInv]);
  p->ID                   = _ID;
  strcpy (p->name, _name);
  p->serialNo             = _serial;
  p->RadioId              = Serial2RadioID(_serial);
  p->measureDef           = liste;
  p->anzMeasures          = anzMeasure;
  p->anzMeasureCalculated = anzMeasureCalculated;
  p->measureCalculated    = calcs;
  p->anzTotalMeasures     = anzMeasure + anzMeasureCalculated;
  memset (p->values, 0, sizeof(p->values));

  DEBUG_OUT.print (F("WR       : "));      DEBUG_OUT.println(anzInv);
  DEBUG_OUT.print (F("Type     : "));      DEBUG_OUT.println(_name);
  DEBUG_OUT.print (F("Serial   : "));      DEBUG_OUT.println(uint64toa(_serial));
  DEBUG_OUT.print (F("Radio-ID : "));      DEBUG_OUT.println(uint64toa(p->RadioId));

  anzInv++;
}


static uint8_t toggle = 0;           // nur für Test, ob's auch für mehere WR funzt
uint8_t findInverter (uint8_t *fourbytes) {
//---------------------------------------
  for (uint8_t i = 0; i < anzInv; i++) {
    longlongasbytes llb;
    llb.ull = inverters[i].serialNo;  
    if (llb.bytes[3] == fourbytes[0] && 
        llb.bytes[2] == fourbytes[1] && 
        llb.bytes[1] == fourbytes[2] &&
        llb.bytes[0] == fourbytes[3] )
      {
        return i;
        //if (toggle) toggle = 0; else toggle = 1; return toggle;     // Test ob mehr WR  auch geht
      }
  }
  return 0xFF;      // nicht gefunden
}


char * error = {"error"};

char *getMeasureName (uint8_t wr, uint8_t i){
//------------------------------------------
  inverter_t *p = &(inverters[wr]);
  if (i >= p->anzTotalMeasures) return error;
  uint8_t idx, channel = 0;
  if (i < p->anzMeasures) {
    idx = p->measureDef[i].nameIdx;  
    channel = p->measureDef[i].channel;
  }
  else {
    idx = p->measureCalculated[i - p->anzMeasures].nameIdx;  
  }
  char tmp[20];
  strcpy_P (_buffer, NAMES[idx]);
  if (channel) {
    sprintf_P (tmp, PSTR(".CH%d"), channel);
    strcat(_buffer,tmp);
  }
  return _buffer;
}

const char *getUnit (uint8_t wr, uint8_t i) {
//------------------------------------------
  inverter_t *p = &(inverters[wr]);
  if (i >= p->anzTotalMeasures) return error;
  uint8_t idx;
  if (i < p->anzMeasures)
    idx = p->measureDef[i].unitIdx;
  else
    idx = p->measureCalculated[i-p->anzMeasures].unitIdx;
  
  //strcpy (_buffer, units[i]);
  //return _buffer;
  return units[idx];
}


float getMeasureValue (uint8_t wr, uint8_t i) {
//------------------------------------------
  if (i >= inverters[wr].anzTotalMeasures) return 0.0;
  return inverters[wr].values[i];
}


int getDivisor (uint8_t wr, uint8_t i) {
//------------------------------------
  inverter_t *p = &(inverters[wr]);
  if (i >= p->anzTotalMeasures) return 1;
  if (i < p->anzMeasures) {
    uint8_t digits = p->measureDef[i].digits;
    if (digits == DIV1) return 1;
    if (digits == DIV10) return 10;
    if (digits == DIV100) return 100;
    if (digits == DIV1000) return 1000;
    return 1;
  }
  else
    return p->measureCalculated[i].digits;  
}


uint8_t getDigits (uint8_t wr, uint8_t i) {
//---------------------------------------  
  inverter_t *p = &(inverters[wr]);
  if (i >= p->anzTotalMeasures) return 0;
  if (i < p->anzMeasures) 
    return p->measureDef[i].digits;
  else
    return p->measureCalculated[i-p->anzMeasures].digits;  
}

// +++++++++++++++++++++++++++++++++++    Inverter    ++++++++++++++++++++++++++++++++++++++++++++++

#include "HM600.h"            // für HM-600 und HM-700

#include "HM1200.h"

// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


void setupInverts() {
//-----------------  

  addInverter (0,"HM-600", 0x114172607952ULL, 
               hm600_measureDef, HM600_MEASURE_LIST_LEN,     // Tabelle der Messwerte
               hm600_measureCalc, HM600_CALCED_LIST_LEN); // Tabelle berechnete Werte

/*
  addInverter (1,"HM-1200", 0x114172607952ULL, 
               hm1200_measureDef, HM1200_MEASURE_LIST_LEN,     // Tabelle der Messwerte
               hm1200_measureCalc, HM1200_CALCED_LIST_LEN);    // Tabelle berechnete Werte
*/
}


#endif