#include <functional>

const uint8_t packet1[] {0x1B, 0x1B, 0x1B, 0x1B, 0x01, 0x01, 0x01, 0x01, 0x76, 0x05, 0xA6, 0x9F, 0x1A, 0x03, 0x62, 0x00, 0x62, 0x00, 0x72, 0x63, 0x01, 0x01, 0x76, 0x01, 0x01, 0x02, 0x31, 0x0B, 0x0A, 0x01, 0x44, 0x5A, 0x47, 0x00, 0x03, 0xA0, 0x86, 0xBC, 0x72, 0x62, 0x01, 0x65, 0x01, 0x08, 0xE0, 0x31, 0x62, 0x02, 0x63, 0xA6, 0xDF, 0x00, 0x76, 0x05, 0xA7, 0x9F, 0x1A, 0x03, 0x62, 0x00, 0x62, 0x00, 0x72, 0x63, 0x07, 0x01, 0x77, 0x01, 0x0B, 0x0A, 0x01, 0x44, 0x5A, 0x47, 0x00, 0x03, 0xA0, 0x86, 0xBC, 0x07, 0x01, 0x00, 0x62, 0x0A, 0xFF, 0xFF, 0x72, 0x62, 0x01, 0x65, 0x01, 0x08, 0xE0, 0x31, 0x75, 0x77, 0x07, 0x01, 0x00, 0x60, 0x32, 0x01, 0x01, 0x01, 0x72, 0x62, 0x01, 0x62, 0x00, 0x62, 0x00, 0x52, 0x00, 0x04, 0x44, 0x5A, 0x47, 0x01, 0x77, 0x07, 0x01, 0x00, 0x60, 0x01, 0x00, 0xFF, 0x01, 0x72, 0x62, 0x01, 0x62, 0x00, 0x62, 0x00, 0x52, 0x00, 0x0B, 0x0A, 0x01, 0x44, 0x5A, 0x47, 0x00, 0x03, 0xA0, 0x86, 0xBC, 0x01, 0x77, 0x07, 0x01, 0x00, 0x01, 0x08, 0x00, 0xFF, 0x64, 0x1C, 0x41, 0x04, 0x72, 0x62, 0x01, 0x62, 0x00, 0x62, 0x1E, 0x52, 0xFF, 0x65, 0x01, 0x27, 0x8A, 0x37, 0x01, 0x77, 0x07, 0x01, 0x00, 0x02, 0x08, 0x00, 0xFF, 0x01, 0x72, 0x62, 0x01, 0x62, 0x00, 0x62, 0x1E, 0x52, 0xFF, 0x64, 0x05, 0x46, 0x0B, 0x01, 0x77, 0x07, 0x01, 0x00, 0x10, 0x07, 0x00, 0xFF, 0x01, 0x72, 0x62, 0x01, 0x62, 0x00, 0x62, 0x1B, 0x52, 0xFE, 0x53, 0x41, 0x28, 0x01, 0x01, 0x01, 0x63, 0x7A, 0x27, 0x00, 0x76, 0x05, 0xA8, 0x9F, 0x1A, 0x03, 0x62, 0x00, 0x62, 0x00, 0x72, 0x63, 0x02, 0x01, 0x71, 0x01, 0x63, 0x7C, 0x9E, 0x00, 0x00, 0x00, 0x1B, 0x1B, 0x1B, 0x1B, 0x1A, 0x02, 0xAE, 0xE0};
const uint8_t packet2[] {0x1B, 0x1B, 0x1B, 0x1B, 0x01, 0x01, 0x01, 0x01, 0x76, 0x05, 0xD1, 0xE6, 0x02, 0x03, 0x62, 0x00, 0x62, 0x00, 0x72, 0x63, 0x01, 0x01, 0x76, 0x01, 0x01, 0x02, 0x31, 0x0B, 0x0A, 0x01, 0x44, 0x5A, 0x47, 0x00, 0x03, 0xA0, 0x86, 0xBC, 0x72, 0x62, 0x01, 0x65, 0x01, 0x00, 0xF7, 0xEA, 0x62, 0x02, 0x63, 0x46, 0x63, 0x00, 0x76, 0x05, 0xD2, 0xE6, 0x02, 0x03, 0x62, 0x00, 0x62, 0x00, 0x72, 0x63, 0x07, 0x01, 0x77, 0x01, 0x0B, 0x0A, 0x01, 0x44, 0x5A, 0x47, 0x00, 0x03, 0xA0, 0x86, 0xBC, 0x07, 0x01, 0x00, 0x62, 0x0A, 0xFF, 0xFF, 0x72, 0x62, 0x01, 0x65, 0x01, 0x00, 0xF7, 0xEA, 0x75, 0x77, 0x07, 0x01, 0x00, 0x60, 0x32, 0x01, 0x01, 0x01, 0x72, 0x62, 0x01, 0x62, 0x00, 0x62, 0x00, 0x52, 0x00, 0x04, 0x44, 0x5A, 0x47, 0x01, 0x77, 0x07, 0x01, 0x00, 0x60, 0x01, 0x00, 0xFF, 0x01, 0x72, 0x62, 0x01, 0x62, 0x00, 0x62, 0x00, 0x52, 0x00, 0x0B, 0x0A, 0x01, 0x44, 0x5A, 0x47, 0x00, 0x03, 0xA0, 0x86, 0xBC, 0x01, 0x77, 0x07, 0x01, 0x00, 0x01, 0x08, 0x00, 0xFF, 0x64, 0x1C, 0x01, 0x04, 0x72, 0x62, 0x01, 0x62, 0x00, 0x62, 0x1E, 0x52, 0xFF, 0x65, 0x01, 0x23, 0x26, 0xA2, 0x01, 0x77, 0x07, 0x01, 0x00, 0x02, 0x08, 0x00, 0xFF, 0x01, 0x72, 0x62, 0x01, 0x62, 0x00, 0x62, 0x1E, 0x52, 0xFF, 0x64, 0x04, 0x1A, 0x24, 0x01, 0x77, 0x07, 0x01, 0x00, 0x10, 0x07, 0x00, 0xFF, 0x01, 0x72, 0x62, 0x01, 0x62, 0x00, 0x62, 0x1B, 0x52, 0xFE, 0x54, 0x00, 0x97, 0xB9, 0x01, 0x01, 0x01, 0x63, 0x91, 0xDB, 0x00, 0x76, 0x05, 0xD3, 0xE6, 0x02, 0x03, 0x62, 0x00, 0x62, 0x00, 0x72, 0x63, 0x02, 0x01, 0x71, 0x01, 0x63, 0xF6, 0x79, 0x00, 0x00, 0x1B, 0x1B, 0x1B, 0x1B, 0x1A, 0x01, 0x68, 0x86};

