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

import java.io.*;
import java.net.*;
import javax.swing.*;

/**
 * Execution of "putFile" commands to copy a directory tree
 * to the server.
 *
 * @version 20030221
 * @author Jörg P. M. Haeger
 */
public class PutFiles extends Command {
	PutDialog view;
	protected DataOutputStream outStream;
	DirTreeNode CDRootNode;
	int sessionSizeKB;
	int transmittedKB;
	boolean doNotShorten = false;
	boolean ignoreUnreadable;
	boolean ignoreAllUnreadable;
	boolean shorten;
	boolean shortenAll;

	public PutFiles(DirTreeNode CDRootNode, int sessionSizeKB) {
		view = new PutDialog();
		outStream = CDcreator.outStream;
		this.CDRootNode = CDRootNode;
		this.sessionSizeKB = sessionSizeKB;
		transmittedKB = 0;
	}

	private void copy(FileInputStream inFile, long bytesToWrite)
			throws java.io.IOException {
		try {
			int countKB = transmittedKB;
			while (bytesToWrite > 0) {
				byte bs[] = new byte[64 * 1024];
				int len = inFile.read(bs);
				if (len == -1)
					break;
				if (len > bytesToWrite)
					len = (int)bytesToWrite;
				outStream.write(bs, 0, len);
				bytesToWrite -= len;
				countKB += (len + 1023) / 1024;
				view.setProgress(countKB);
			}
		}
		finally {
			while (bytesToWrite-- > 0)
				outStream.write(0);
		}
	}

	protected void editFileName(DirTreeNode node, int index)
		throws Exception {
		to64Characters(node, index);
	}

	FileInputStream open(File file) throws Exception {
		try {
			FileInputStream inFile = new FileInputStream(file);
			return inFile;
		}
		catch (java.io.IOException e) {
		}

		if (ignoreAllUnreadable)
			return null;

//		System.out.println("cannot read " + file);
		final String name = file.getPath();
		try {
			ignoreUnreadable = false;
			SwingUtilities.invokeAndWait(new Runnable() {
				public void run() {
					String[] options = {
						i18n("PutFiles.ignore"),
						i18n("PutFiles.ignoreAll"),
						i18n("cancel") };
					JPanel panel = new JPanel();
					panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
					JLabel l = new JLabel(i18n("PutFiles.cannotRead"));
					l.setAlignmentX(JLabel.LEFT_ALIGNMENT);
					panel.add(l);
					JTextArea t = new JTextArea(name);
					t.setAlignmentX(JTextArea.LEFT_ALIGNMENT);
					t.setEditable(false);
					panel.add(t);
					int result = JOptionPane.showOptionDialog(
						CDcreator.mainWin,
						panel,
						i18n("error"),
						JOptionPane.DEFAULT_OPTION,
						JOptionPane.ERROR_MESSAGE,
						null, options, options[0]);
					if (result == 0)
						ignoreUnreadable = true;
					else
					if (result == 1)
						ignoreAllUnreadable = true;
				}
			});
		}
		catch (Exception e2) {
		}

		if (ignoreUnreadable || ignoreAllUnreadable)
			return null;

		throw new Exception("open cancelled");
	}

	protected void putFile(String dirName, DirTreeNode node)
		throws java.io.IOException, CommandError, Exception {
		File file = node.getFile();
		String fileName = dirName + "/" + node.toString();
		Version.showDebugInfo(1, fileName + " " + file);
		view.setMessage(fileName);

		if (file instanceof ServerFile)
			outStream.writeBytes("linkFile "
				+ spaceToEsc(file.getPath()) + " "
				+ spaceToEsc(fileName) + "\n");
		else {
			long bytesToWrite = file.length();
			FileInputStream inFile = null;
			if (bytesToWrite > 0) {
				inFile = open(file);
				if (inFile == null)
					return;
			}

			int mode = 0444;
			if (node.isExecutable())
				mode |= 0111;

			outStream.writeBytes("putFile "
				+ spaceToEsc(fileName) + " "
				+ bytesToWrite + " "
				+ Integer.toOctalString(mode) + " "
				+ file.lastModified() / 1000
				+ "\n");

			copy(inFile, bytesToWrite);
			if (inFile != null)
				inFile.close();
		}

		readResult();
		if (!lastResultOK())
			throw new CommandError();

		if (file.length() > 0)
			transmittedKB += (file.length() - 1) / 1024 + 1;
		view.setProgress(transmittedKB);
		State.numOfFiles++;

		if (node == MainWin.bootImageNode)
			Options.bootImage = fileName;
	}

