/**
 ** -----------------------------------------------------------------------------**
 ** Boson640Telemetry.java
 **
 ** Parses FLIR Boson640 telemetry data
 **
 ** Copyright (C) 2021 Elphel, Inc.
 **
 ** -----------------------------------------------------------------------------**
 **
 **  Boson640Telemetry.java is free software: you can redistribute it and/or modify
 **  it under the terms of the GNU General Public License as published by
 **  the Free Software Foundation, either version 3 of the License, or
 **  (at your option) any later version.
 **
 **  This program is distributed in the hope that it will be useful,
 **  but WITHOUT ANY WARRANTY; without even the implied warranty of
 **  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 **  GNU General Public License for more details.
 **
 **  You should have received a copy of the GNU General Public License
 **  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 ** -----------------------------------------------------------------------------**
 **
 */
package com.elphel.imagej.readers;

import java.nio.ByteBuffer;
import java.util.HashMap;

public class Boson640Telemetry {
	private static final int BOSON640_IMAGE_WIDTH =     640;
	private static final int BOSON640_IMAGE_HEIGHT =    512;
	private static final int BOSON640_TELEMETRY_LINES =   1;
	private static final String REVISION =      "REVISION";      private static final int REVISION_OFFSET =       0; //  2
	private static final String CAMSERIAL =     "CAMSERIAL";     private static final int CAMSERIAL_OFFSET =      2; //  4
	private static final String SENSSERIAL =    "SENSSERIAL";    private static final int SENSSERIAL_OFFSET =     6; //  4
	private static final String PART_ASCII =    "PART_ASCII";     private static final int PART_ASCII_OFFSET =     10; // 20

	private static final String SOFT_REV =      "SOFT_REV";      private static final int SOFT_REV_OFFSET =      44; // 12
	private static final String FRAME_RATE =    "FRAME_RATE";    private static final int FRAME_RATE_OFFSET =    56; //  2
	private static final String STATUS =        "STATUS";        private static final int STATUS_OFFSET =        76; // hex,8
	private static final String FRAME =         "FRAME";         private static final int FRAME_OFFSET =         84; // 4
	private static final String FRAME_FFC =     "FRAME_FFC";     private static final int FRAME_FFC_OFFSET =     88; // 4
	private static final String FPA_KELV =      "FPA_KELV";      private static final int FPA_KELV_OFFSET =      94; // 2
	private static final String FFC_KELV =      "FFC_KELV";      private static final int FFC_KELV_OFFSET =      96; // 2
	private static final String PIPELINE =      "PIPELINE";      private static final int PIPELINE_OFFSET =     110; // 4
	private static final String FFC_INTEG =     "FFC_INTEG";     private static final int FFC_INTEG_OFFSET =    114; // 2
	private static final String NUC_CURRENT =   "NUC_CURRENT";   private static final int NUC_CURRENT_OFFSET =  158; // 2
	private static final String NUC_DESIRED =   "NUC_DESIRED";   private static final int NUC_DESIRED_OFFSET =  160; // 2
	private static final String CORE_TEMP =     "CORE_TEMP";     private static final int CORE_TEMP_OFFSET =    162; // 4
	private static final String OVERTEMP =      "OVERTEMP";      private static final int OVERTEMP_OFFSET =     166; // 4
	private static final String ROI_POP_LTH =   "ROI_POP_LTH";   private static final int ROI_POP_LTH_OFFSET =  170; // 4
	private static final String ROI_POP_HTL =   "ROI_POP_HTL";   private static final int ROI_POP_HTL_OFFSET =  174; // 4
	private static final String TGL_PATT =      "TGL_PATT";      private static final int TGL_PATT_OFFSET =     178; // 6
	private static final String ZOOM_FACT =     "ZOOM_FACT";     private static final int ZOOM_FACT_OFFSET =    184; // 4
	private static final String ZOOM_X0 =       "ZOOM_X0";       private static final int ZOOM_X0_OFFSET =      188; // 4
	private static final String ZOOM_Y0 =       "ZOOM_Y0";       private static final int ZOOM_Y0_OFFSET =      192; // 4



	private float [] pixels =  null;
	private byte  [] btm =     null;
	private ByteBuffer bbtm  = null;

	public Boson640Telemetry(ByteBuffer bb, float [] pixels, boolean bottom) {
		this.pixels = pixels;
		int offset = 2*(bottom?(BOSON640_IMAGE_WIDTH * BOSON640_IMAGE_HEIGHT):0);
		if (pixels.length > BOSON640_IMAGE_WIDTH*BOSON640_IMAGE_HEIGHT) {
			this.btm = new byte [BOSON640_IMAGE_WIDTH* BOSON640_TELEMETRY_LINES * 2];
			bb.position(offset);
			byte dbg = bb.get();
			dbg = bb.get();
			dbg = bb.get();
			dbg = bb.get();
			dbg = bb.get();
			bb.position(offset);

			bb.get(btm, 0,btm.length);
			this.bbtm = ByteBuffer.wrap(btm);
			this.bbtm.order( bb.order()); // or is it already same as in bb?
			
//			System.out.println("getShort(94)"+this.bbtm.getShort(94));
//			System.out.println("getLong(162)"+this.bbtm.getLong(162));
//			System.out.println("getLong(162)"+this.bbtm.getInt(162));
			
			
			
			
			this.pixels = new float [BOSON640_IMAGE_WIDTH * BOSON640_IMAGE_HEIGHT];
			System.arraycopy(
					pixels,
					(bottom? 0: BOSON640_IMAGE_WIDTH * BOSON640_TELEMETRY_LINES),
					this.pixels,
					0,
					this.pixels.length);
		}
	}

