// Decode uplink function.  INNOTAS 19.05.2026 
  //
  // Input is an object with the following fields:
  // - bytes = Byte array containing the uplink payload, e.g. [255, 230, 255, 0]
// Decode uplink function.
//
// Input is an object with the following fields:
// - bytes = Byte array containing the uplink payload, e.g. [255, 230, 255, 0]
// - fPort = Uplink fPort.
// - variables = Object containing the configured device variables.
//
// Output must be an object with the following fields:
// - data = Object representing the decoded payload.
function decodeUplink(input) {
    switch (input.fPort) {
        case 1:
            return {
                data: {
                  ZS: 	parse4BytesMsb(input.bytes, 0),
                  ZSYST: parse4BytesMsb(input.bytes, 4),
				  ZSMST: parse4BytesMsb(input.bytes, 8),
				  Month_Last: input.bytes[12]>>4,
				  Year_Month: input.bytes[12] & 0x0F,
				  STATUS: parseStatusBits([input.bytes[13], input.bytes[14]]),
                }
            };

		case 3:
            return {
              data: {
                		ZS: 				parse4BytesMsb(input.bytes, 0),
                        ZSYST:              parse4BytesMsb(input.bytes, 4),
				        ZSMST:              parse4BytesMsb(input.bytes, 8),
				        Month_Last:         input.bytes[12]>>4,
				        Year_Month:         input.bytes[12] & 0x0F,                		
						Max_DF_L_pro_h:		parse2BytesMsb(input.bytes, 13),
                		Still_proz_pro_Tag:	(input.bytes[15]/2),
                		Max_Start_Tag:		parse2BytesMsb(input.bytes, 16),
						//Min_DF_L_pro_h:		parse2BytesMsb(input.bytes, 18),
                		STATUS: 			parseStatusBits([input.bytes[18], input.bytes[19]])
					}
            };

	case 4:
            return {
                data: {
                  		ZS: 			parse4BytesMsb(input.bytes, 0),
                  		ZSYST: 			parse4BytesMsb(input.bytes, 4),
				  		ZSMST: 			parse4BytesMsb(input.bytes, 8),
				  		Month_Last: 	input.bytes[12]>>4,
				  		Year_Month: 	input.bytes[12] & 0x0F,
						Time:			parse4ByteDTime(input.bytes, 13),
                  		ZS_h1: 			parse2BytesMsb(input.bytes, 17),
                  		ZS_h2: 			parse2BytesMsb(input.bytes, 19),
                  		ZS_h3: 			parse2BytesMsb(input.bytes, 21),
                  		ZS_h4: 			parse2BytesMsb(input.bytes, 23),
				  		ZS_h5: 			parse2BytesMsb(input.bytes, 25),                  
                  		ZS_h6: 			parse2BytesMsb(input.bytes, 27),
                  		ZS_h7: 			parse2BytesMsb(input.bytes, 29),
                  		ZS_h8: 			parse2BytesMsb(input.bytes, 31),
                  		ZS_h9: 			parse2BytesMsb(input.bytes, 33),
				  		ZS_h10: 		parse2BytesMsb(input.bytes, 35),
				  		ZS_h11: 		parse2BytesMsb(input.bytes, 37),
				  		ZS_h12: 		parse2BytesMsb(input.bytes, 39),
                  		STATUS: 		parseStatusBits([input.bytes[41], input.bytes[42]])
                	}
            };
        case 5:
        return {
				data: {
                  		Month: 		input.bytes[0],
				  		YEAR:		input.bytes[1]+2000,
                  		ZSM1: 		parse4BytesMSBasArray(input.bytes,2,18)

					}
        };
        case 6:
        return {
				data: {
                  		Month: 		input.bytes[0],
				  		YEAR:		input.bytes[1]+2000,
                  		ZSM1: 		parse4BytesMSBasArray(input.bytes,2,18)

					}
        };
        case 9:
            return {
                data: {
                    	SF7: 			parse4BytesLsb(input.bytes, 0),
                    	SF8: 			parse4BytesLsb(input.bytes, 4),
                    	SF9: 			parse4BytesLsb(input.bytes, 8),
                    	SF10: 			parse4BytesLsb(input.bytes, 12),
                    	SF11: 			parse4BytesLsb(input.bytes, 16),
                  		SF12: 			parse4BytesLsb(input.bytes, 20),
                  		JOINCOUNTER: 	parse2BytesMsb(input.bytes[24]),
					 	VDD_mV :		((input.bytes[26]<<8)+input.bytes[27]),
						STATUS: 		parseStatusBits([input.bytes[28], input.bytes[29]])
                	}
            };

        case 11:
        return {
				data: {
                  		ZS: 		parse4BytesMsb(input.bytes, 0),
				  		SETUP:		parseInstallBits(input.bytes,4),
              	  		STATUS: 	parseStatusBits([input.bytes[24], input.bytes[25]])
					}
        }

    }
    return {
        data: {
        }
    }
}
function parse4ByteDTime(bytes,start_index) {
  	var index = start_index;
	var msblsb=0x00000000;
  	msblsb= bytes[index++] << 24;
  	msblsb|= bytes[index++] << 16;
	msblsb|= bytes[index++] << 8;
  	msblsb|= bytes[index++] ; 
  return {
    YEAR: (msblsb >> (32-6) & (32+16+8+4+2+1)),
    MON:  ((msblsb >> (32-6-4)) & (8+4+2+1)),
    DAY:  (msblsb >> (32-6-4-5) & (16+8+4+2+1)),
	HOUR: (msblsb >> (32-6-4-5-5) & (16+8+4+2+1)),
	MIN:  (msblsb >> (32-6-4-5-5-6) & (32+16+8+4+2+1)),
	SEC:  (msblsb & (1+2+4+8+16+32))
		}
}
function parseStatusBits(bytes) {
    var msblsb = (bytes[0]<<8)
    msblsb|= bytes[1]
    var intervall = (msblsb & 7)
    return {
      	STATUSBYTE0: bytes[0],
		STATUSBYTE1: bytes[1],
		ERROR_BACKFLOW: Boolean(msblsb & (1 << 15)),
        ERROR_ZEROFLOW: Boolean(msblsb & (1 << 14)),
      	ERROR_RESET: 	Boolean(msblsb & (1 << 13)),
		ERROR_RF: 		Boolean(msblsb & (1 << 12)),
        ERROR_CS: 		Boolean(msblsb & (1 << 11)),
        ERROR_BATTLOW: 	Boolean(msblsb & (1 << 10)),
      	ERROR_SABOT: 	Boolean(msblsb & (1 << 9)),
      	ERROR_MESS: 	Boolean(msblsb & (1 << 8)),
		ERROR_LEAKAGE: 	Boolean(msblsb & (1 << 7)),
      	ERROR_FROST: 	Boolean(msblsb & (1 << 6)),
        CUT_OFF: 		Boolean(msblsb & (1 << 4)) ? "monthly" : "yearly",
        INSTALL: 		Boolean(msblsb & (1 << 3)) ? "2min" : "no",
      	INTERVAL:
    					intervall === 0 ? "HIST" :
    					intervall === 1 ? "STATISTIC" :
    					intervall === 2 ? "1_DAY" :
						intervall === 3 ? "2_DAY" :
    					intervall === 4 ? "4_DAY" :
      					intervall === 5 ? "OPTION1" : 
						intervall === 6 ? "OPTION2" : 
            			intervall === 7 ? "OPTION3" : "undefined"
    }
}