const uint8_t OBIS_1_8_0[] {0x07, 0x01, 0x00, 0x01, 0x08, 0x00};
const uint8_t OBIS_2_8_0[] {0x07, 0x01, 0x00, 0x02, 0x08, 0x00};
const uint8_t OBIS_16_7_0[] {0x07, 0x01, 0x00, 0x10, 0x07, 0x00};

const char* unit_str[] = {
  "",          // (0) dummy
  "a",         // (1) a time year
  "mo",        // (2) mo time month
  "wk",        // (3) wk time week 7*24*60*60 s
  "d",         // (4) d time day 24*60*60 s
  "h",         // (5) h time hour 60*60 s
  "min",       // (6) min time minute 60 s
  "s",         // (7) s time (t) second s
  "deg",       // (8) ° (phase) angle degree rad*180/π
  "degC",      // (9) °C temperature (T) degree-celsius K-273.15
  "",          // (10) currency (local) currency
  "m",         // (11) m length (l) metre m
  "m/s",       // (12) m/s speed (v) metre per second m/s
  "m3",        // (13) m 3 volume (V) rV , meter constant or pulse value (volume) cubic metre m 3
  "m3",        // (14) m 3 corrected volume cubic metre m 3
  "m3/h",      // (15) m 3/h volume flux cubic metre per hour m 3/(60*60s)
  "m3/h",      // (16) m 3/h corrected volume flux cubic metre per hour m 3/(60*60s)
  "m3/d",      // (17) m 3/d volume flux  m 3/(24*60*60s)
  "m3/d",      // (18) m 3/d corrected volume flux  m 3/(24*60*60s)
  "l",         // (19) l volume litre 10- 3 m 3
  "kg",        // (20) kg mass (m) kilogram
  "N",         // (21) N force (F) newton
  "Nm",        // (22) Nm energy  newton meter J = Nm = Ws
  "Pa",        // (23) Pa pressure (p) pascal N/m2
  "bar",       // (24) bar pressure (p) bar 105 N/m2
  "J",         // (25) J energy  joule J = Nm = Ws
  "J/h",       // (26) J/h thermal power joule per hour J/(60*60s)
  "W",         // (27) W active power (P) watt W = J/s
  "VA",        // (28) VA apparent power (S) volt-ampere
  "var",       // (29) var reactive power (Q) var
  "Wh",        // (30) Wh active energy rW  , active energy meter constant or pulse value watt-hour W*(60*60s)
  "VAh",       // (31) VAh apparent energy rS , apparent energy meter constant or pulse value volt-ampere-hour VA*(60*60s)
  "varh",      // (32) varh reactive energy rB , reactive energy meter constant or pulse value var-hour var*(60*60s)
  "A",         // (33) A current (I) ampere A
  "C",         // (34) C electrical charge (Q) coulomb C = As
  "V",         // (35) V voltage (U) volt V
  "V/m",       // (36) V/m electric field strength (E) volt per metre V/m
  "F",         // (37) F capacitance (C) farad C/V = As/V
  "Ohm",       // (38) Ω resistance (R) ohm Ω = V/A
  "Ohmm 2/m",  // (39) Ωm 2/m resistivity (ρ)  Ωm
  "Wb",        // (40) Wb magnetic flux (Φ) weber Wb = Vs
  "T",         // (41) T magnetic flux density (B) tesla Wb/m2
  "A/m",       // (42) A/m magnetic field strength (H) ampere per metre A/m
  "H",         // (43) H inductance (L) henry H = W b/A
  "Hz",        // (44) Hz frequency (f, ω) hertz 1/s
  "1/(W h)",   // (45) 1/(W h) RW  , active energy meter constant or pulse value
  "1/(varh)",  // (46) 1/(varh) RB , reactive energy meter constant or pulse value
  "1/(VAh)",   // (47) 1/(VAh) RS , apparent energy meter constant or pulse value
  "V2h",       // (48) V2h  volt-squared hour, rU 2h , volt-squared hour meter constant or pulse value volt-squared-hours V2(60*60s)
  "A2h",       // (49) A2h  ampere-squared hour, rI2 h ,  ampere-squared hour meter constant or pulse value ampere-squared-hours A2(60*60s)
  "kg/s",      // (50) kg/s mass flux kilogram per second kg/s
  "S",         // (51) S, mho conductance siemens 1/Ω
  "K",         // (52) K temperature (T) kelvin
  "1/(V2h)",   // (53) 1/(V2h) RU2 h , volt-squared hour meter constant or pulse value
  "1/(A2h)",   // (54) 1/(A2h) RI2h , ampere-squared hour meter constant or pulse value
  "1/m3",      // (55) 1/m3 RV , meter constant or pulse value (volume)
  "%",         // (56)  percentage %
  "Ah",        // (57) Ah ampere-hours Ampere-hour  ...
  "",          // (58) dummy
  "",          // (59) dummy
  "Wh/m3",     // (60) Wh/m3 energy per volume 3,6*103 J/m3
  "J/m3",      // (61) J/m3 calorific value, wobbe
  "Mol",       // (62) Mol % molar fraction of gas composition mole percent (Basic gas composition unit)
  "g/m3",      // (63) g/m3 mass density, quantity of material   (Gas analysis, accompanying elements)
  "Pas",       // (64) Pas dynamic viscosity pascal second (Characteristic of gas stream)
  "J/kg",      // (65) J/kg specific energy NOTE  The amount of energy per unit of mass of a substance Joule / kilogram m 2 . kg . s -2 / kg  = m2 . s –2  ....
  "",          // (66) dummy
  "",          // (67) dummy
  "",          // (68) dummy
  "",          // (69) dummy
  "dBm",       // (70) dBm signal strength, dB milliwatt (e.g. of GSM radio systems)
  "dbmV",      // (71) dbμV signal strength, dB microvolt
  "dB",        // (72) dB logarithmic unit that expresses the ratio between two values of a physical quantity   ...
};