	public static int getWidth() {
		return BOSON640_IMAGE_WIDTH;
	}

	public static int getHeight() {
		return BOSON640_IMAGE_HEIGHT;
	}
	
	public static int getTelemetryLines() {
		return BOSON640_TELEMETRY_LINES;
	}
	
	
	public boolean hasTelemetry() {
		return bbtm != null;
	}

	public float [] getPixels() {
		return pixels;
	}

	public HashMap<String, String> parseTelemetry(){
		HashMap<String, String> tm = new HashMap<String, String>();
		byte [] bytes = getBytes(REVISION_OFFSET,2);
		tm.put(REVISION,      "" + bytes[0] + "." + bytes[1]); // "0.2"
		tm.put(CAMSERIAL,     "" + String.format("%d", getU32 (CAMSERIAL_OFFSET)));
		tm.put(SENSSERIAL,    "" + String.format("%d", getU32 (SENSSERIAL_OFFSET)));
		tm.put(PART_ASCII,    "" + getAscii(PART_ASCII_OFFSET,20));
		tm.put(STATUS,        "" + (bbtm.getLong(STATUS_OFFSET))); // getHex (STATUS_OFFSET,8));
		tm.put(SOFT_REV,      "" + getU32 (SOFT_REV_OFFSET)+"."+getU32 (SOFT_REV_OFFSET+4)+"."+getU32 (SOFT_REV_OFFSET+8));
		tm.put(FRAME_RATE,    "" + getU16 (FRAME_RATE_OFFSET));
		tm.put(FRAME,         "" + getU32 (FRAME_OFFSET));
		tm.put(FRAME_FFC,     "" + getU32 (FRAME_FFC_OFFSET));
		tm.put(FPA_KELV,      String.format("%.1f", getKelv10 (FPA_KELV_OFFSET)));
		tm.put(FFC_KELV,      String.format("%.1f", getKelv10 (FFC_KELV_OFFSET)));
		tm.put(PIPELINE,      "" + getU32 (PIPELINE_OFFSET));
		tm.put(FFC_INTEG,     "" + getU16 (FFC_INTEG_OFFSET));
		tm.put(NUC_CURRENT,   "" + getU16 (NUC_CURRENT_OFFSET));
		tm.put(NUC_DESIRED,   "" + getU16 (NUC_DESIRED_OFFSET));
		tm.put(CORE_TEMP,     String.format("%.3f", getCelsius1000 (CORE_TEMP_OFFSET)));
		tm.put(OVERTEMP,      "" + getU32 (OVERTEMP_OFFSET));
		tm.put(ROI_POP_LTH,   "" + getU32 (ROI_POP_LTH_OFFSET));
		tm.put(ROI_POP_HTL,   "" + getU32 (ROI_POP_HTL_OFFSET));
		tm.put(TGL_PATT,      "" + getHex (TGL_PATT_OFFSET,8));
		tm.put(ZOOM_FACT,     "" + getU32 (ZOOM_FACT_OFFSET));
		tm.put(ZOOM_X0,       "" + getU32 (ZOOM_X0_OFFSET));
		tm.put(ZOOM_Y0,       "" + getU32 (ZOOM_Y0_OFFSET));
		return tm;
	}
	// internal methods

	private long getU32(int offset) {
		return ((long) (bbtm.getInt(offset))) & 0xffffffff;
	}

	private long getU16(int offset) {
		return ((long) (bbtm.getShort(offset))) & 0xffff;
	}

	private byte[] getBytes (int offset, int len) { // offset, len in bytes!
		byte [] bytes = new byte [len];
		for (int i = 0; i<len; i++) {
			bytes[i] = bbtm.get(offset+i);
		}
		return bytes;
	}
	private String getHex (int offset, int len) { // offset, len bytes,
		String s = "";
		for (byte b :getBytes(offset, len)) {
			s += String.format("%02x", b);
		}
		return s;
	}

	private String getAscii (int offset, int len) { // offset, len bytes,
		String s = "";
		for (byte b :getBytes(offset, len)) {
			if (b == 0) break;
			s += String.format("%c", b);
		}
		return s;
	}
	
	private double getKelv10(int offset) {
		return 0.1*getU16(offset);
	}
	private double getCelsius1000(int offset) {
		return 0.001*bbtm.getInt(offset);
	}
	

}