/*
 * @(#)Command.java
 *
 * This file is part of webCDwriter - Network CD Writing.
 *
 * Copyright (C) 1999-2004 Jörg P. M. Haeger
 *
 * webCDwriter is free software. See CDcreator.java for details.
 */

import java.io.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;

/**
 * Execution of a command.
 *
 * @version 20040504
 * @author Jörg P. M. Haeger
 */
public class Command implements Runnable {
	final String cancelButton = i18n("cancel");
	final String[] cancelOption = { cancelButton };
	final Object okButton[] = { "OK" };
	final String okButton2 = i18n("OK");
	final String[] okOption = { okButton2 };
	public static final int INIT = 0;
	public static final int RUNNING = 1;
	public static final int ERROR = 2;
	public static final int SUCCESS = 3;
	public int status = INIT;
	private volatile javax.swing.JDialog dialog;
	private volatile boolean dialogOpen;
	protected volatile boolean cancel = false, infoWin = false;
	private volatile boolean quit = false;
	String infoStr;
	String lastResult = "unknown";

	public Command() {
		infoStr = new String();
	}

	void cancelCommand() {
	}

	final public void exec() throws CommandError {
		Thread thread = new Thread(this);
		thread.start();

		while (true) {
			synchronized(this) {
				while (dialog == null && !quit)
				try {
					wait();
				}
				catch (InterruptedException e) {
				}
			}
			if (quit)
				break;

			while (true) {
				dialog.setModal(true);
				Version.showDebugInfo(21, "show start");
				dialog.show();
				synchronized(this) {
					JDialog d = nextDialog();
					if (!dialogOpen || d == null)
						break;
					dialog = d;
				}
			}
			synchronized(this) {
				Version.showDebugInfo(21, "show end");
				if (dialogOpen) {
					Version.showDebugInfo(21,
						"closed by user");
					dialogOpen = false;
					if (infoWin)
						dialog = null;
					else {
						if (!cancel) {
							cancel = true;
							cancelCommand();
						}
						dialog = getCancelDialog();
					}
					notifyAll();
				}
			}
		}

		try {
			thread.join();
		}
		catch (InterruptedException e) {
		}
		Version.showDebugInfo(21, "exec end");

		showResult();

		if (status == ERROR)
			throw new CommandError();
		if (cancel)
			throw new CommandError();
	}

	protected void execCommand(String cmdStr) {
		try {
			status = INIT;
			writeCommand(cmdStr);
			status = RUNNING;
			readResult();
		}
		catch (java.io.IOException e) {
			System.out.println("execCommand: IOException");
			lastResult = "error " + e.getLocalizedMessage();
		}
	}

	private JDialog getCancelDialog() {
		JDialog dialog = new JOptionPane(i18n("waitForCancel"),
			JOptionPane.INFORMATION_MESSAGE)
			.createDialog(CDcreator.mainWin,
				i18n("titleWaitForCancel"));
		dialog.addWindowListener(
			new WindowAdapter() {
				public void windowOpened(WindowEvent e) {
					dialogOpen = true;
					Version.showDebugInfo(21,
						"dialogOpened");
					synchronized(Command.this) {
						Command.this.notifyAll();
					}
				}
			}
		);
		return dialog;
	}

	protected void hideDialog() {
		Version.showDebugInfo(21, "hide start");
		if (dialog == null)
			return;

		synchronized(this) {
			while (cancel && !dialogOpen)
				try {
					wait();
				}
				catch (InterruptedException e) {
				}
		}

		synchronized(this) {
			if (dialogOpen) {
/*				if (SwingUtilities.isEventDispatchThread())
					dialog.dispose();
				else
*/				try {
					SwingUtilities.invokeAndWait(new Runnable() {
						public void run() {
							Version.showDebugInfo(21, "run gui");
//							dialog.setVisible(false);
							dialog.dispose();
						}
					});
				}
				catch (Exception e) {
				}
				dialog = null;
				dialogOpen = false;
			}
		}
		Version.showDebugInfo(21, "hide end");
	}

