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

#include "IEC6205621.h"
#include "Uptime.h"

IEC6205621::IEC6205621(const char* p, Timezone* tz, MeterConfig* meterConfig) {
	if(strlen(p) < 16)
		return;

	this->packageTimestamp = time(nullptr);

	String payload(p+1);

	lastUpdateMillis = millis64();
	listId = payload.substring(payload.startsWith("/") ? 1 : 0, payload.indexOf("\n"));

	if(listId.startsWith(F("ADN"))) {
		meterType = AmsTypeAidon;
		listId = listId.substring(0,4);
	} else if(listId.startsWith(F("KFM"))) {
		meterType = AmsTypeKaifa;
		listId = listId.substring(0,4);
	} else if(listId.startsWith(F("KMP"))) {
		meterType = AmsTypeKamstrup;
		listId = listId.substring(0,4);
	} else if(listId.startsWith(F("KAM"))) {
		meterType = AmsTypeKamstrup;
		listId = listId.substring(0,4);
	} else if(listId.startsWith(F("ISk"))) {
		meterType = AmsTypeIskra;
		listId = listId.substring(0,5);
	} else if(listId.startsWith(F("XMX"))) {
		meterType = AmsTypeLandisGyr;
		listId = listId.substring(0,6);
	} else if(listId.startsWith(F("Ene")) || listId.startsWith(F("EST"))) {
		meterType = AmsTypeSagemcom;
		listId = listId.substring(0,4);
	} else if(listId.startsWith(F("LGF"))) {
		meterType = AmsTypeLandisGyr;
		listId = listId.substring(0,4);
	} else {
		meterType = AmsTypeUnknown;
		listId = listId.substring(0,4);
	}
	
	meterId = extract(payload, F("96.1.0"));
	if(meterId.isEmpty()) {
		meterId = extract(payload, F("0.0.5"));
	}

	meterModel = extract(payload, F("96.1.1"));
	if(meterModel.isEmpty()) {
		meterModel = extract(payload, F("96.1.7"));
		if(meterModel.isEmpty()) {
			meterModel = payload.substring(payload.indexOf(listId) + listId.length(), payload.indexOf(F("\n")));
			meterModel.trim();
		}
	}

	tmElements_t tm { 0, 0, 0, 0, 0, 0, 0 };
	String timestamp = extract(payload, F("1.0.0"));
	if(timestamp.length() == 13) { // yyMMddHHmmssX
		char x = timestamp.charAt(12);
		if(x == 'S' || x == 'W') {
			tm.Year = (timestamp.substring(0,2).toInt() + 2000) - 1970;
			tm.Month = timestamp.substring(2,4).toInt();
			tm.Day = timestamp.substring(4,6).toInt();
			tm.Hour = timestamp.substring(6,8).toInt();
			tm.Minute = timestamp.substring(8,10).toInt();
			tm.Second = timestamp.substring(10,12).toInt();
		}
	} else if(timestamp.length() == 17) { // yyyyMMdd HH:mm:ss
		char x = timestamp.charAt(11);
		char y = timestamp.charAt(14);
		if(x == ':' && y == ':') {
			tm.Year = (timestamp.substring(0,4).toInt()) - 1970;
			tm.Month = timestamp.substring(4,6).toInt();
			tm.Day = timestamp.substring(6,8).toInt();
			tm.Hour = timestamp.substring(9,11).toInt();
			tm.Minute = timestamp.substring(12,14).toInt();
			tm.Second = timestamp.substring(15,17).toInt();
		}
	} else if(timestamp.length() == 19) { // yyyy-MM-dd HH:mm:ss
		char x = timestamp.charAt(4);
		char y = timestamp.charAt(13);
		if(x == '-' && y == ':') {
			tm.Year = (timestamp.substring(0,4).toInt()) - 1970;
			tm.Month = timestamp.substring(5,7).toInt();
			tm.Day = timestamp.substring(8,10).toInt();
			tm.Hour = timestamp.substring(11,13).toInt();
			tm.Minute = timestamp.substring(14,16).toInt();
			tm.Second = timestamp.substring(17,19).toInt();
		}
	} else {
		meterTimestamp = 0;
	}
	if(tm.Year > 0) {
		meterTimestamp = makeTime(tm);
		if(tz != NULL) meterTimestamp = tz->toUTC(meterTimestamp);
	}

	activeImportPower = (uint16_t) (extractDouble(payload, F("1.7.0")));
	activeExportPower = (uint16_t) (extractDouble(payload, F("2.7.0")));
	reactiveImportPower = (uint16_t) (extractDouble(payload, F("3.7.0")));
	reactiveExportPower = (uint16_t) (extractDouble(payload, F("4.7.0")));

	if(activeImportPower > 0)
		listType = 1;
	
	l1voltage = extractFloat(payload, F("32.7.0"));
	l2voltage = extractFloat(payload, F("52.7.0"));
	l3voltage = extractFloat(payload, F("72.7.0"));

	l1current = extractFloat(payload, F("31.7.0"));
	l2current = extractFloat(payload, F("51.7.0"));
	l3current = extractFloat(payload, F("71.7.0"));

	l1activeImportPower = extractFloat(payload, F("21.7.0"));
	l2activeImportPower = extractFloat(payload, F("41.7.0"));
	l3activeImportPower = extractFloat(payload, F("61.7.0"));
	
	l1activeExportPower = extractFloat(payload, F("22.7.0"));
	l2activeExportPower = extractFloat(payload, F("42.7.0"));
	l3activeExportPower = extractFloat(payload, F("62.7.0"));
	
	if(l1voltage > 0 || l2voltage > 0 || l3voltage > 0)
		listType = 2;

	double val = 0.0;
	
	val = extractDouble(payload, F("1.8.0"));
	if(val == 0) {
		for(int i = 1; i < 9; i++) {
			val += extractDouble(payload, "1.8." + String(i,10));
		}
	}
	if(val > 0) activeImportCounter = val / 1000;

	val = extractDouble(payload, F("2.8.0"));
	if(val == 0) {
		for(int i = 1; i < 9; i++) {
			val += extractDouble(payload, "2.8." + String(i,10));
		}
	}
	if(val > 0) activeExportCounter = val / 1000;

	val = extractDouble(payload, F("3.8.0"));
	if(val == 0) {
		for(int i = 1; i < 9; i++) {
			val += extractDouble(payload, "3.8." + String(i,10));
		}
	}
	if(val > 0) reactiveImportCounter = val / 1000;

	val = extractDouble(payload, F("4.8.0"));
	if(val == 0) {
		for(int i = 1; i < 9; i++) {
			val += extractDouble(payload, "4.8." + String(i,10));
		}
	}
	if(val > 0) reactiveExportCounter = val / 1000;

	if(activeImportCounter > 0 || activeExportCounter > 0 || reactiveImportCounter > 0 || reactiveExportCounter > 0)
		listType = 3;

	if (l1activeImportPower > 0 || l2activeImportPower > 0 || l3activeImportPower > 0 || l1activeExportPower > 0 || l2activeExportPower > 0 || l3activeExportPower > 0)
		listType = 4;

    if(meterConfig->wattageMultiplier > 0) {
        activeImportPower = activeImportPower > 0 ? activeImportPower * (meterConfig->wattageMultiplier / 1000.0) : 0;
        activeExportPower = activeExportPower > 0 ? activeExportPower * (meterConfig->wattageMultiplier / 1000.0) : 0;
        reactiveImportPower = reactiveImportPower > 0 ? reactiveImportPower * (meterConfig->wattageMultiplier / 1000.0) : 0;
        reactiveExportPower = reactiveExportPower > 0 ? reactiveExportPower * (meterConfig->wattageMultiplier / 1000.0) : 0;
    }
    if(meterConfig->voltageMultiplier > 0) {
        l1voltage = l1voltage > 0 ? l1voltage * (meterConfig->voltageMultiplier / 1000.0) : 0;
        l2voltage = l2voltage > 0 ? l2voltage * (meterConfig->voltageMultiplier / 1000.0) : 0;
        l3voltage = l3voltage > 0 ? l3voltage * (meterConfig->voltageMultiplier / 1000.0) : 0;
    }
    if(meterConfig->amperageMultiplier > 0) {
        l1current = l1current > 0 ? l1current * (meterConfig->amperageMultiplier / 1000.0) : 0;
        l2current = l2current > 0 ? l2current * (meterConfig->amperageMultiplier / 1000.0) : 0;
        l3current = l3current > 0 ? l3current * (meterConfig->amperageMultiplier / 1000.0) : 0;
    }
    if(meterConfig->accumulatedMultiplier > 0) {
        activeImportCounter = activeImportCounter > 0 ? activeImportCounter * (meterConfig->accumulatedMultiplier / 1000.0) : 0;
        activeExportCounter = activeExportCounter > 0 ? activeExportCounter * (meterConfig->accumulatedMultiplier / 1000.0) : 0;
        reactiveImportCounter = reactiveImportCounter > 0 ? reactiveImportCounter * (meterConfig->accumulatedMultiplier / 1000.0) : 0;
        reactiveExportCounter = reactiveExportCounter > 0 ? reactiveExportCounter * (meterConfig->accumulatedMultiplier / 1000.0) : 0;
    }

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

String IEC6205621::extract(String payload, String obis) {
	int a = payload.indexOf(String(":" + obis + "("));
	if(a > 0) {
		int b = payload.indexOf(F(")"), a);
		if(b > a) {
			return payload.substring(a+obis.length()+2, b);
		}
	}
	return "";
}

double IEC6205621::extractDouble(String payload, String obis) {
	String str = extract(payload, obis);
	if(str.isEmpty()) {
		return 0.0;
	}

	int a = str.indexOf(F("*"));
	String val = str.substring(0,a);
	String unit = str.substring(a+1);

	return unit.startsWith(F("k")) ? val.toDouble() * 1000 : val.toDouble();
}

float IEC6205621::extractFloat(String payload, String obis) {
	String str = extract(payload, obis);
	if(str.isEmpty()) {
		return 0.0;
	}

	int a = str.indexOf(F("*"));
	String val = str.substring(0,a);
	String unit = str.substring(a+1);

	return unit.startsWith(F("k")) ? val.toFloat() * 1000 : val.toFloat();
}
