

//--------------------------------------------------------------------
// Class MasterFileTable (MFT)
//
// Purpose  : handle all file Index (MFTIndex)
//--------------------------------------------------------------------

import java.util.Vector;
import java.util.StringTokenizer;
import java.awt.Font;
import java.awt.Frame;
import java.awt.TextArea;

public class MasterFileTable extends Vector
{
	private static FreeSpaceMgr freeSpaceMgr;
	private static int noOfFiles;

	private static TextArea theDisplay;
	private static Frame theFrame;
	private static FileAttribute availableEntry;


//--------------------------------------------------------------------
// Contructor for MasterFileTable (1)
//
// Input	: content of MFT
// Output   : None
// Purpose  : contructing the MasterFileTable, initial Mounting
//--------------------------------------------------------------------

	MasterFileTable (String fileContent)
	{
		super ();
		
		int endIndex = fileContent.indexOf("<eof>");
		endIndex = fileContent.lastIndexOf("\n", endIndex);
		fileContent = fileContent.substring(0, endIndex + 1);

		// <type fileName linkName linkIndex size lastModified lastAccesssed 
		// hardLinkCount blockNo0 blockNo1 ... \n> 
		StringTokenizer entry = new StringTokenizer(fileContent, "\n");
		availableEntry = new FileAttribute
			("? - - 0 0 0 0 0 0 \n", false);
		FileAttribute tmpFileAttribute;
		int count = entry.countTokens();
		for ( int index = 0 ; index < count ; index++ )
		{
			tmpFileAttribute = new FileAttribute(entry.nextToken(), false);
			addElement(tmpFileAttribute);
			if (tmpFileAttribute.isAvailable())
				setElementAt(availableEntry, index);
			else ++this.noOfFiles;
		}
	}


//--------------------------------------------------------------------
// Contructor for MasterFileTable (2)
//
// Input	: content of MFT and FSM
// Output   : None
// Purpose  : contructing the MasterFileTable (fully mounting)
//--------------------------------------------------------------------
	
	MasterFileTable (String fileContent, String freeSpcContent)
	{
		this (fileContent);
		this.freeSpaceMgr = new FreeSpaceMgr(freeSpcContent);

		// table display
		theFrame = new Frame ("Master File Table");
		theDisplay = new TextArea ();
		theFrame.add ("Center", theDisplay );
		theDisplay.setEditable (false);
		theDisplay.setFont(new Font("Monospaced", Font.PLAIN, 17));
		theFrame.setSize(720, 320);	
		theFrame.show();
		theFrame.toFront();
		theDisplay.setText(getContent());
	}


//--------------------------------------------------------------------
// Contructor for MasterFileTable (3)
//
// Input	: noOfBlocks, volumeName
// Output   : None
// Purpose  : contructing the MasterFileTable (during formatting)
//--------------------------------------------------------------------

	MasterFileTable (int noOfBlocks, String volumeName)
	{
		// block no. 0 reserved for MFT file 
		// block no. 1 reserved for FreeSpaceMgr (FSM) file 
		// block no. 2 reserved for root directory
		this ("s MFT - 0 0 0 0 1 0 \n" +
              "s FSM - 0 0 0 0 1 1 \n" +
			  "d " + volumeName + " - 0 0 0 0 1 2 \n" +
			  "<eof>" );
		this.freeSpaceMgr = new FreeSpaceMgr(noOfBlocks);

		// update FSM properties
		getFileAttribute(1).lastModified = System.currentTimeMillis();
		getFileAttribute(1).lastAccessed = System.currentTimeMillis();
		getFileAttribute(1).hardLinkCount = 1;
		getFileAttribute(1).size = getFSMContent().length();

		// update MFT properties
		getFileAttribute(0).lastModified = System.currentTimeMillis();
		getFileAttribute(0).lastAccessed = System.currentTimeMillis();
		getFileAttribute(0).hardLinkCount = 1;
		getFileAttribute(0).size = getContent().length();

		theDisplay.setText(getContent());
	}


//--------------------------------------------------------------------
// Method getNoOfFiles
//
// Input	: None
// Output   : number of files
// Purpose  : to get total number of files
//--------------------------------------------------------------------

	public int getNoOfFiles ()
	{
		return noOfFiles;
	}


//--------------------------------------------------------------------
// Method getNoOfFreeBlocks
//
// Input	: None
// Output   : NoOfFreeBlocks
// Purpose  : to get NoOfFreeBlocks of disks
//--------------------------------------------------------------------

