/**
 **
 ** TestDataWriter.java - write a standalone-GPU-test data directory:
 ** raw little-endian arrays + manifest.txt describing every buffer and
 ** scalar parameter. Contract documented in
 ** tile_processor_gpu/src/tests/testdata_format.md: the Java oracle exports
 ** ALL inputs a per-kernel CUDA test needs, plus its own expected outputs
 ** (name prefix "expected_"), so the C++ side can run and self-check with
 ** no dependency on old calibration files or a live Java process.
 **
 ** Copyright (C) 2026 Elphel, Inc.
 **
 ** -----------------------------------------------------------------------------**
 **
 **  TestDataWriter.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/09/2026
 */
package com.elphel.imagej.gpu.testdata;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.WritableByteChannel;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;

public class TestDataWriter implements AutoCloseable {
	private final File          dir;
	private final StringBuilder manifest = new StringBuilder();
	private boolean             closed = false;

	/**
	 * @param dir_path test-case directory (created if missing), one per kernel
	 *                 test case, e.g. .../testdata/1773135476_186641/avg_td
	 * @param comment  free-text provenance line for the manifest header
	 */
	public TestDataWriter(String dir_path, String comment) throws IOException {
		dir = new File(dir_path);
		if (!dir.isDirectory() && !dir.mkdirs()) {
			throw new IOException("TestDataWriter: cannot create " + dir_path);
		}
		manifest.append("# ").append(comment).append("\n");
	}

	/** Add a scalar parameter ("prm" manifest record). */
	public void prm(String name, double value) {
		manifest.append("prm ").append(name).append(" ").append(value).append("\n");
	}

	/**
	 * Write a float32 buffer: raw little-endian <name>.f32 + "buf" manifest
	 * record. Dims are row-major, LAST dimension fastest (flat Java order).
	 */
	public void buf(String name, float [] data, int ... dims) throws IOException {
		checkDims(name, data.length, dims);
		ByteBuffer bb = ByteBuffer.allocate(data.length * 4).order(ByteOrder.LITTLE_ENDIAN);
		bb.asFloatBuffer().put(data);
		writeRaw(name + ".f32", bb);
		manifestBuf(name, "f32", name + ".f32", dims);
	}

	/** Write an int32 buffer: raw little-endian <name>.i32 + manifest record. */
	public void buf(String name, int [] data, int ... dims) throws IOException {
		checkDims(name, data.length, dims);
		ByteBuffer bb = ByteBuffer.allocate(data.length * 4).order(ByteOrder.LITTLE_ENDIAN);
		bb.asIntBuffer().put(data);
		writeRaw(name + ".i32", bb);
		manifestBuf(name, "i32", name + ".i32", dims);
	}

	/** Finalize: write manifest.txt. Buffers already on disk. */
	@Override
	public void close() throws IOException {
		if (closed) return;
		closed = true;
		Files.write(new File(dir, "manifest.txt").toPath(),
				manifest.toString().getBytes(StandardCharsets.UTF_8));
	}

	public String getDir() {
		return dir.getPath();
	}

	private static void checkDims(String name, int length, int [] dims) {
		long product = 1;
		for (int d : dims) product *= d;
		if (product != length) {
			throw new IllegalArgumentException("TestDataWriter: buffer '" + name +
					"' length " + length + " does not match dims product " + product);
		}
	}

	private void writeRaw(String fname, ByteBuffer bb) throws IOException {
		bb.rewind();
		try (FileOutputStream fos = new FileOutputStream(new File(dir, fname));
				WritableByteChannel ch = fos.getChannel()) {
			ch.write(bb);
		}
	}

	private void manifestBuf(String name, String dtype, String fname, int [] dims) {
		manifest.append("buf ").append(name).append(" ").append(dtype)
				.append(" ").append(dims.length);
		for (int d : dims) manifest.append(" ").append(d);
		manifest.append(" ").append(fname).append("\n");
	}

	/** Debug print of the manifest built so far. */
	public void print(PrintWriter pw) {
		pw.print(manifest);
	}
}