	void putTree(String dirName, DirTreeNode node)
		throws java.io.IOException, CommandError, Exception {
		String dirName2;
		if (dirName.equals("/"))
			dirName2 = "";
		else
			dirName2 = dirName + '/' + node.toString();

		File dir = node.getFile();
		if (dir instanceof ServerFile) {
			// just make a link to the file on the server
			outStream.writeBytes("linkFile "
				+ spaceToEsc(dir.getPath()) + " "
				+ spaceToEsc(dirName2) + "\n");
			readResult();
			if (!lastResultOK())
				throw new CommandError();
			return;
		}

		// transfer the files and directories contained
		// in this directory
		for (int i = 0; i < node.getChildCount(); i++) {
			if (cancel)
				return;
			editFileName(node, i);
			DirTreeNode child =
				(DirTreeNode)node.getChildAt(i);
			if (child.isLeaf())
				putFile(dirName2, child);
			else
				putTree(dirName2, child);
		}

		if (dir != null && dirName2.length() != 0)
			// create the directory and set the modification time
			execCommand("mkdir "
				+ spaceToEsc(dirName2) + " "
				+ "555 "
				+ dir.lastModified() / 1000);
	}

	public void run() {
//		System.out.println("PutFiles");
		view.setTitle(i18n("titlePutFiles"));
		view.setRange(0, sessionSizeKB);
		showDialog(view);

		// for netscape
		if (CDcreator.isNetscapeVM())
		try {
			netscape.security.PrivilegeManager
				.enablePrivilege("UniversalFileRead");
		}
		catch (Exception e) {
		}

		boolean error = false;
		doNotShorten = false;
		ignoreAllUnreadable = false;
		shortenAll = false;
		try {
			putTree("/", CDRootNode);
		}
		catch (java.io.IOException e) {
			status = ERROR;
			System.out.println("PutFiles: " + e);
		}
		catch (CommandError cmdErr) {
			status = ERROR;
			error = true;
		}
		catch (Exception e) {
			status = ERROR;
		}

		hideDialog();
		if (error)
			showInfoWin(new JOptionPane(
				lastResult,
				JOptionPane.INFORMATION_MESSAGE,
				JOptionPane.DEFAULT_OPTION,
				null, okButton).createDialog(
				CDcreator.mainWin, i18n("error")));

		super.run();
//		System.out.println("PutFiles end");
	}

	private void to64Characters(DirTreeNode node, int index)
			throws Exception {
		final int max = 64;
		final String name = node.getChildAt(index).toString();
		int len = name.length();
		int max2;
		try {
			max2 = max - (name.getBytes("UTF-8").length - len);
		}
		catch (Exception e) {
			max2 = max;
		}
		if (len <= max2)
			return;

		if (!doNotShorten && !shortenAll)
		try {
			shorten = false;
			SwingUtilities.invokeAndWait(new Runnable() {
				public void run() {
					String[] options = {
						i18n("PutFiles.shorten"),
						i18n("PutFiles.shortenAll"),
						i18n("PutFiles.ignore"),
						i18n("PutFiles.ignoreAlways"),
						i18n("cancel") };
					JPanel panel = new JPanel();
					panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
					JLabel l = new JLabel(i18n("PutFiles.above64characters"));
					l.setAlignmentX(JLabel.LEFT_ALIGNMENT);
					panel.add(l);
					JTextArea t = new JTextArea(name);
					t.setAlignmentX(JTextArea.LEFT_ALIGNMENT);
					t.setEditable(false);
					panel.add(t);
					t.setAlignmentX(JLabel.LEFT_ALIGNMENT);
					l = new JLabel(i18n("PutFiles.problem"));
					panel.add(l);
					int result = JOptionPane.showOptionDialog(
						CDcreator.mainWin,
						panel,
						i18n("warning"),
						JOptionPane.DEFAULT_OPTION,
						JOptionPane.WARNING_MESSAGE,
						null, options, options[0]);
					if (result == 0)
						shorten = true;
					else
					if (result == 1)
						shortenAll = true;
					else
					if (result == 3)
						doNotShorten = true;
					else
					if (result == 4)
						shorten = doNotShorten = true;
				}
			});
		}
		catch (Exception e) {
		}

		if (shorten && doNotShorten)
			throw new Exception("shorten cancelled");

		if (!shorten && !shortenAll)
			return;

		int dot = name.lastIndexOf('.');
		if (dot < len - 5)
			dot = len;
		int suffixLen = len - dot;

		int j0 = 1;
		for (int i = 2; i <= max2; i++) {
			for (int j = j0; j < 10 * j0; j++) {
				String name2 = name.substring(0, max2 - i - suffixLen)
					+ "~" + j
					+ name.substring(len - suffixLen);
				int k;
				for (k = 0; k < node.getChildCount(); k++)
					if (node.getChildAt(k).toString().equals(name2))
						break;
				if (k == node.getChildCount()) {
					DirTreeNode child
						= (DirTreeNode)node.getChildAt(index);
					child.setUserObject(name2);
//					System.out.println(name2);
					return;
				}
			}
			j0 *= 10;
		}
	}
}