	public int getNoOfFreeBlocks ()
	{
		return freeSpaceMgr.getNoOfFreeBlocks();
	}


//--------------------------------------------------------------------
// Method getContent
//
// Input	: None
// Output   : MFT file content
// Purpose  : to get MFT file Content
//--------------------------------------------------------------------
	
	public String getContent ()
	{
		String tmpString = new String();
		for ( int index = 0 ; index < size() ; index++ )
			tmpString += getFileAttribute(index).getContent() + "\n";
		return tmpString + "<eof>";
	}


//--------------------------------------------------------------------
// Method getFSMContent
//
// Input	: None
// Output   : FSM file content
// Purpose  : to get FSM file Content
//--------------------------------------------------------------------

	public String getFSMContent ()
	{
		return freeSpaceMgr.getContent();
	}


//--------------------------------------------------------------------
// Method getAvailable
//
// Input	: None
// Output   : MFTIndex that available
// Purpose  : to get available entry in MFT
//--------------------------------------------------------------------
	
	private int getAvailable ( )
	{
		return indexOf(availableEntry);
	}


//--------------------------------------------------------------------
// Method getFileAttribute
//
// Input	: MFTIndex
// Output   : fileAttribute at that MFTIndex
// Purpose  : to get fileAttribute at that MFTIndex
//--------------------------------------------------------------------

	public FileAttribute getFileAttribute (int index)
	{
		if (index >= size())
			return availableEntry;

		return (FileAttribute)elementAt(index);
	}


//--------------------------------------------------------------------
// Method getLastModified
//
// Input	: MFTIndex
// Output   : lastModified at that MFTIndex
// Purpose  : to get lastModified at that MFTIndex
//--------------------------------------------------------------------

	public long getLastModified (int index)
	{
		return getFileAttribute(index).lastModified;
	}


//--------------------------------------------------------------------
// Method getSize
//
// Input	: MFTIndex
// Output   : fileSize at that MFTIndex
// Purpose  : to get fileSize at that MFTIndex
//--------------------------------------------------------------------

	public int getSize (int index)
	{
		return getFileAttribute(index).size;
	}


//--------------------------------------------------------------------
// Method getFileName
//
// Input	: MFTIndex
// Output   : fileName at that MFTIndex
// Purpose  : to get fileName at that MFTIndex
//--------------------------------------------------------------------

	public String getFileName (int index)
	{
		return getFileAttribute(index).fileName;
	}


//--------------------------------------------------------------------
// Method getLinkIndex
//
// Input	: MFTIndex
// Output   : linkIndex at that MFTIndex
// Purpose  : to get linkIndex at that MFTIndex
//--------------------------------------------------------------------

	public int getLinkIndex (int index)
	{
		return getFileAttribute(index).linkIndex;
	}


//--------------------------------------------------------------------
// Method getLinkName
//
// Input	: MFTIndex
// Output   : linkName at that MFTIndex
// Purpose  : to get linkName at that MFTIndex
//--------------------------------------------------------------------

	public String getLinkName (int index)
	{
		return getFileAttribute(index).linkName;
	}


//--------------------------------------------------------------------
// Method getNoOfBlocks 
//
// Input	: MFTIndex
// Output   : NoOfBlocks at that MFTIndex
// Purpose  : to get NoOfBlocks at that MFTIndex
//--------------------------------------------------------------------
	
	public int getNoOfBlocks (int index)
	{
		return getFileAttribute(index).getNoOfBlocks();
	}


//--------------------------------------------------------------------
// Method getType 
//
// Input	: MFTIndex
// Output   : file type at that MFTIndex
// Purpose  : to get file type at that MFTIndex
//--------------------------------------------------------------------
	
	public String getType (int index)
	{
		return getFileAttribute(index).type;
	}


//--------------------------------------------------------------------
// Method updateEntry
//
// Input	: FileAttribute, MFTIndex
// Output   : none
// Purpose  : to update MFT entry with new one
//--------------------------------------------------------------------

	public void updateEntry (FileAttribute fileAttribute, int index)
	{
		setElementAt(fileAttribute, index);
		theDisplay.setText(getContent());
		return;
	}


//--------------------------------------------------------------------
// Method newEntry
//
// Input	: fileName, linkName, linkIndex, type, noOfBlock
// Output   : MFTIndex
// Purpose  : to insert newEntry into MFT
//--------------------------------------------------------------------

