/*
 * @(#)Command.java
 *
 * This file is part of webCDwriter - Network CD Writing.
 *
 * Copyright (C) 1999-2002 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.*;

public class Command implements Runnable {
	final Object cancelButton[] = { i18n("cancel") };
	final Object okButton[] = { "OK" };
	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;

			dialog.setModal(true);
			Version.showDebugInfo(21, "show start");
			dialog.show();
			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) {
				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);
	}

	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 void replaceDialog(javax.swing.JDialog dialog)
	{
		hideDialog();
		showDialog(dialog);
	}

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

	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;
	}

	void processHint(String str)
	{
	}

	void processInfo(String str)
	{
		if (infoStr.length() > 0)
			infoStr = infoStr.concat("\n");
		infoStr = infoStr.concat(str);
	}

	void processLog(String str)
	{
	}

	void readResult()
		throws java.io.IOException
	{
		while (true)
		{
			String str = CDcreator.inStream.readLine();
			if (str == null)
				break;
			Version.showDebugInfo(5, str);

			String typeStr, tailStr;
			int i = str.indexOf(' ');
			if (i >= 0)
			{
				typeStr = str.substring(0, i);
				tailStr = str.substring(
					i+1, str.length());
			}
			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"))
				processHint(tailStr);
			else
			if (typeStr.equals("INFO"))
				processInfo(tailStr);
			else
			if (typeStr.equals("CDRECORD")
				|| typeStr.equals("CDWverify")
				|| typeStr.equals("MKISOFS"))
			{
				Log.put(tailStr);
				processLog(tailStr);
			}
		}
	}

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

		int i, j;
		for (i = 0, j = 0; i < str.length(); i++, j++)
		{
			if (str.charAt(i) == '\\')
			{
				i++;
				if (str.charAt(i) == '_')
				{
					str2[j] = ' ';
					continue;
				}
			}
			str2[j] = str.charAt(i);
		}
		return new String(str2, 0, j);
	}

	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');
	}
}
