/*
 * @(#)Burn.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.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowAdapter;
import java.io.*;
import java.text.*;
import javax.swing.*;

/**
 * Abstract parent of the burn command classes.
 *
 * @version 20040511
 * @author Jörg P. M. Haeger
 */
abstract public class Burn extends Command {
	final static String
	hintBlankingTOC = "blanking TOC",
	hintCDdetected = "CD detected",
	hintCDrecordStarted = "cdrecord started",
	hintCDwasClosed = "CD was closed",
	hintDVDFormatStarted = "dvd-format started",
	hintFixating = "fixating",
	hintGrowisofsStarted = "growisofs started",
	hintImageReady = "image ready",
	hintMakingImage = "making image",
	hintPercentDone = "percentDone",
	hintVerifying = "verifying",
	hintWriterReserved = "writer reserved"
	;

	protected JDialog activeDialog;
	protected JOptionPane optionPane = null, subPane = null;
	BurnDialog burnDialog;
	JPanel writersPanel;
	int totalKB, oldFreeKB, newFreeKB;
	boolean CDwasClosed = false;
	final String backButton = i18n("MainWin.back");
	final String offlineButton = i18n("burnOffline");
	Object[] options = { i18n("details"), okButton2 };
	String burnSessionResult;
	boolean burnSessionResultOK;

	Burn() {
		burnDialog = new BurnDialog();
		writersPanel = new JPanel();
		writersPanel.setLayout(
			new BoxLayout(writersPanel, BoxLayout.Y_AXIS));
		writersPanel.setAlignmentX(Box.LEFT_ALIGNMENT);
	}

	void cancelCommand() {
		try {
			writeCommand("noop");
		}
		catch (java.io.IOException e) {
			System.out.println("cancelCommand: IOException");
		}
	}

	private JDialog createErrorDialog() {
		JPanel vbox = new JPanel();
		vbox.setLayout(new BoxLayout(vbox, BoxLayout.Y_AXIS));
		vbox.add(new JLabel(burnSessionResult));
		if (burnSessionResult.indexOf(
				"power calibration area error") >= 0) {
			JComponent viewer = CDcreator.getHTML("errors/037303.html");
			if (viewer != null && !CDcreator.isNetscapeVM()) {
				vbox.add(Box.createVerticalStrut(10));
				JScrollPane scroller = new JScrollPane();
				scroller.setPreferredSize(new Dimension(440, 200));
				scroller.setAlignmentX(Box.LEFT_ALIGNMENT);
				scroller.getViewport().add(viewer);
				vbox.add(scroller);
			}
		}
		optionPane = new JOptionPane(
			vbox,
			JOptionPane.ERROR_MESSAGE,
			JOptionPane.DEFAULT_OPTION,
			null, options);
		return optionPane.createDialog(
			CDcreator.mainWin, i18n("error"));
	}

	protected JDialog createSuccessDialog() {
		MessageFormat formatter = new MessageFormat("");
		formatter.setLocale(CDcreator.locale);
		if (CDwasClosed)
			formatter.applyPattern(
				i18n("burnSuccessCDwasClosed"));
		else
			formatter.applyPattern(i18n("burnSuccess"));

		int writtenKB = oldFreeKB - newFreeKB;
		if (totalKB < 1024 * 1024) {
			// is a CD-R(W)
			writtenKB -= 2 * (4500 + 2250);
			if (oldFreeKB == totalKB)
				// lead out of the first session
				writtenKB -= 2 * 4500;
			if (writtenKB < 0)
				writtenKB = 0;
		}

		Object[] args = {
			new Integer(writtenKB / 1024),
			new Integer(newFreeKB / 1024),
			new Integer(totalKB / 1024) };

		optionPane = new JOptionPane(
			formatter.format(args),
			JOptionPane.INFORMATION_MESSAGE,
			JOptionPane.DEFAULT_OPTION,
			null, options);
		return optionPane.createDialog(
			CDcreator.mainWin, i18n("titleBurnResult"));
	}

	abstract protected void execBurnCommand();

	protected void execOptions() {
	}

