/**
 **
 ** CuasTile.java - CLT tile data container for CUAS UAS target accumulation
 **
 ** Copyright (C) 2025-2026 Elphel, Inc.
 **
 ** -----------------------------------------------------------------------------**
 **
 **  CuasTile.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.cuas;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

import com.elphel.imagej.gpu.GPUTileProcessor;

public class CuasTile implements  Comparable<CuasTile>, Serializable {
	private static final long serialVersionUID = 1L;
	public final static int TILE_SIZE = GPUTileProcessor.DTT_SIZE; // 8
	public final static int TILE_LENGTH = TILE_SIZE * TILE_SIZE;   // 64
	public final static int CLT_TILE_LENGTH = 4 * TILE_LENGTH;     // 256
	public float [] clt_data; //  = new float [CLT_TILE_LENGTH];
	public double   dts =      0; // time stamp as double
	public double   weight = 0;

	/*
	public int      parent_index = -1;
	*/
	public CuasTile (
			int num_colors,
			double dts,
			double weight,
			float [] clt_data) {
//		clt_data = new float [num_colors * CLT_TILE_LENGTH];
		this.dts = dts;
		this.weight = weight;
		this.clt_data = clt_data;
	}
	public CuasTile (
			int num_colors,
			double dts,
			double weight,
			float [] clt_full,
			int ntile) {
		int tile_length = num_colors * CLT_TILE_LENGTH;
		clt_data = new float [tile_length];
		this.dts = dts;
		this.weight = weight;
		System.arraycopy(
				clt_full,
				ntile * tile_length,
				clt_data,
				0,
				tile_length);
	}
	
	public void getData (
			float [] fclt,
			int      ntile) {
		if (isEmpty()) return; // do nothing
		System.arraycopy(
				clt_data,
				0,
				fclt,
				ntile * clt_data.length,
				clt_data.length);
	}
	
	
	public double getWeight() {
		return weight;
	}

	public double getTimeStamp() {
		return dts;
	}
	
	public boolean isEmpty() {
		return (clt_data == null) || (weight <= 0);
	}
	public float [] getData() {
		return clt_data;
	}

	public double getWeight(
			double decay,
			double ts_now) {
		if (isEmpty()) {
			return 0;
		}
		if (decay > 0) {
			return weight * Math.exp(-Math.max(ts_now-this.dts, 0)/decay);
		}
		return weight;
	}
	
	public CuasTile cloneEmpty() {
		int num_colors = clt_data.length/CLT_TILE_LENGTH;
//		return new CuasTile(num_colors, dts, 0, null);
		return new CuasTile(num_colors, dts, 0, new float[clt_data.length]);
	}
	
	public double merge (CuasTile tile, double decay) {
		if ((tile.clt_data == null) || (tile.weight == 0)) {
			// do nothing
		} else if ((clt_data == null) || (weight == 0)) {
			clt_data = tile.clt_data;
			weight =   tile.weight;
			dts =      tile.dts;
		} else {
			double w = weight, wt = tile.weight;
			if (decay > 0) {
				if (dts < tile.dts) {
					w = getWeight(
							decay, // double decay,
							tile.dts); // double ts_now)
				} else if (dts > tile.dts) {
					wt = tile.getWeight(
							decay, // double decay,
							dts); // double ts_now)
				}
			}
			double sumw = w + wt;
			w  /= sumw;
			wt /= sumw;
			for (int i = 0; i < clt_data.length; i++) {
				clt_data[i] = (float)(clt_data[i] * w + tile.clt_data[i] * wt); 
			}
			dts = Math.max(dts, tile.dts);
			weight = sumw;
		}
		return weight;
		
	}
	
	@Override
	public CuasTile clone() {
		int num_colors = clt_data.length/CLT_TILE_LENGTH;
		return new CuasTile(num_colors, dts, weight, clt_data.clone());
	}

	@Override
	public int compareTo(CuasTile otherTile) {
		return Double.compare(dts, otherTile.dts);
	}

	public double diffTile2 (float [] clt_data_other) {
		double d2 = 0;
		for (int i = 0; i < clt_data.length; i++) {
			double dd = clt_data_other[i] - clt_data[i];
			d2 += dd * dd;
		}
		return d2/clt_data.length;
	}

	public double diffTile2 (CuasTile tile_other) {
		return diffTile2(tile_other.clt_data);
	}

	@SuppressWarnings("static-method")
	private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
		ois.defaultReadObject();
	}

	@SuppressWarnings("static-method")
	private void writeObject(ObjectOutputStream oos) throws IOException {
		oos.defaultWriteObject();
	}

}
