#include <math.h>
#include <ArduinoJson.h>

const uint8_t packet1[] {0x1B, 0x1B, 0x1B, 0x1B, 0x01, 0x01, 0x01, 0x01, 0x76, 0x05, 0x28, 0x91, 0x2B, 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, 0x0E, 0x86, 0x07, 0x62, 0x02, 0x63, 0x6A, 0x40, 0x00, 0x76, 0x05, 0x29, 0x91, 0x2B, 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, 0x0E, 0x86, 0x07, 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, 0x49, 0x04, 0x72, 0x62, 0x01, 0x62, 0x00, 0x62, 0x1E, 0x52, 0xFF, 0x65, 0x01, 0x2C, 0xF5, 0xF8, 0x01, 0x77, 0x07, 0x01, 0x00, 0x02, 0x08, 0x00, 0xFF, 0x01, 0x72, 0x62, 0x01, 0x62, 0x00, 0x62, 0x1E, 0x52, 0xFF, 0x64, 0x05, 0xC5, 0x51, 0x01, 0x77, 0x07, 0x01, 0x00, 0x10, 0x07, 0x00, 0xFF, 0x01, 0x72, 0x62, 0x01, 0x62, 0x00, 0x62, 0x1B, 0x52, 0xFE, 0x53, 0x8F, 0x7F, 0x01, 0x01, 0x01, 0x63, 0xB7, 0x43, 0x00, 0x76, 0x05, 0x2A, 0x91, 0x2B, 0x03, 0x62, 0x00, 0x62, 0x00, 0x72, 0x63, 0x02, 0x01, 0x71, 0x01, 0x63, 0xCD, 0x6E, 0x00, 0x00, 0x00, 0x1B, 0x1B, 0x1B, 0x1B, 0x1A, 0x02, 0xEA, 0xB6};

#define SML_ARRAY 0x00
#define SML_BOOL  0x04
#define SML_INT   0x05
#define SML_UINT  0x06
#define SML_LIST  0x07

#define SML_OpenResponse    0x0101
#define SML_CloseResponse   0x0201
#define SML_GetListResponse 0x0701

#define OBIS_1_8_0  0x010800
#define OBIS_2_8_0  0x020800
#define OBIS_16_7_0 0x100700

#define SML_TYPE_MASK 0x70

#define MAX_DEPTH 15

#define FieldType(x) *x >> 4
#define FieldTypeStr(x) SML_TYPE_STR[x]
#define FieldLength(x) *x & 0x0F

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   ...
};

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

uint32_t obis_short(const uint8_t* data) {
  data += 2;
  uint32_t result = 0;
  for (size_t i = 0; i < 3; i++) {
    result = (result << 8) + *(data++);
  }
  return result;
}

uint64_t parseUint(const uint8_t* data, size_t len) {
  uint64_t result = 0;
  for (size_t i = 0; i < len; i++) result = (result << 8) + *(data++);
  return result;
}

int64_t parseInt(const uint8_t* data, size_t len) {
  int64_t result = *(data) & 0x80 ? -1 : 0;
  for (size_t i = 0; i < len; i++) result = (result << 8) + *(data++);
  return result;
}

String parseObis(const uint8_t* data) {
  char buf[255];
  sprintf(buf, "%d.%d.%d",
          data[2],
          data[3],
          data[4]
         );
  return String(buf);
}

void parseList(const uint8_t* data, size_t len) {
  ListInfo li[MAX_DEPTH];
  uint8_t current_level = 0;
  const uint8_t* ptr = data;
  uint64_t smlMessage = 0;
  uint64_t unit = 0;
  int64_t scaler = 0;
  double value = 0;

  String obis_code = "";

  StaticJsonDocument<1024> doc;

  while (ptr < data + len) {
    uint8_t field_type   = FieldType(ptr);
    uint8_t field_length = FieldLength(ptr);
    ptr++;

    if (current_level == 0 && field_type != SML_LIST) {
      continue;
    }

    li[current_level].currentItem++;

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

      if (current_level == 2 && li[current_level].currentItem == 1) {
        smlMessage = parseUint(ptr, field_length - 1);
      }

      if (smlMessage = SML_GetListResponse && current_level == 5) {
        uint8_t& currentItem = li[current_level].currentItem;

        switch (currentItem) {
          case 1:
            obis_code = parseObis(ptr);
            break;
          case 4:
            unit = parseUint(ptr, field_length - 1);
            break;
          case 5:
            scaler = parseInt(ptr, field_length - 1);
            break;
          case 6:
            if (field_type == SML_INT || field_type == SML_UINT) {

              if (field_type == SML_UINT) value = parseUint(ptr, field_length - 1 ) * pow(10, scaler);
              if (field_type == SML_INT) value = parseInt(ptr, field_length - 1 ) * pow(10, scaler);

              doc[obis_code]["v"] = value;
              doc[obis_code]["u"] = unit_str[unit];

              unit = 0;
              scaler = 0;
              obis_code = "";
            }
            break;

          default: break;
        }
      }

      ptr += field_length ? field_length - 1 : 0;
    }

    while (current_level > 0 && li[current_level].currentItem == li[current_level].maxItems) {
      li[current_level].maxItems = 0;
      li[current_level].currentItem = 0;
      current_level--;
    }
  }

  String result = "";
  serializeJsonPretty(doc, result);
  Serial.println(result);
}

void setup() {
  Serial.begin(115200);

  parseList(packet1, sizeof(packet1));
}

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