	public int newEntry (String fileName, String linkName, int linkIndex,
		String type, int noOfBlocks)
		throws NotEnoughSpaceException
	{
		if (noOfBlocks >= getNoOfFreeBlocks())
			throw new NotEnoughSpaceException
			(noOfBlocks, getNoOfFreeBlocks());

		int index, blockNo = 0;
		String blockNoContent = new String();
		FileAttribute fileAttribute;

		if (!type.equals("l"))
			blockNo = freeSpaceMgr.firstFit(noOfBlocks);

		// first fit found
		if (blockNo != -1)
		{
			if (!type.equals("l"))
			{
				for (int i = 0 ; i < noOfBlocks ; i++)
					blockNoContent += Integer.toString(blockNo + i) + " ";
				freeSpaceMgr.take (blockNo, noOfBlocks);
			}
			else
			{
				blockNoContent = "0 ";
			}
			fileAttribute = new FileAttribute(type + " " +
							            	  fileName + " " +
							            	  linkName + " " +
							            	  Integer.toString(linkIndex) + " " +
								              blockNoContent + "\n",
								              true);
		}
		else
		{
			blockNo = freeSpaceMgr.maxNoOfFreeBlocks;
			int blockAvail = freeSpaceMgr.maxFreeBlockNo;
			for (int i = 0 ; i < blockAvail ; i++)
				blockNoContent += Integer.toString(blockNo + i) + " ";
			freeSpaceMgr.take (blockNo, blockAvail);
			fileAttribute = new FileAttribute(type + " " +
							            	  fileName + " " +
							            	  linkName + " " +
							            	  Integer.toString(linkIndex) + " " +
								              blockNoContent + "\n",
								              true);

			noOfBlocks -= blockAvail;
			while (noOfBlocks > 0)
			{
				blockNo = freeSpaceMgr.firstFit(noOfBlocks);
				blockNoContent = new String();
				if (blockNo != -1)
				{
					for (int i = 0 ; i < blockAvail ; i++)
						blockNoContent += Integer.toString(blockNo + i) + " ";
					freeSpaceMgr.take (blockNo, noOfBlocks);
					fileAttribute.addBlocks(blockNoContent);
					break;
				}
				else
				{
					blockNo = freeSpaceMgr.maxNoOfFreeBlocks;
					blockAvail = freeSpaceMgr.maxFreeBlockNo;
					for (int i = 0 ; i < blockAvail ; i++)
						blockNoContent += Integer.toString(blockNo + i) + " ";
					freeSpaceMgr.take (blockNo, blockAvail);
					fileAttribute.addBlocks(blockNoContent);
					noOfBlocks -= blockAvail;
				}
			}

		}

		// insert to MFT
		if ((index = getAvailable()) != -1)
		{
			setElementAt(fileAttribute, index);
		}
		else
		{
			addElement(fileAttribute);
			index = indexOf(fileAttribute);
		}

		theDisplay.setText(getContent());
		return index;
	}


//--------------------------------------------------------------------
// Method reorder
//
// Input	: blockContent
// Output   : sorted blockContent
// Purpose  : sorting blockContent
//--------------------------------------------------------------------
	
	public String reorder (String blockNoContent, boolean ascend)
	{
		StringTokenizer blockToken = new StringTokenizer(blockNoContent);
		int count = blockToken.countTokens();
		if (count == 1) return blockNoContent;

		int[] blockNo = new int[count];
		int i = 0, j = 0;

		blockNo[i] = Integer.parseInt(blockToken.nextToken());
		for ( i = 1 ; i < count ; ++i )
		{
			blockNo[i] = Integer.parseInt(blockToken.nextToken());
			for ( j = 0 ; j < i ; ++j )
			{
				if (ascend)
				{
					if (blockNo[j] > blockNo[i])
					{
						int tmp = blockNo[j];
						blockNo[j] = blockNo[i];
						blockNo[i] = tmp;
					}
				}
				else
				{
					if (blockNo[j] < blockNo[i])
					{
						int tmp = blockNo[j];
						blockNo[j] = blockNo[i];
						blockNo[i] = tmp;
					}
				}
			}
		}

		blockNoContent = new String();
		for ( i = 0 ; i < count ; ++i )
			blockNoContent += " " + Integer.toString(blockNo[i]);

		return blockNoContent;
	}


//--------------------------------------------------------------------
// Method reallocate
//
// Input	: MFTIndex
// Output   : none
// Purpose  : to reallocate blocks from MFTEntry
//--------------------------------------------------------------------
	