function parseInstallBits(bytes, start_index) {
    var msblsb = (bytes[start_index+18]<<8)
        msblsb |= bytes[start_index+19]
  	var intervall = (msblsb >> 5) & 7
  	var confirm = (msblsb >> 14) & 3
	
  return {
    	HW_VERSION: 	bytesToBCD_1(bytes[start_index+0]), 
    	SW_VERSION: 	bytesToBCD_1(bytes[start_index+1]),
    	GRNr: 			bytesToBCD_4([bytes[start_index+5],bytes[start_index+4],bytes[start_index+3],bytes[start_index+2]]),
    	ZLNr: 			bytesToBCD_4([bytes[start_index+9],bytes[start_index+8],bytes[start_index+7],bytes[start_index+6]]),
    	ChipTemp: 		bytes[start_index+10],
		VDD_mV :		((bytes[start_index+11]<<8)+bytes[start_index+12]),
      	MaxBckFlow:  	bytes[start_index+13],
      	StartFlow: 		bytes[start_index+14],
      	MaxLeakHour: 	bytes[start_index+15],
      	MaxZeroFlowDay: bytes[start_index+16],
      	ForceReJoin: 	bytes[start_index+17],

        CONFIRM: 	    confirm === 0 ? "DIS" :
    					confirm === 1 ? "ALL" :
    					confirm === 2 ? "2_TELEGRAMM" :
						confirm === 3 ? "5_TELEGRAMM" : "DIS",

        OTAA: 		Boolean(msblsb & (1 << 13)),
        ADR: 		Boolean(msblsb & (1 << 12)),
        LINKCHECK: 	Boolean(msblsb & (1 << 11)),
        RTC_AUTO: 	Boolean(msblsb & (1 << 10)),
        ALARM: 		Boolean(msblsb & (1 << 9)),
        CUT_OFF: 	Boolean(msblsb & (1 << 8)) ? "monthly" : "yearly",
      	INTERVAL:
    					intervall === 0 ? "HIST" :
    					intervall === 1 ? "STATISTIC" :
    					intervall === 2 ? "1_DAY" :
						intervall === 3 ? "2_DAY" :
    					intervall === 4 ? "4_DAY" :
      					intervall === 5 ? "OPTION1" : 
						intervall === 6 ? "OPTION2" : 
            			intervall === 7 ? "OPTION3" : "undefined"
  }    

}