	protected void execSetProjectInfo() {
		if (!CDcreator.askForProjectInfo)
			return;

		String from = Options.projectInfo.from;
		if (from.length() == 0)
			from = "-";
		String to = Options.projectInfo.to;
		if (to.length() == 0)
			to = "-";
		String label = Options.projectInfo.label;
		if (label.length() == 0)
			label = "-";
		String contents = Options.projectInfo.contents;
		if (contents.length() == 0)
			contents = "-";
		execCommand("setProjectInfo "
			+ spaceToEsc(from) + " " + spaceToEsc(to)
			+ " " + spaceToEsc(label) + " " + spaceToEsc(contents));
	}

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

		if (subPane != null)
			if (subPane.getValue() == okButton2) {
				try {
					writeCommand("setBurnOffline");
				}
				catch (Exception e) {
				}
				CDcreator.closeMainWin();
				subPane = null;
				return null;
			}
			else {
				subPane = null;
				return activeDialog;
			}

		if (optionPane.getValue() == offlineButton) {
			String[] options = { backButton, okButton2 };
			subPane = new JOptionPane(
				i18n("willNowExit"),
				JOptionPane.INFORMATION_MESSAGE,
				JOptionPane.DEFAULT_OPTION,
				null, options);
			return subPane.createDialog(
				CDcreator.mainWin, i18n("burnOffline"));
		}

