/*
 * @(#)DirTreeNode.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 javax.swing.tree.*;

/**
 * DirTreeNode
 *
 * @version 20040317
 * @author Jörg P. M. Haeger
 */
class DirTreeNode implements MutableTreeNode {
	private TreeNode parent;
	private String name;
	protected File file;
	protected boolean expanded, isDir, isExe;
	protected TreeNodesList childs;

	DirTreeNode(String aName) {
		parent = null;
		name = aName;
		file = null;
		childs = new TreeNodesList(10);
		expanded = true;
		isDir = true;
		isExe = false;
	}

	DirTreeNode(File aFile) {
		this(aFile, false);
	}

	DirTreeNode(File aFile, boolean root) {
		parent = null;
		name = aFile.getName();
		if (name.length() == 0)
			if (aFile.getPath().length() > 0)
				name = aFile.getPath().substring(0, 1) + ":";
			else
				name = "a";
		file = aFile;
		expanded = false;
		isDir = root;
		if (!isDir)
			isDir = file.isDirectory();
		isExe = false;
	}

	public java.util.Enumeration children() {
		return null;
	}

	public static int compare(TreeNode a, TreeNode b) {
		String aStr = a.toString().toLowerCase();
		String bStr = b.toString().toLowerCase();

		if (a.isLeaf())
			if (b.isLeaf())
				return aStr.compareTo(bStr);
			else
				return 1;
		else
			if (b.isLeaf())
				return -1;
			else
				return aStr.compareTo(bStr);
	}

	protected DirTreeNode createNode(File file) {
		return new DirTreeNode(file);
	}

	private DirTreeNode createNode2(File file) {
		DirTreeNode node = createNode(file);
		node.setParent(this);
		return node;
	}

	public boolean getAllowsChildren() {
		return !isLeaf();
	}

	public TreeNode getChildAt(int childIndex) {
		expand();
		return childs.getNode(childIndex);
	}

	public int getChildCount() {
		expand();
		return childs.getNumOfNodes();
	}

	public File getFile() {
		return file;
	}

	public int getIndex(String name) {
		for (int i = 0; i < getChildCount(); i++)
			if (getChildAt(i).toString().equals(name))
				return i;

		return -1;
	}

	public int getIndex(TreeNode node) {
		for (int i = 0; i < getChildCount(); i++)
			if (getChildAt(i) == node)
				return i;

		return -1;
	}

	public TreeNode getParent() {
		return parent;
	}

	protected void expand() {
		if (expanded)
			return;

		if (file == null) {
			expanded = true;
			return;
		}

//		System.out.println("expand(): " + name);
		File files[] = ((File2)file).listFiles();
		int filesNum;
		if (files == null)
			filesNum = 0;
		else
			filesNum = files.length;

		childs = new TreeNodesList(filesNum + 10);

		expanded = true;

		for (int i = 0; i < filesNum; i++) {
			File f = files[i];
			if (!Mode.audioCD()
				|| f.isDirectory()
				|| f.getName().toLowerCase()
					.endsWith(".wav")
				|| (CDcreator.mp3Support
					&&  f.getName().toLowerCase()
						.endsWith(".mp3"))
				|| (CDcreator.oggSupport
					&&  f.getName().toLowerCase()
						.endsWith(".ogg")))
//				insert2(createNode(f));
				childs.append(createNode2(f));
		}

		childs.sort();
	}

	public void insert(MutableTreeNode child) {
		insert2(child);
		file = null;
	}

	public void insert(MutableTreeNode child, int index) {
		insert2(child, index);
		file = null;
	}

	private void insert2(MutableTreeNode child) {
		expand();
		int i;
		for (i = 0; i < childs.getNumOfNodes()
			&& compare(childs.getNode(i), child) < 0; i++);
		if (Mode.type == Mode.audio)
			i = childs.getNumOfNodes();
		insert2(child, i);
	}

	private void insert2(MutableTreeNode child, int index) {
		expand();
		childs.insert(child, index);
		child.setParent(this);
	}

	public boolean isExecutable() {
		return isExe;
	}

	public boolean isExpanded() {
		return expanded;
	}

	public boolean isLeaf() {
		return !isDir;
	}

	public void remove(int index) {
		if (index < 0) {
			System.out.println("DirTreeNode.remove: index = "
				+ index);
			return;
		}

		((MutableTreeNode)childs.getNode(index)).setParent(null);
		childs.remove(index);
		file = null;
	}

	public void remove(MutableTreeNode node) {
		remove(getIndex(node));
	}

	public void removeFromParent() {
		((MutableTreeNode)getParent()).remove(this);
	}

	public void resetExpanded() {
		expanded = false;
	}

	public void setParent(MutableTreeNode newParent) {
		parent = newParent;
	}

	public void setUserObject(Object object) {
		name = (String)object;
	}

	public String toString() {
		return name;
	}

	protected static class TreeNodesList {
		public TreeNodesList(int initialSize) {
			nodes = new TreeNode[initialSize];
			numOfNodes = 0;
		}

		public void append(TreeNode node) {
			insert(node, numOfNodes);
		}

		private int compare(int a, int b) {
			return DirTreeNode.compare(nodes[a], nodes[b]);
		}

		public TreeNode getNode(int index) {
			return nodes[index];
		}

		public int getNumOfNodes() {
			return numOfNodes;
		}

		public void insert(TreeNode node, int index) {
			if (index > numOfNodes)
				return;

			if (numOfNodes == nodes.length) {
				TreeNode[] old = nodes;
				nodes = new TreeNode[numOfNodes + 100];
				for (int i = 0; i < numOfNodes; i++)
					nodes[i] = old[i];
			}

			for (int i = numOfNodes; i > index; i--)
				nodes[i] = nodes[i-1];
			nodes[index] = node;
			numOfNodes++;
		}

		public void remove(int index) {
			if (index < 0)
				return;

			for (int i = index; i < numOfNodes - 1; i++)
				nodes[i] = nodes[i+1];
			numOfNodes--;
		}

		public void sort() {
			// create a heap
			for (int i = 1; i < numOfNodes; i++) {
				int child, parent = i;
				while (true) {
					child = parent;
					parent = (parent + 1) / 2 - 1;
					if (compare(parent, child) >= 0)
						break;
					swap(parent, child);
					if (parent == 0)
						break;
				}
			}

			// remove the root and repair the heap
			for (int i = numOfNodes - 1; i > 0; i--) {
				swap(0, i);
				int parent = 0;
				while (true) {
					int child = 2 * (parent + 1) - 1;
					if (child >= i)
						break;

					if (child + 1 < i
						&& compare(child, child + 1) < 0)
						child++;

					if (compare(parent, child) < 0) {
						swap(parent, child);
						parent = child;
					}
					else
						break;
				}
			}
		}

		private void swap(int a, int b) {
			TreeNode node = nodes[a];
			nodes[a] = nodes[b];
			nodes[b] = node;
		}

		protected TreeNode nodes[];
		protected int numOfNodes;
	}
}
