/**
 * @copyright Utilitech AS 2023-2026
 * License: Fair Source
 * 
 */

#include "LNG.h"
#include "lwip/def.h"
#include "ntohll.h"
#include "Uptime.h"

LNG::LNG(AmsData& meterState, const char* payload, uint8_t useMeterType, MeterConfig* meterConfig, DataParserContext &ctx) {
    LngHeader* h = (LngHeader*) payload;
    if(h->tag == CosemTypeStructure && h->arrayTag == CosemTypeArray) {
        apply(meterState);
        meterType = AmsTypeLandisGyr;
        this->packageTimestamp = ctx.timestamp;

        uint8_t* ptr = (uint8_t*) &h[1];
        uint8_t* data = ptr + (18*h->arrayLength); // Skip descriptors

        uint64_t o170 = 0, o270 = 0;
        uint64_t o180 = 0, o280 = 0;
        uint64_t o181 = 0, o182 = 0;
        uint64_t o281 = 0, o282 = 0;
        uint64_t o380 = 0, o480 = 0;
        uint64_t o580 = 0, o680 = 0;
        uint64_t o780 = 0, o880 = 0;
        LngObisDescriptor* descriptor = (LngObisDescriptor*) ptr;
        for(uint8_t x = 0;  x < h->arrayLength-1; x++) {
            ptr = (uint8_t*) &descriptor[1];
            descriptor = (LngObisDescriptor*) ptr;

            CosemData* item = (CosemData*) data;
            if(descriptor->obis[3] == 7) {
                if(descriptor->obis[4] == 0) {
                    if(descriptor->obis[2] > 1) {
                        listType = listType >= 2 ? listType : 2;
                    } else {
                        listType = listType >= 1 ? listType : 1;
                    }
                    switch(descriptor->obis[2]) {
                        case 1:
                            o170 = getNumber(item);
                            if(meterConfig->wattageMultiplier > 0) {
                                o170 = o170 > 0 ? o170 * (meterConfig->wattageMultiplier / 1000.0) : 0;
                            }
                            break;
                        case 2:
                            o270 = getNumber(item);
                            if(meterConfig->wattageMultiplier > 0) {
                                o270 = o270 > 0 ? o270 * (meterConfig->wattageMultiplier / 1000.0) : 0;
                            }
                            break;
                        case 3:
                            reactiveImportPower = getNumber(item);
                            if(meterConfig->wattageMultiplier > 0) {
                                reactiveImportPower = reactiveImportPower > 0 ? reactiveImportPower * (meterConfig->wattageMultiplier / 1000.0) : 0;
                            }
                            break;
                        case 4:
                            reactiveExportPower = getNumber(item);
                            if(meterConfig->wattageMultiplier > 0) {
                                reactiveExportPower = reactiveExportPower > 0 ? reactiveExportPower * (meterConfig->wattageMultiplier / 1000.0) : 0;
                            }
                            break;
                        case 21:
                            l1activeImportPower = getNumber(item);
                            listType = listType >= 4 ? listType : 4;
                            break;
                        case 41:
                            l2activeImportPower = getNumber(item);
                            listType = listType >= 4 ? listType : 4;
                            break;
                        case 61:
                            l3activeImportPower = getNumber(item);
                            listType = listType >= 4 ? listType : 4;
                            break;
                        case 31:
                            l1current = getNumber(item) / 100.0;
                            if(meterConfig->amperageMultiplier > 0) {
                                l1current = l1current > 0 ? l1current * (meterConfig->amperageMultiplier / 1000.0) : 0;
                            }
                            break;
                        case 51:
                            l2current = getNumber(item) / 100.0;
                            if(meterConfig->amperageMultiplier > 0) {
                                l2current = l2current > 0 ? l2current * (meterConfig->amperageMultiplier / 1000.0) : 0;
                            }
                            break;
                        case 71:
                            l3current = getNumber(item) / 100.0;
                            if(meterConfig->amperageMultiplier > 0) {
                                l3current = l3current > 0 ? l3current * (meterConfig->amperageMultiplier / 1000.0) : 0;
                            }
                            break;
                        case 32:
                            l1voltage = getNumber(item) / 10.0;
                            if(meterConfig->voltageMultiplier > 0) {
                                l1voltage = l1voltage > 0 ? l1voltage * (meterConfig->voltageMultiplier / 1000.0) : 0;
                            }
                            break;
                        case 52:
                            l2voltage = getNumber(item) / 10.0;
                            if(meterConfig->voltageMultiplier > 0) {
                                l2voltage = l2voltage > 0 ? l2voltage * (meterConfig->voltageMultiplier / 1000.0) : 0;
                            }
                            break;
                        case 72:
                            l3voltage = getNumber(item) / 10.0;
                            if(meterConfig->voltageMultiplier > 0) {
                                l3voltage = l3voltage > 0 ? l3voltage * (meterConfig->voltageMultiplier / 1000.0) : 0;
                            }
                            break;
                    }
                }
            } else if(descriptor->obis[3] == 8) {
                listType = listType >= 3 ? listType : 3;
                if(descriptor->obis[4] == 0) {
                    switch(descriptor->obis[2]) {
                        case 1:
                            o180 = getNumber(item);
                            activeImportCounter = o180 / 1000.0;
                            if(meterConfig->accumulatedMultiplier > 0) {
                                activeImportCounter = activeImportCounter > 0 ? activeImportCounter * (meterConfig->accumulatedMultiplier / 1000.0) : 0;
                            }
                            break;
                        case 2:
                            o280 = getNumber(item);
                            activeExportCounter = o280 / 1000.0;
                            if(meterConfig->accumulatedMultiplier > 0) {
                                activeExportCounter = activeExportCounter > 0 ? activeExportCounter * (meterConfig->accumulatedMultiplier / 1000.0) : 0;
                            }
                            break;
                        case 3:
                            o380 = getNumber(item);
                            reactiveImportCounter = o380 / 1000.0;
                            if(meterConfig->accumulatedMultiplier > 0) {
                                reactiveImportCounter = reactiveImportCounter > 0 ? reactiveImportCounter * (meterConfig->accumulatedMultiplier / 1000.0) : 0;
                            }
                            break;
                        case 4:
                            o480 = getNumber(item);
                            reactiveExportCounter = o480 / 1000.0;
                            if(meterConfig->accumulatedMultiplier > 0) {
                                reactiveExportCounter = reactiveExportCounter > 0 ? reactiveExportCounter * (meterConfig->accumulatedMultiplier / 1000.0) : 0;
                            }
                            break;
                        case 5:
                            o580 = getNumber(item);
                            if(meterConfig->accumulatedMultiplier > 0) {
                                o580 = o580 > 0 ? o580 * (meterConfig->accumulatedMultiplier / 1000.0) : 0;
                            }
                            break;
                        case 6:
                            o680 = getNumber(item);
                            if(meterConfig->accumulatedMultiplier > 0) {
                                o680 = o680 > 0 ? o680 * (meterConfig->accumulatedMultiplier / 1000.0) : 0;
                            }
                            break;
                        case 7:
                            o780 = getNumber(item);
                            if(meterConfig->accumulatedMultiplier > 0) {
                                o780 = o780 > 0 ? o780 * (meterConfig->accumulatedMultiplier / 1000.0) : 0;
                            }
                            break;
                        case 8:
                            o880 = getNumber(item);
                            if(meterConfig->accumulatedMultiplier > 0) {
                                o880 = o880 > 0 ? o880 * (meterConfig->accumulatedMultiplier / 1000.0) : 0;
                            }
                            break;
                    }
                } else if(descriptor->obis[4] == 1) {
                    listType = listType >= 3 ? listType : 3;
                    switch(descriptor->obis[2]) {
                        case 1:
                            o181 = getNumber(item);
                            if(meterConfig->accumulatedMultiplier > 0) {
                                o181 = o181 > 0 ? o181 * (meterConfig->accumulatedMultiplier / 1000.0) : 0;
                            }
                            break;
                        case 2:
                            o281 = getNumber(item);
                            if(meterConfig->accumulatedMultiplier > 0) {
                                o281 = o281 > 0 ? o281 * (meterConfig->accumulatedMultiplier / 1000.0) : 0;
                            }
                            break;
                    }
                } else if(descriptor->obis[4] == 2) {
                    listType = listType >= 3 ? listType : 3;
                    switch(descriptor->obis[2]) {
                        case 1:
                            o182 = getNumber(item);
                            if(meterConfig->accumulatedMultiplier > 0) {
                                o182 = o182 > 0 ? o182 * (meterConfig->accumulatedMultiplier / 1000.0) : 0;
                            }
                            break;
                        case 2:
                            o282 = getNumber(item);
                            if(meterConfig->accumulatedMultiplier > 0) {
                                o282 = o282 > 0 ? o282 * (meterConfig->accumulatedMultiplier / 1000.0) : 0;
                            }
                            break;
                    }
                }
            } else if(descriptor->obis[2] == 96) {
                if(descriptor->obis[3] == 1) {
                    if(descriptor->obis[4] == 0) {
                        char str[item->oct.length+1];
                        memcpy(str, item->oct.data, item->oct.length);
                        str[item->oct.length] = '\0';
                        meterId = String(str);
                        listType = listType >= 2 ? listType : 2;
                    } else if(descriptor->obis[4] == 1) {
                        char str[item->oct.length+1];
                        memcpy(str, item->oct.data, item->oct.length);
                        str[item->oct.length] = '\0';
                        meterModel = String(str);
                        listType = listType >= 2 ? listType : 2;
                    }
                }
            }

            if(o170 > 0 || o270 > 0) {
                int32_t sum = o170-o270;
                if(sum > 0) {
                    activeImportPower = sum;
                    activeExportPower = 0;
                } else {
                    activeImportPower = 0;
                    activeExportPower = sum * -1;
                    listType = listType >= 2 ? listType : 2;
                }
            }

            if(o181 > 0 || o182 > 0) {
                activeImportCounter = (o181 + o182) / 1000.0;
            }
            if(o281 > 0 || o282 > 0) {
                activeExportCounter = (o281 + o282) / 1000.0;
            }

            if(o580 > 0 || o680 > 0) {
                reactiveImportCounter = (o580 + o680) / 1000.0;
            }
            if(o780 > 0 || o880 > 0) {
                reactiveExportCounter = (o780 + o880) / 1000.0;
            }

            if((*data) == 0x09) {
                data += (*(data+1))+2;
            } else if((*data) == 0x15) {
                data += 9;
            } else if((*data) == 0x06) {
                data += 5;
            } else if((*data) == 0x12) {
                data += 3;
            }

            lastUpdateMillis = millis64();
        }
        lastUpdateMillis = millis64();

        threePhase = l1voltage > 0 && l2voltage > 0 && l3voltage > 0;
        if(!threePhase)
            twoPhase = (l1voltage > 0 && l2voltage > 0) || (l2voltage > 0 && l3voltage > 0) || (l3voltage > 0  && l1voltage > 0);
    }
}

uint64_t LNG::getNumber(CosemData* item) {
    if(item != NULL) {
        uint64_t ret = 0.0;
        switch(item->base.type) {
            case CosemTypeLongSigned: {
                int16_t i16 = ntohs(item->ls.data);
                return i16;
            }
            case CosemTypeLongUnsigned: {
                uint16_t u16 = ntohs(item->lu.data);
                return u16;
            }
            case CosemTypeDLongSigned: {
                int32_t i32 = ntohl(item->dlu.data);
                return i32;
            }
            case CosemTypeDLongUnsigned: {
                uint32_t u32 = ntohl(item->dlu.data);
                return u32;
            }
            case CosemTypeLong64Signed: {
                int64_t i64 = ntohll(item->l64s.data);
                return i64;
            }
            case CosemTypeLong64Unsigned: {
                uint64_t u64 = ntohll(item->l64u.data);
                return u64;
            }
        }
        return ret;
    }
    return 0.0;
}