	String i18n(String key) {
		return CDcreator.i18n(key);
	}

	boolean lastResultOK() {
		return status == SUCCESS;
	}

	/**
	 * Get the next dialog of a sequence of user dialogs.
	 */
	protected JDialog nextDialog() {
		return null;
	}

	protected Integer parse(String str, String key) {
		try {
			StreamTokenizer tokenizer
				= new StreamTokenizer(
					new StringReader(str));

			if (tokenizer.nextToken()
				!= StreamTokenizer.TT_WORD)
				return null;
			if (!tokenizer.sval.equals(key))
				return null;

			if (tokenizer.nextToken()
				!= StreamTokenizer.TT_NUMBER)
				return null;

			return new Integer((int)tokenizer.nval);
		}
		catch (IOException e) {
			return null;
		}
	}

	protected Integer parse(String str, String key, int arg) {
		StringTokenizer t = new StringTokenizer(str, " \t");
		try {
			String str2 = t.nextToken();
			if (!str2.equals(key))
				return null;

			for (int i = 0; i < arg; i++)
				str2 = t.nextToken();
			return new Integer(str2);
		}
		catch (Exception e) {
			return null;
		}
	}

	int parsePairStrInt(String lineStr) throws IOException {
		StreamTokenizer tokenizer
			= new StreamTokenizer(
				new StringReader(lineStr));

		tokenizer.nextToken();
		if (tokenizer.nextToken() != StreamTokenizer.TT_NUMBER)
			return 0;
		return (int)tokenizer.nval;
	}

	protected void process(int code, String text) {
	}

	void processHint(String str) {
	}

	void processInfo(String str) {
		if (infoStr.length() > 0)
			infoStr += "\n";
		infoStr = CDcreator.replaceAll(infoStr, "CD-R(W)", Mode.medium);
		infoStr += str;
	}

	protected boolean processInternal(int code, String text) {
		return false;
	}

	void processLog(String str) {
	}

	void readResult() throws java.io.IOException {
		synchronized(CDcreator.inStream) {
			readResult2();
		}
	}

	private void readResult2() throws java.io.IOException {
		while (true) {
			StringBuffer strBuf = new StringBuffer();
			while (true) {
				int b = CDcreator.inStream.read();
				if (b < 0)
					break;
				if (b == 10)
					break;
				if (b < 32 && b != '\t')
					strBuf.append("(" + String.valueOf(b) + ")");
				else
					strBuf.append((char) b);
			}
			if (strBuf.length() == 0)
				break;
			String str = new String(strBuf);
			Version.debug("inStream", str);

			String typeStr, tailStr;
			int i = str.indexOf(' ');
			if (i >= 0) {
				typeStr = str.substring(0, i);
				tailStr = str.substring(i + 1);
			}
			else {
				typeStr = str;
				tailStr = "";
			}
			typeStr.toUpperCase();
			if (typeStr.equals("ERR")) {
				status = ERROR;
				lastResult = i18n("error_"
					+ tailStr.replace(' ', '_'));
				if (lastResult.startsWith("error_"))
					lastResult = "\"" + tailStr + "\"";
				break;
			}
			else
			if (typeStr.equals("OK")) {
				status = SUCCESS;
				lastResult = i18n("success_"
					+ tailStr.replace(' ', '_'));
				break;
			}
			else
			if (typeStr.equals("HINT")) {
				final String tailStr0 = tailStr;
				if (true || SwingUtilities.isEventDispatchThread())
					processHint(tailStr0);
				else
				try {
					SwingUtilities.invokeAndWait(new Runnable() {
						public void run() {
							processHint(tailStr0);
						}
					});
				}
				catch (Exception e) {
				}
			}
			else
			if (typeStr.equals("INFO"))
				processInfo(tailStr);
			else
			if (typeStr.equals("CDRECORD")
				|| typeStr.equals("CDWverify")
				|| typeStr.equals("MKISOFS")) {
				Log.put(tailStr);
				processLog(tailStr);
			}
			else {
				try {
					int n = Integer.parseInt(typeStr);
					if (!processInternal(n, tailStr))
						SwingUtilities.invokeLater(new Worker(n, tailStr));
				}
				catch (Exception e) {
				}
			}
		}
	}