		return null;
	}

	void parseCapacityHint(String lineStr) throws IOException {
		StreamTokenizer tokenizer
			= new StreamTokenizer(
				new StringReader(lineStr));

		tokenizer.nextToken();
		if (!tokenizer.sval.equals("capacity"))
			return;

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

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

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

	void parseCDrecordLine(String lineStr) throws IOException {
		StreamTokenizer tokenizer
			= new StreamTokenizer(
				new StringReader(lineStr));
		tokenizer.wordChars(':', ':');

		tokenizer.nextToken();
		if (!tokenizer.sval.equals("Track"))
			return;

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

		if (tokenizer.nextToken() != StreamTokenizer.TT_WORD)
			return;
		if (!tokenizer.sval.equals(":"))
			return;

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

		if (tokenizer.nextToken() != StreamTokenizer.TT_WORD)
			return;
		if (!tokenizer.sval.equals("of"))
			return;

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

		burnDialog.setRange(0, N);
		burnDialog.setProgress(n);

		if (oldFreeKB == 0)
			oldFreeKB = 1024 * N;
	}

	void parsePercentDoneHint(String lineStr) throws IOException {
		StreamTokenizer tokenizer
			= new StreamTokenizer(
				new StringReader(lineStr));

		tokenizer.nextToken();
		if (!tokenizer.sval.equals(hintPercentDone))
			return;

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

	void parseRankHint(String lineStr) throws IOException {
		StreamTokenizer tokenizer
			= new StreamTokenizer(
				new StringReader(lineStr));

		tokenizer.nextToken();
		if (!tokenizer.sval.equals("rank"))
			return;

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

		JPanel vp = new JPanel();
		vp.setLayout(new BoxLayout(vp, BoxLayout.Y_AXIS));

		JPanel hp = new JPanel();
		hp.setBorder(BorderFactory.createEmptyBorder(5, 0, 5, 0));
		hp.setLayout(new BoxLayout(hp, BoxLayout.X_AXIS));
		hp.add(new JLabel(
			CDcreator.i18n("queue") + " " + rank));
		hp.add(Box.createHorizontalStrut(10));

		JButton button = new JButton(CDcreator.i18n("showStatus"));
		button.addActionListener(new java.awt.event.ActionListener() {
			public void actionPerformed(java.awt.event.ActionEvent e) {
				CDcreator.showServerDocument("/status.html");
			}
		});
		hp.add(button);
		hp.setAlignmentX(Box.LEFT_ALIGNMENT);
		vp.add(hp);

		QueueView qv = new QueueView(rank);
		qv.setAlignmentX(Box.LEFT_ALIGNMENT);
		vp.add(qv);
		vp.add(writersPanel);

		optionPane.setMessage(vp);
		activeDialog.pack();
	}

	protected void process(int code, String text) {
//		System.out.println("code = " + code + ", text = " + text);

		if (text.indexOf("eject") >= 0)
			Log.put(text);

		int writer = -1;
		String no = "";
		if (text.startsWith("W")) {
			int p = text.indexOf(" ");
			if (p >= 2) {
				writer = Integer.parseInt(text.substring(1, p));
				no = text.substring(0, p + 1);
				text = text.substring(p + 1);
			}
		}

		switch (code) {
			case 199:
				Version.debug("state", text);
				String a = text.substring(0, 2);
				java.awt.Component[] cs = writersPanel.getComponents();
				int i = 0;
				for (; i < cs.length; i++) {
					JLabel l = (JLabel) cs[i];
					String b = l.getText().substring(0, 2);
					if (b.equals(a)) {
						l.setText(text);
						break;
					}
				}
				if (i == cs.length)
					writersPanel.add(new JLabel(text));
				activeDialog.pack();
				break;
			case 901:
				if (text.indexOf("eject") >= 0)
					break;
				String str = i18n("MainWin.unexpectedMedium");
				str = CDcreator.replaceAll(str, "@MEDIUM@", text);
				if (JOptionPane.showConfirmDialog(activeDialog, str,
						i18n("warning"), JOptionPane.YES_NO_OPTION) == 0)
					writeCommandIgnoreException(no + "setAcceptMedium");
				break;
			case 902:
				str = i18n("MainWin.unexpectedEmptyCD");
				if (JOptionPane.showConfirmDialog(activeDialog, str,
						i18n("warning"), JOptionPane.YES_NO_OPTION) == 0)
					writeCommandIgnoreException(no + "setAcceptEmptyCD");
				break;
			case 903:
				str = i18n("MainWin.unexpectedUserID");
				str = CDcreator.replaceAll(str, "@USER-ID@", text);
				if (JOptionPane.showConfirmDialog(activeDialog, str,
						i18n("warning"), JOptionPane.YES_NO_OPTION) == 0)
					writeCommandIgnoreException(
						no + "setAcceptKey2 " + text);
				break;
			case 904:
				str = i18n("MainWin.unexpectedWithoutUserID");
				if (JOptionPane.showConfirmDialog(activeDialog, str,
						i18n("warning"), JOptionPane.YES_NO_OPTION) == 0)
					writeCommandIgnoreException(
						no + "setAcceptWithoutUserID");
				break;
			default:
				;
		}
	}

	void processHint(String str) {
//		System.out.println("processHint " + str);
		try {
			if (str.startsWith("rank"))
				parseRankHint(str);
			else
			if (str.startsWith("Track"))
				parseCDrecordLine(str);
			else
			if (str.startsWith("capacity"))
				parseCapacityHint(str);
			else
			if (str.startsWith(hintPercentDone))
				parsePercentDoneHint(str);
		}
		catch (IOException e) {
		}

		if (str.equals(hintWriterReserved)) {
			hideDialog();
			JPanel vbox = new JPanel();
			vbox.setLayout(new BoxLayout(vbox, BoxLayout.Y_AXIS));
			vbox.add(Box.createVerticalStrut(10));
			int p0 = 0;
			while (true) {
				int p1 = infoStr.indexOf('\n', p0);
				if (p1 < 0)
					break;
				JLabel l = new JLabel(infoStr.substring(p0, p1));
				l.setAlignmentX(JPanel.LEFT_ALIGNMENT);
				vbox.add(l);
				vbox.add(Box.createVerticalStrut(5));
				p0 = p1 + 1;
			}
			vbox.add(Box.createVerticalStrut(5));
			String[] options = { cancelButton, offlineButton };
			optionPane = new JOptionPane(
				vbox,
				JOptionPane.INFORMATION_MESSAGE,
				JOptionPane.DEFAULT_OPTION,
				null, options);
			activeDialog = optionPane.createDialog(
				CDcreator.mainWin, i18n("titleInsertCD"));
			showDialog(activeDialog);
			infoStr = "";
		}
		else
		if (str.equals(hintCDdetected)) {
			Log.put("Medium detected");
			burnDialog = new BurnDialog();
			burnDialog.setTitle(i18n("titleBurnSessionControl"));
			burnDialog.setRange(0, 100);
			replaceDialog(burnDialog);
		}
		else
		if (str.equals(hintBlankingTOC)) {
			Log.put("");
			Log.put("cdrecord blank=fast started ==>");
			burnDialog.setMessage(i18n("blankingTOC"));
		}
		else
		if (str.equals(hintDVDFormatStarted)) {
			Log.put("");
			Log.put("dvd+rw-format started ==>");
			burnDialog.setMessage(i18n("BurnDialog.formatting"));
		}
		else
		if (str.equals(hintMakingImage)) {
			Log.put("");
			Log.put("mkisofs started ==>");
			burnDialog.setTitle(i18n("titleCreateImage"));
			burnDialog.setMessage(i18n("createImage"));
		}
		else
		if (str.equals(hintCDrecordStarted)) {
			Log.put("");
			Log.put("cdrecord started ==>");
//			hideDialog();
			burnDialog.setTitle(i18n("titleBurn"));
			burnDialog.setMessage(i18n("burn"));
			burnDialog.setProgress(0);
//			showDialog(burnDialog);
		}
		else
		if (str.equals(hintGrowisofsStarted)) {
			Log.put("");
			Log.put("growisofs started ==>");
			burnDialog.setTitle(i18n("titleBurn"));
			burnDialog.setMessage(i18n("burn"));
			burnDialog.setProgress(0);
		}
		else
		if (str.equals(hintFixating)) {
			burnDialog.setMessage(i18n("fixating"));
			burnDialog.setProgress(0);
//			burnDialog.setIndeterminate(true);
			burnDialog.setRange(0, 100);
		}
		else
		if (str.equals(hintCDwasClosed)) {
			CDwasClosed = true;
		}
		else
		if (str.equals(hintVerifying)) {
			Log.put("");
			Log.put("CDWverify started ==>");
			burnDialog.setTitle(i18n("titleVerifying"));
			burnDialog.setMessage(i18n("verifying"));
			burnDialog.setProgress(0);
			burnDialog.setRange(0, 100);
			JButton button = new JButton(CDcreator.i18n("cancel"));
			button.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					writeCommandIgnoreException("cancel");
					Version.debug("cancel", "button clicked");
				}
			});
			burnDialog.addButton(button);
			burnDialog.addWindowListener(new WindowAdapter() {
				public void windowClosing(WindowEvent e) {
					writeCommandIgnoreException("cancel");
					Version.debug("cancel", "window closed");
				}
			});
		}
		else
		if (str.startsWith("Medium ")
				|| str.indexOf("-> eject") >= 0) {
			Log.put(str);
			final Object panel = optionPane.getMessage();
			if (panel instanceof JPanel
					&& Log.getView().getParent() != panel)
				try {
					SwingUtilities.invokeAndWait(new Runnable() {
						public void run() {
//							Log.getView().setPreferredSize(
//								new Dimension(160, 100));
							Log.getView().setAlignmentX(
								JPanel.LEFT_ALIGNMENT);
							((JPanel)panel).add(Log.getView());
//							activeDialog.pack();
							Dimension d = activeDialog.getSize();
							d.height += 100;
							activeDialog.setSize(d);
							CDcreator.view.center(activeDialog);
						}
					});
				}
				catch (Exception e) {
				}
		}
	}

	void processInfo(String str) {
		super.processInfo(str);
		if (optionPane != null)
			optionPane.setMessage(infoStr);
	}

	public void run() {
		if (activeDialog != null)
			System.out.println("activeDialog != null");
		String[] options = { cancelButton, offlineButton };
		optionPane = new JOptionPane(
			i18n("reserve"),
			JOptionPane.INFORMATION_MESSAGE,
			JOptionPane.DEFAULT_OPTION,
			MainWin.getIcon("burn.png"), options, options[1]);
		activeDialog = optionPane.createDialog(
			CDcreator.mainWin, i18n("titleReserve"));
		showDialog(activeDialog);

		execCommand("resetOptions");
		execOptions();

		if (Options.format)
			execCommand("setFormat");

		if (Options.speed > 0)
			execCommand("setSpeed " + Options.speed);

		execBurnCommand();
		burnSessionResult = lastResult;
		burnSessionResultOK = lastResultOK();

		hideDialog();

		try {
			if (cancel)
				readResult();
		}
		catch (java.io.IOException e) {
			System.out.println("BurnSession: IOException");
		}

		super.run();
	}

	void showResult() {
		if (cancel)
			return;

		JDialog dialog;
		if (burnSessionResultOK)
			dialog = createSuccessDialog();
		else
			dialog = createErrorDialog();

		while (true) {
			optionPane.setInitialValue(options[1]);
			optionPane.setValue(
				JOptionPane.UNINITIALIZED_VALUE);
			dialog.show();
			if (optionPane.getValue() == options[0])
				new LogWin().show();
			else
				break;
		}
	}
}