	public void reallocate (int index)
	{
		int totalNoOfBlocks = getNoOfBlocks(index);
		int noOfReallocateBlock = totalNoOfBlocks;

		// if it is MFT blockNo 0, 1, 2 is default
		if (index == 0)
			noOfReallocateBlock -= 1;

		// free blocks and then reallocate
		try 
		{
			deleteBlocks(index, noOfReallocateBlock);
			addBlocks(index, noOfReallocateBlock);
		}
		catch (Exception e) { }
		return;
	}


//--------------------------------------------------------------------
// Method deleteBlocks
//
// Input	: MFTIndex, noOfBlocks to be deleted
// Output   : none
// Purpose  : to delete blocks from MFTEntry
//--------------------------------------------------------------------
	
	public void deleteBlocks (int index, int noOfBlocks)
	{
		int totalNoOfBlocks = getNoOfBlocks(index);

		for (int i = totalNoOfBlocks - noOfBlocks ; i < totalNoOfBlocks ; ++i)
		{
			freeSpaceMgr.free
			(((FileAttribute)elementAt(index)).getBlockAt(i), 1);
		}

		((FileAttribute)elementAt(index)).deleteBlocks(noOfBlocks);
		return;
	}


//--------------------------------------------------------------------
// Method addBlocks
//
// Input	: MFTIndex, additional noOfBlocks
// Output   : none
// Purpose  : to add more blocks to MFTEntry
//--------------------------------------------------------------------
	
	public void addBlocks (int index, int noOfBlocks)
				throws NotEnoughSpaceException
	{
		FileAttribute fileAttribute = getFileAttribute (index);

		String blockNoContent = new String();
		while (noOfBlocks > 0)
		{
			int blockNo = freeSpaceMgr.firstFit(noOfBlocks);
			if (blockNo != -1)
			{
				for (int i = 0 ; i < noOfBlocks ; i++)
					blockNoContent += Integer.toString(blockNo + i) + " ";
				freeSpaceMgr.take (blockNo, noOfBlocks);
				break;
			}
			else
			{
				blockNo = freeSpaceMgr.maxFreeBlockNo;
				int blockAvail = freeSpaceMgr.maxNoOfFreeBlocks;
				for (int i = 0 ; i < blockAvail ; i++)
					blockNoContent += Integer.toString(blockNo + i) + " ";
				freeSpaceMgr.take (blockNo, blockAvail);
				noOfBlocks -= blockAvail;
			}
		}

		fileAttribute.addBlocks(reorder(blockNoContent, true));

		setElementAt(fileAttribute, index);
		theDisplay.setText(getContent());
		return ;
	}


//--------------------------------------------------------------------
// Method deleteEntry
//
// Input	: MFTIndex
// Output   : none
// Purpose  : to delete entry from the MFT
//--------------------------------------------------------------------

	public void deleteEntry (int index)
	{
		// free it
		FileAttribute fileAttribute = getFileAttribute(index);
		int noOfBlocks = fileAttribute.getNoOfBlocks();
		if (!isLink(index))
		{
			for (int i = 0 ; i < noOfBlocks ; i++)
				freeSpaceMgr.free (fileAttribute.getBlockAt(i), 1);
		}

		// delete entry
		setElementAt(availableEntry, index);

		--noOfFiles;
		theDisplay.setText(getContent());
		return;
	}


//--------------------------------------------------------------------
// Method trim
//
// Input	: none
// Output   : none
// Purpose  : trim the tail
//--------------------------------------------------------------------

	public void trim() 
	{ 
		// take away last element
		while (((FileAttribute)lastElement()).isAvailable())
			setSize(size() - 1);
		validate();
		return;
	}

	public void validate() 
	{ 
		theDisplay.setText(getContent());
	}


//--------------------------------------------------------------------
// Method typeCheck
//
// Input	: none
// Output   : flag true if it is that type
// Purpose  : check the type of fileAttribute
//--------------------------------------------------------------------

	public boolean defragOK(int noOfBlocks) 
	{ 
		return freeSpaceMgr.defragOK(noOfBlocks); 
	}

	public boolean isAvailable(int index) 
	{ 
		return getFileAttribute(index).isAvailable(); 
	}

	public boolean isSystem(int index) 
	{ 
		return getFileAttribute(index).isSystem(); 
	}

	public boolean isDirectory(int index) 
	{
		return getFileAttribute(index).isDirectory();
	}

	public boolean isText(int index) 
	{
		return getFileAttribute(index).isText();
	}

	public boolean isExecutable(int index)
	{
		return getFileAttribute(index).isExecutable();
	}

	public boolean isLink(int index)
	{
		return getFileAttribute(index).isLink(); 
	}

}

//--------------------------------------------------------------------
// End Class MasterFileTable
//--------------------------------------------------------------------