	public static String removeEscChars(String str) {
		char str2[] = new char[str.length()];

		int i, j;
		for (i = 0, j = 0; i < str.length(); j++) {
			if (str.charAt(i) == '%') {
				i++;
				int digits = 2;
				if (str.charAt(i) == 'u') {
					digits = 4;
					i++;
				}
				try {
					str2[j] = (char) Integer.parseInt(
						str.substring(i, i + digits), 16);
				}
				catch (NumberFormatException e) {
					str2[j] = '_';
				}
				i += digits;
			}
			else
				str2[j] = str.charAt(i++);
		}

		return new String(str2, 0, j);
	}

	protected void replaceDialog(javax.swing.JDialog dialog) {
		hideDialog();
		showDialog(dialog);
	}

	public void run() {
		synchronized(this) {
			quit = true;
			notifyAll();
		}
	}

	protected void showDialog(javax.swing.JDialog newDialog) {
		if (newDialog == null)
			return;

		synchronized(this) {
			while (dialogOpen)
				try {
					wait();
				}
				catch (InterruptedException e) {
				}
		}

		dialog = newDialog;
		dialog.addWindowListener(
			new WindowAdapter() {
/*				public void windowClosing(WindowEvent e) {
					e.getWindow().setVisible(false);
				}
*/				public void windowOpened(WindowEvent e) {
					dialogOpen = true;
					Version.showDebugInfo(21,
						"dialogOpened");
					synchronized(Command.this) {
						Command.this.notifyAll();
					}
				}
			}
		);

		synchronized(this) {
			notifyAll();
			while (!dialogOpen)
				try {
					wait();
				}
				catch (InterruptedException e) {
				}
		}
	}

	protected void showInfoWin(javax.swing.JDialog newDialog) {
		synchronized(this) {
			while (dialogOpen)
				try {
					wait();
				}
				catch (InterruptedException e) {
				}
		}

		infoWin = true;
		showDialog(newDialog);
	}

	void showResult() {
	}

	protected void sleep(int msec) {
		try {
			Thread.currentThread().sleep(msec);
		} catch (InterruptedException e) {
		}
	}

	/**
	 * replace 0x00 to 0x20 by %00 to %20
	 */
	public static String spaceToEsc(String str) {
		char str2[] = new char[6 * str.length()];

		int i, j;
		for (i = 0, j = 0; i < str.length(); i++, j++) {
			char code = str.charAt(i);
			if (code > 0x20 && code < 0x7F && code != '%')
				str2[j] = code;
			else
			if (code < 0x100) {
				str2[j++] = '%';
				String codeStr = Integer.toHexString(0x100 + code);
				str2[j++] = codeStr.charAt(1);
				str2[j] = codeStr.charAt(2);
			}
			else {
				str2[j++] = '%';
				str2[j++] = 'u';
				String codeStr = Integer.toHexString(
					0x10000 + (code & 0xFFFF));
				str2[j++] = codeStr.charAt(1);
				str2[j++] = codeStr.charAt(2);
				str2[j++] = codeStr.charAt(3);
				str2[j] = codeStr.charAt(4);
			}
		}

		return new String(str2, 0, j);
	}

	protected void writeCommand(String cmdStr)
		throws java.io.IOException {
		cmdStr.replace('\n', ' ');
		CDcreator.outStream.writeBytes(cmdStr + '\n');
	}

	protected void writeCommandIgnoreException(String cmdStr) {
		try {
			writeCommand(cmdStr);
		}
		catch (java.io.IOException e) {
		}
	}

	class Worker extends Thread {
		private int code;
		private String text;

		Worker(int code, String text) {
			this.code = code;
			this.text = text;
		}

		public void run() {
			process(code, text);
		}
	}
}
