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 void setParentIndex(int indx) {
		parent_index = indx;
	}
	
	public int getParentIndex() {
		return parent_index;
	}
	*/
	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();
	}

}
