/**
 **
 ** TestDataView.java - open any test-case or C++-results buffer as a
 ** properly-shaped ImageJ hyperstack, straight from the manifest — no more
 ** typing dimensions into the raw importer by hand. TD buffers (trailing
 ** dim 256 = 4 x 8x8 CLT tile) are rendered as tile grids with the same
 ** 2x2 quadrant placement as GpuQuad.presentCltData(); count/tile planes
 ** (product = tilesy*tilesx*colors) become tilesX x tilesY images; anything
 ** else falls back to [slices][height][width] from its last two dims.
 **
 ** Copyright (C) 2026 Elphel, Inc.
 **
 ** -----------------------------------------------------------------------------**
 **
 **  TestDataView.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/>.
 ** -----------------------------------------------------------------------------**
 **
 ** By Claude on 07/10/2026
 */
package com.elphel.imagej.gpu.testdata;

import java.io.IOException;

import com.elphel.imagej.gpu.GPUTileProcessor;

import ij.ImagePlus;
import ij.ImageStack;
import ij.process.FloatProcessor;

public class TestDataView {
	/** View a buffer from the test-case inputs (<dir>/manifest.txt). */
	public static ImagePlus viewCase(String case_dir, String name) throws IOException {
		TestDataReader rdr = new TestDataReader(case_dir);
		return view(rdr, rdr, name, name);
	}

	/**
	 * View a buffer from the C++ test results (<dir>/results/). Scalar
	 * parameters (tilesx/tilesy/colors) come from the case manifest — the
	 * results manifest only lists buffers.
	 */
	public static ImagePlus viewResults(String case_dir, String name) throws IOException {
		TestDataReader prms = new TestDataReader(case_dir);
		TestDataReader rdr = TestDataReader.results(case_dir);
		return view(rdr, prms, name, name + " (results)");
	}

	/** Open every buffer of the case and of results/ (if present) as windows. */
	public static void showAll(String case_dir) throws IOException {
		TestDataReader rdr = new TestDataReader(case_dir);
		for (String name : rdr.getNames()) {
			view(rdr, rdr, name, name).show();
		}
		try {
			TestDataReader res = TestDataReader.results(case_dir);
			for (String name : res.getNames()) {
				view(res, rdr, name, name + " (results)").show();
			}
		} catch (IOException e) { // no results yet - fine
		}
	}

	/**
	 * Shape a buffer into an ImagePlus using its manifest dims (+ tile
	 * parameters from prms when the shape is TD- or tile-plane-like).
	 */
	public static ImagePlus view(
			TestDataReader rdr,
			TestDataReader prms,
			String         name,
			String         title) throws IOException {
		float [] data;
		if (rdr.getEntry(name).dtype.equals("i32")) {
			int [] idata = rdr.getInts(name);
			data = new float [idata.length];
			for (int i = 0; i < idata.length; i++) data[i] = idata[i];
		} else {
			data = rdr.getFloats(name);
		}
		int [] dims =  rdr.dims(name);
		int tilesX =   prms.paramInt("tilesx", 0);
		int tilesY =   prms.paramInt("tilesy", 0);
		int colors =   prms.paramInt("colors", 1);
		int dtt =      GPUTileProcessor.DTT_SIZE;
		int chunk =    4 * dtt * dtt; // one TD tile per color, 256
		int num_tiles = tilesX * tilesY * colors;

		if ((dims[dims.length - 1] == chunk) && (num_tiles > 0) &&
				((data.length % (num_tiles * chunk)) == 0)) {
			return tdGrid(data, title, tilesX, tilesY, colors, data.length / (num_tiles * chunk));
		}
		if ((num_tiles > 0) && (data.length % num_tiles == 0) &&
				((data.length / num_tiles) <= 16)) { // counts/weights/flags per tile
			return planes(data, title, tilesX, tilesY, (data.length / num_tiles) * colors);
		}
		// generic fallback: [slices][height][width] from the last two dims
		int width =  dims[dims.length - 1];
		int height = (dims.length > 1) ? dims[dims.length - 2] : 1;
		return planes(data, title, width, height, data.length / (width * height));
	}

	/** TD tile grid, same quadrant placement as GpuQuad.presentCltData(). */
	private static ImagePlus tdGrid(
			float [] data,
			String   title,
			int      tilesX,
			int      tilesY,
			int      colors,
			int      num_slices) { // e.g. 16 sensors, or 1 for the average
		int dtt = GPUTileProcessor.DTT_SIZE;
		int quad_size = dtt * dtt;
		int width =  2 * dtt * tilesX;
		int height = 2 * dtt * tilesY;
		int slice_len = tilesX * tilesY * colors * 4 * quad_size;
		ImageStack stack = new ImageStack(width, height);
		for (int slice = 0; slice < num_slices; slice++) {
			for (int color = 0; color < colors; color++) {
				float [] pixels = new float [width * height];
				for (int ntile = 0; ntile < (tilesX * tilesY); ntile++) {
					for (int nquad = 0; nquad < 4; nquad++) {
						for (int row = 0; row < dtt; row++) {
							int src = slice * slice_len +
									(((ntile * colors + color) * 4) + nquad) * quad_size + row * dtt;
							int dst = (((ntile / tilesX) * 2 + (nquad / 2)) * dtt + row) * width +
									((ntile % tilesX) * 2 + (nquad % 2)) * dtt;
							System.arraycopy(data, src, pixels, dst, dtt);
						}
					}
				}
				stack.addSlice(title + "-" + slice + ((colors > 1) ? ("-c" + color) : ""), pixels);
			}
		}
		return new ImagePlus(title, stack);
	}

	private static ImagePlus planes(float [] data, String title, int width, int height, int num_slices) {
		ImageStack stack = new ImageStack(width, height);
		for (int slice = 0; slice < num_slices; slice++) {
			float [] pixels = new float [width * height];
			System.arraycopy(data, slice * width * height, pixels, 0, width * height);
			stack.addSlice(title + "-" + slice, new FloatProcessor(width, height, pixels, null).getPixels());
		}
		return new ImagePlus(title, stack);
	}
}