function parse2BytesMsb(bytes, start_index) {
    var index = start_index;
    var value = bytes[index++] << 8;
    value |= bytes[index++];
    return value;
}
function parse4BytesHEX(bytes, start_index) {
    var index = start_index;
  	var value = [0, 0, 0, 0];
 	value[0]= bytes[index++];
    value[1]= bytes[index++];
    value[2]= bytes[index++];
    value[3]= bytes[index++];
    return value;
}
function parse4BytesMsb(bytes, start_index) {
    var index = start_index;
    var value = bytes[index++] << 24;
    value |= bytes[index++] << 16;
    value |= bytes[index++] << 8;
    value |= bytes[index++];
    return value;
}

function parse4BytesLsb(bytes, start_index) {
    var index = start_index;
    var value = bytes[index++];
    value |= bytes[index++] << 8;
    value |= bytes[index++] << 16;
    value |= bytes[index++] << 24;
    return value;
}
function parse4BytesMSBasArray(buffer,startindex,Anzahl) {
    var result = [];
    
    for (var i = 0; i < Anzahl; i ++) {
        var value = 0;
        value |= buffer[startindex] << 24;
        value |= buffer[startindex + 1] << 16;
        value |= buffer[startindex + 2] << 8;
      	value |= buffer[startindex + 3];
		startindex=startindex+4;
        result.push(value);
    }
    
    return result;
}
function bytesToBCD_4(bytes) {
    var bcdString = '';
    for (var i = 0; i < bytes.length; i++) {
        var byte = bytes[i];
        var upperNibble = byte >> 4;
        var lowerNibble = byte & 0x0F;
        bcdString += upperNibble.toString(16) + lowerNibble.toString(16);
    }
    return bcdString.toUpperCase().padStart(8, '0');
}
function bytesToBCD_1(bytes) {
    var bcdString = '';
    var byte = bytes;
    var upperNibble = byte >> 4;
    var lowerNibble = byte & 0x0F;
    bcdString += upperNibble.toString(16) + lowerNibble.toString(16);
    return bcdString.toUpperCase();
}
// Encode downlink function.
//
// Input is an object with the following fields:
// - data = Object representing the payload that must be encoded.
// - variables = Object containing the configured device variables.
//
// Output must be an object with the following fields:
// - bytes = Byte array containing the downlink payload.
function encodeDownlink(input) {
  return {
    bytes: [225, 230, 255, 0]
  };
}
