/*
 * @(#)FileTree.java
 *
 * classi non pubbliche definite in questo sorgente:
 * - FTSerialNode
 */
package ereinion.io;

import java.io.File;
import java.util.Vector;

import ereinion.math.BinNumber;
import ereinion.util.NTree;
import ereinion.util.BNTree;

/**
 * Classe per la gestione di un Tree di file e directory.
 * Vengono fornite anche funzioni di serializzazione.
 *
 * @author  Ereinion
 * @version 1.1, 25/11/2002
 * @since EPR 1.0
 */
public class FileTree
{

	/**
     * Funzione che riconverte un array di byte in un NTree rappresentante un Tree di directory.
     *
     * @param treeData i dati del FileTree.
	 * @return il nodo radice del FileTree.
     */
	public static NTree fromBytes(byte[] treeData)
	{
		int nodeCount = new BinNumber(subarray(treeData,0,4)).intValue();
		int[] parentNodes = new int[nodeCount];
		BNTree[] nodes = new BNTree[nodeCount];
		int start = 4;
		int parentNode = 0;
		int childCount = (new BinNumber(subarray(treeData,start+2,2))).intValue();
		int nodeLength = (new BinNumber(subarray(treeData,start+4,2))).intValue();
		BNTree root = BNTree.makeRoot(new String(subarray(treeData,start+6,nodeLength)),true);
		nodes[0] = root;
		start+= nodeLength+6;
		for (int i=1; i<nodeCount; i++) {
			parentNode = (new BinNumber(subarray(treeData,start,2))).intValue();
			childCount = (new BinNumber(subarray(treeData,start+2,2))).intValue();
			nodeLength = (new BinNumber(subarray(treeData,start+4,2))).intValue();
			if (childCount>0)
				nodes[i] = BNTree.makeNode(nodes[parentNode],new String(subarray(treeData,start+6,nodeLength)),true);
			else
				nodes[i] = BNTree.makeNode(nodes[parentNode],new String(subarray(treeData,start+6,nodeLength)),false);
			start+= nodeLength+6;
		}
		return root;
	}

	/**
     * Funzione che converte un FileTree in un array di byte.
     *
     * @param root la radice del FileTree da convertire.
	 * @return i dati del FileTree.
     */
	public static byte[] toBytes(NTree root)
	{
		Vector serialTree = new Vector();
		int byteCount = serializeTree(root,serialTree,0);
		byte[] treeData = new byte[4+(byteCount+(serialTree.size()*6))];
		byte[] nodeCount = (new BinNumber(serialTree.size(),4)).getBytes();
		int start = 0;
		write(treeData,nodeCount,start);
		start+= 4;
		for (int k=0; k<serialTree.size(); k++) {
			FTSerialNode current = (FTSerialNode)serialTree.elementAt(k);
			int length = current.node.length();
			write(treeData,(new BinNumber(current.parentValue,2)).getBytes(),start);
			write(treeData,(new BinNumber(current.childCount,2)).getBytes(),start+2);
			write(treeData,(new BinNumber(length,2)).getBytes(),start+4);
			start+=6;
			write(treeData,current.node.getBytes(),start);
			start+=length;
		}
		return treeData;
	}

	/**
     * Funzione che crea un FileTree a partire da un File radice.
     *
     * @param sourcePath il file radice
	 * @return il nodo radice del FileTree.
     */
	public static NTree buildFileTree(File sourcePath)
	{
		BNTree tree = BNTree.makeRoot(sourcePath.getName(),true);
		expandNode(tree,sourcePath);
		return tree;
	}


	/* campi privati */

	private static void expandNode(BNTree node, File basePath)
	{
		String[] fileList = basePath.list();
		for (int k=0; k<fileList.length; k++) {
			File currentFile = new File(basePath,fileList[k]);
			if (currentFile.isDirectory())
				expandNode(BNTree.makeNode(node,fileList[k],true),currentFile);
			else
				BNTree.makeNode(node,fileList[k],false);
		}
	}

	private static void write(byte[] array, byte[] sub, int offset)
	{
		for (int k=0; k<sub.length; k++)
			array[offset+k] = sub[k];
	}

	private static byte[] subarray(byte[] array, int offset, int length)
	{
		byte[] sub = new byte[length];
		for (int k=0; k<length; k++)
			sub[k] = array[offset+k];
		return sub;
	}

	private static int serializeTree(NTree node, Vector data, int parentValue)
	{
		int nodeValue = data.size();
		String nodeData = (String)node.getData();
		int dim = nodeData.length();
		data.addElement(new FTSerialNode(nodeData,parentValue,node.getChildCount()));
		for (int k=0; k<node.getChildCount(); k++)
			dim+=serializeTree(node.getChildAt(k),data,nodeValue);
		return dim;
	}

	private final static String VERSION = "v100";

}

/*
 * classe di package : FTSerialNode
 *
 * classe per la gestione di un nodo da serializzare.
 */
class FTSerialNode
{

	public int parentValue;

	public int childCount;

	public String node;

	public FTSerialNode(String n, int pv, int cc)
	{
		node = n;
		childCount = cc;
		parentValue = pv;
	}

}