#define SML_TYPE_MASK 0x70
#define SML_LENGTH_MASK 0x0F

#define SML_ARRAY 0x00
#define SML_BOOL  0x40
#define SML_INT   0x50
#define SML_UINT  0x60
#define SML_LIST  0x70

const char* SML_ARRAY_STR = "SML_ARRAY";
const char* SML_BOOL_STR  = "SML_BOOL";
const char* SML_INT_STR   = "SML_INT";
const char* SML_UINT_STR  = "SML_UINT";
const char* SML_LIST_STR  = "SML_LIST";
const char* SML_UNKNOWN   = "UNKNWON-TYPE";

#define MAX_DEPTH 15
#define MIN_LEVEL 0

using ParseFunction = std::function<void(const uint8_t* data, uint8_t type, uint8_t length)>;

struct ListInfo {
  uint8_t maxItems = 0;
  uint8_t currentItem = 0;
};

const char* getFieldTypeStr(uint8_t field_type) {
  switch (field_type) {
    case SML_ARRAY : return SML_ARRAY_STR;
    case SML_BOOL  : return SML_BOOL_STR;
    case SML_INT   : return SML_INT_STR;
    case SML_UINT  : return SML_UINT_STR;
    case SML_LIST  : return SML_LIST_STR;
  }
  return SML_UNKNOWN;
}

