/**
 **
 ** GenericJTabbedDialogMcp.java - MCP-accessible wrapper for GenericJTabbedDialog
 **
 ** Copyright (C) 2026 Elphel, Inc.
 **
 ** -----------------------------------------------------------------------------**
 **
 **  GenericJTabbedDialogMcp.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.common;

import java.util.ArrayList;
import java.util.List;

import com.elphel.imagej.mcp.McpDialogField;
import com.elphel.imagej.mcp.McpDialogRegistry;
import com.elphel.imagej.mcp.McpDialogSession;

import ij.IJ;

public class GenericJTabbedDialogMcp extends GenericJTabbedDialog{
	// codex 2026-01-25: MCP dialog capture state
	private static boolean defaultMcpMode = false;
	private static long dialogTimeoutMs = 30000L;
	public boolean mcp_mode = false;
	private boolean mcpCanceled = false;
	private McpDialogSession mcpSession = null; // this dialog's OWN session: the registry is cleared on submit BEFORE the getNext*() reads run, so reads must not go through it // By Claude on 07/07/2026
	private final List<McpDialogField> mcpFields = new ArrayList<McpDialogField>();
	private final String dialogTitle;
	private String currentTab = "";
	private int readIndex = 0;
	public GenericJTabbedDialogMcp(String title) {
		super(title);
		this.dialogTitle = title;
		this.mcp_mode = defaultMcpMode;
	}
	public GenericJTabbedDialogMcp(String title, int width, int height) {
		super(title, width, height);
		this.dialogTitle = title;
		this.mcp_mode = defaultMcpMode;
	}
	// codex 2026-01-27: allow modeless dialog creation
	public GenericJTabbedDialogMcp(String title, int width, int height, boolean modal) {
		super(title, width, height, modal);
		this.dialogTitle = title;
		this.mcp_mode = defaultMcpMode;
	}

	// codex 2026-01-25: global MCP mode default
	public static void setDefaultMcpMode(boolean enabled) {
		defaultMcpMode = enabled;
	}
	// codex 2026-01-27: expose MCP mode default
	public static boolean isDefaultMcpMode() {
		return defaultMcpMode;
	}
	// codex 2026-01-28: configurable MCP dialog submit timeout (0 = wait forever)
	public static void setDialogTimeoutMs(long timeoutMs) {
		dialogTimeoutMs = timeoutMs;
	}
	// codex 2026-01-28: override MCP mode per dialog instance
	public void setMcpMode(boolean enabled) {
		this.mcp_mode = enabled;
	}

	private void addMcpField(McpDialogField field) {
		mcpFields.add(field);
	}

	@Override
	public void addTab(String tab_name, String tab_tooltip) {
		if (mcp_mode) {
			// codex 2026-01-25: capture current tab for MCP schema
			currentTab = tab_name;
		}
		else super.addTab(tab_name, tab_tooltip);
	}

	@Override
	public void addCheckbox(String label, boolean defaultValue, String tooltip) {
		if (mcp_mode) {
			addMcpField(new McpDialogField(
					McpDialogField.Type.BOOLEAN,
					label,
					tooltip,
					currentTab,
					null,
					null,
					Boolean.toString(defaultValue)));
		}
		else super.addCheckbox(label, defaultValue, tooltip);
	}

	@Override
	public void addNumericField(String label, double defaultValue, int digits, int columns, String units, String tooltip) {
		if (mcp_mode) {
			String defaultText = IJ.d2s(defaultValue, digits);
			addMcpField(new McpDialogField(
					McpDialogField.Type.NUMBER,
					label,
					tooltip,
					currentTab,
					units,
					null,
					defaultText));
		}
		else super.addNumericField(label, defaultValue, digits, columns, units, tooltip);
	}    

	@Override
	public void addStringField (
			String label,
			String value,
			int    width,
			String tooltip) {
		if (mcp_mode) {
			addMcpField(new McpDialogField(
					McpDialogField.Type.STRING,
					label,
					tooltip,
					currentTab,
					null,
					null,
					value));
		}
		else super.addStringField (label, value, width, tooltip);
	}

	@Override
	public void addChoice(String label, String[] items, String defaultItem, String tooltip, int count) {
		if (mcp_mode) {
			addMcpField(new McpDialogField(
					McpDialogField.Type.CHOICE,
					label,
					tooltip,
					currentTab,
					null,
					items,
					defaultItem));
		}
		else super.addChoice(label, items, defaultItem, tooltip, count);
	}	

	@Override
	public void addChoice(String label, String[] items, String defaultItem) {
		addChoice(label, items, defaultItem, null, 0);
	}

	@Override
	public void addChoice(String label, String[] items, String defaultItem, String tooltip) {
		addChoice(label, items, defaultItem, tooltip, 0);
	}

	@Override
	public void addDefaultButtons() { // not used
		if (mcp_mode) {

		}
		else super.addDefaultButtons();
	}

	@Override
	public void addMessage(String message, String tooltip) {
		if (mcp_mode) {
			addMcpField(new McpDialogField(
					McpDialogField.Type.MESSAGE,
					message,
					tooltip,
					currentTab,
					null,
					null,
					null));
		}
		else super.addMessage(message, tooltip);
	}

	@Override
	public void buildDialog() { // non-blocking, does not show
		if (mcp_mode) {
			// codex 2026-01-25: publish dialog schema to MCP registry
			readIndex = 0;
			mcpSession = new McpDialogSession(dialogTitle, mcpFields); // By Claude on 07/07/2026: keep own reference (see field comment)
			boolean applied = McpDialogRegistry.setCurrent(mcpSession);
			if (!applied) {
				System.out.println("MCP: dialog already active, ignoring \"" + dialogTitle + "\"");
			}
		}
		else super.buildDialog();
	}

	private McpDialogField nextField(McpDialogField.Type type) {
		while (readIndex < mcpFields.size()
				&& mcpFields.get(readIndex).type == McpDialogField.Type.MESSAGE) {
			readIndex++;
		}
		if (readIndex >= mcpFields.size()) {
			return null;
		}
		McpDialogField field = mcpFields.get(readIndex);
		if (field.type != type) {
			throw new IllegalArgumentException(
					"MCP dialog field mismatch: expected " + type + ", got " + field.type + " label=" + field.label);
		}
		readIndex++;
		return field;
	}

	private String getValueOrDefault(McpDialogField field) {
		McpDialogSession session = mcpSession; // By Claude on 07/07/2026: was McpDialogRegistry.getCurrent() - always null here (registry cleared on submit before the reads), so /mcp/dialog/values was silently discarded
		if (session != null && field != null) {
			String value = session.getValue(field.label);
			if (value != null) {
				return value;
			}
		}
		return field == null ? null : field.defaultValue;
	}

	@Override
	public boolean getNextBoolean() {
		if (mcp_mode) {
			McpDialogField field = nextField(McpDialogField.Type.BOOLEAN);
			String value = getValueOrDefault(field);
			if (value == null) {
				return false;
			}
			String normalized = value.trim().toLowerCase();
			return normalized.equals("true") || normalized.equals("1") || normalized.equals("yes") || normalized.equals("y");
		}
		else return super.getNextBoolean();
	}

	@Override
	public int getNextChoiceIndex() {
		if (mcp_mode) {
			McpDialogField field = nextField(McpDialogField.Type.CHOICE);
			String value = getValueOrDefault(field);
			if (value == null || field.choices == null) {
				return 0;
			}
			try {
				int idx = Integer.parseInt(value.trim());
				if (idx >= 0 && idx < field.choices.length) {
					return idx;
				}
			} catch (NumberFormatException e) {
				// ignore and try by name
			}
			for (int i = 0; i < field.choices.length; i++) {
				if (field.choices[i].equals(value)) {
					return i;
				}
			}
			return 0;
		}
		else return super.getNextChoiceIndex();
	}

	@Override
	public double getNextNumber() {
		if (mcp_mode) {
			McpDialogField field = nextField(McpDialogField.Type.NUMBER);
			String value = getValueOrDefault(field);
			if (value == null || value.isEmpty()) {
				return Double.NaN;
			}
			try {
				return Double.parseDouble(value);
			} catch (NumberFormatException e) {
				return Double.NaN;
			}
		}
		else return super.getNextNumber();
	}

	@Override
	public String getNextString() {
		if (mcp_mode) {
			McpDialogField field = nextField(McpDialogField.Type.STRING);
			String value = getValueOrDefault(field);
			return value == null ? "" : value;
		}
		else return super.getNextString();
	}

	@Override
	public boolean showDialog() {
		if (mcp_mode) {
			buildDialog();
			McpDialogSession session = mcpSession; // By Claude on 07/07/2026: own session (registry may hold another dialog / be cleared by submit)
			if (session != null) {
				long timeout = dialogTimeoutMs;
				Boolean ok = (timeout <= 0) ? session.awaitSubmit(Long.MAX_VALUE) : session.awaitSubmit(timeout);
				if (ok != null && !ok.booleanValue()) {
					mcpCanceled = true;
				} else {
					mcpCanceled = false;
				}
			}
			if (McpDialogRegistry.getCurrent() == mcpSession) { // By Claude on 07/07/2026: clear only OUR session (submit normally cleared it; this covers the timeout path without clobbering a newer dialog)
				McpDialogRegistry.setCurrent(null);
			}
			return true;
		}
		else return super.showDialog();
	}

	// codex 2026-01-27: modeless dialog for GUI mode
	public void showDialogNonBlocking() {
		if (mcp_mode) {
			buildDialog();
		}
		else super.showDialogNonBlocking();
	}

	@Override
	public String showDialogAny() {
		if (mcp_mode) {
			buildDialog();
			return "";
		}
		else return super.showDialogAny();
	}


	@Override
	public boolean wasCanceled() {
		if (mcp_mode) {
			return mcpCanceled;
		}
		else return super.wasCanceled();
	}

	@Override
	public boolean wasOKed() {
		if (mcp_mode) {
			return true;
		}
		else return super.wasOKed();
	}
}