void printArray(const uint8_t* arr, uint8_t len) {
  const uint8_t* ptr = arr;
  if (len == 0) {
    Serial.println();
    return;
  }

  Serial.print(" [");
  for (size_t i = 0; i < len; i++) Serial.printf("%s%02X", i ? ", 0x" : "0x", *(ptr++));
  Serial.println("]");
}

void parseList(const uint8_t* data, size_t len) {
  const uint8_t* ptr = data;
  ListInfo li[MAX_DEPTH];
  int current_level = MIN_LEVEL;

  while (*(ptr) >> 4 != 0x07) {
    ptr++;
  }

  if (!ptr || *(ptr) >> 4 != 0x07) {
    Serial.println("Invalid list");
    return;
  }

  const uint8_t listLength = *(ptr++) & SML_LENGTH_MASK;
  Serial.printf("SML_LIST (%d)\r\n", listLength);
  current_level++;
  li[current_level].maxItems = listLength;
  li[current_level].currentItem = 0;

  // while (li[MIN_LEVEL].currentItem < li[MIN_LEVEL].maxItems) {
  while (ptr < data + len) {
    uint8_t field_type   = *(ptr) & SML_TYPE_MASK;
    uint8_t field_length = *(ptr) & SML_LENGTH_MASK;

    if (field_type == SML_LIST) {
      li[current_level + 1].maxItems = field_length;
    }

    li[current_level].currentItem++;

    Serial.printf("%02d | ", current_level); for (int i = 0; i < current_level; i++) Serial.print("  ");
    Serial.printf("[%d/%d] %02X = %s (%d)",
                  li[current_level].currentItem,
                  li[current_level].maxItems,
                  *(ptr),
                  getFieldTypeStr(field_type),
                  field_type == SML_LIST ? field_length : field_length - 1
                 );

    if (current_level == 0 && li[current_level].currentItem == 6) {
      const uint8_t* ptr2 = ptr;
      for (size_t i = 0; i < field_length; i++) {
        Serial.printf(" %02X", *(ptr2++));
      }
    }

    if (field_type == SML_LIST) {
      current_level++;
      ptr++;
      Serial.println();
    } else {
      // if (pf && current_level == sLevel && li[current_level].currentItem == sField) pf(ptr, field_type, field_length);
      if (field_length >= 1) printArray(ptr + 1, field_length - 1);
      else Serial.println();
      ptr += field_length ? field_length : 1;
    }

    // while (current_level > MIN_LEVEL && li[current_level].currentItem >= li[current_level].maxItems) {
    while (li[current_level].currentItem >= li[current_level].maxItems && current_level >= MIN_LEVEL) {
      li[current_level].currentItem = 0;
      li[current_level].maxItems = 0;
      current_level--;
      // if (current_level < MIN_LEVEL) return;
    }
  }
  Serial.println();
}

void setup() {
  Serial.begin(115200);
  Serial.println("Begin parsing...");

  parseList(packet1, sizeof(packet1));
  // parseList(findOBIS(packet1, sizeof(packet1), OBIS_2_8_0), parseOBIS_16_7_0, 0, 6);
  // parseList(findOBIS(packet1, sizeof(packet1), OBIS_16_7_0), parseOBIS_16_7_0, 0, 6);
  // parseList(findOBIS(packet2, sizeof(packet2), OBIS_1_8_0) + 1);
  // parseList(nullptr);
  // parseList(findOBIS(packet2, sizeof(packet2), OBIS_2_8_0));
  // parseList(findOBIS(packet2, sizeof(packet2), OBIS_16_7_0));
}

void loop() {
  delay(10); // this speeds up the simulation
}