import java.io.*;
import java.util.*;

/**
 * @author Scott Douglas ( scott@scottdouglas.net )
 */
public class HuffmanDecoder {

	public static void main(String[] args) {
		if ( args.length != 3 ) {
			System.err.println( "Usage: HuffmanDecoder filename.freq filename.comp outputfile" );
			System.exit( 0 );
		} 
		
		System.out.println( "Beginning Decoding" );
		System.out.println( "=Loading Table From File" );
		
		List unusedNodes = new LinkedList();
		List allNonNullNodes = new LinkedList();
		List unusedChars = new LinkedList( FrequencyTable.getCharactersFromTable( args[ 0 ] ) );

		for ( int i = 0; i < unusedChars.size(); i++ ) {
			HuffmanTreeNode aNode = new HuffmanTreeNode( (HuffmanCharacter) unusedChars.get( i ) );
			unusedNodes.add( aNode );
			allNonNullNodes.add( aNode );
		}
		
		System.out.println( "=Creating Tree of Character Frequencies" );
		System.out.println( "==Connecting Nodes" );
		
		if ( unusedNodes.size() == 0 ) {
			System.err.println( "===No Nodes to connect" );
			System.err.println( "===Error Encoding...Exiting" );
			System.exit( 0 );
		}
	
		Collections.sort( unusedNodes );

		while ( unusedNodes.size() > 1 ) {
			HuffmanTreeNode newNode = new HuffmanTreeNode();
			newNode.setLeftNode( ( HuffmanTreeNode ) unusedNodes.remove( 0 ) );
			newNode.setRightNode( ( HuffmanTreeNode ) unusedNodes.remove( 0 ) );
			unusedNodes.add( newNode );
			Collections.sort( unusedNodes );
			
			System.out.println( "===Connecting " + newNode.getLeftNode() + " to " + newNode.getRightNode() );
		}
	
		HuffmanTreeNode root = ( HuffmanTreeNode ) unusedNodes.remove( 0 );

		System.out.println( "=Uncompressing bits" );
		
		BitInputStream inputBits = null;
		try {
			BufferedWriter output = new BufferedWriter( new PrintWriter( new FileOutputStream( args[ 2 ] ) ) );
			FileInputStream input = new FileInputStream( args[ 1 ] );
			long bitsToRead = FrequencyTable.getNumBitsToRead( args[ 0 ] );
			
			
			HuffmanTreeNode currentNode = root;
			while ( bitsToRead > 0 ) {
				byte[] buffer = new byte[ 1024 ];
				input.read( buffer );
				inputBits = new BitInputStream( buffer );
				int aBit = -1;
				while ( inputBits.availableBits() > 0 && bitsToRead > 0 ) {
					aBit = inputBits.readBit();
				
					if ( aBit == 0 ) {
						currentNode = currentNode.getLeftNode();
					
						if ( currentNode.getCharacter() != null ) {
							// Print character to file
							output.write( "" + currentNode.getCharacter().getCharacter() );
							// Reset to root
							currentNode = root;
						}
					} else if ( aBit == 1 ) {
						currentNode = currentNode.getRightNode();
					
						if ( currentNode.getCharacter() != null ) {
							// Print character to file
							output.write( "" + currentNode.getCharacter().getCharacter() );
							// Reset to root
							currentNode = root;
						}					
					} else {
						System.err.println( "Flagrant SystemError!" );
						System.err.println( aBit );
						System.exit( 0 );
					}
				
					bitsToRead--;					
				}
				
			}
			/*
			byte[] buffer = new byte[ FrequencyTable.getNumBitsToRead( args[ 0 ] ) / 8 + 1 ];

			input.read( buffer );
			BitInputStream inputBits = new BitInputStream( buffer );
	
			HuffmanTreeNode currentNode = root;
			int aBit = -1;
			while ( inputBits.availableBits() > 0 && bitsToRead > 0 ) {
				aBit = inputBits.readBit();
				
				if ( aBit == 0 ) {
					currentNode = currentNode.getLeftNode();
					
					if ( currentNode.getCharacter() != null ) {
						// Print character to file
						output.write( "" + currentNode.getCharacter().getCharacter() );
						// Reset to root
						currentNode = root;
					}
				} else if ( aBit == 1 ) {
					currentNode = currentNode.getRightNode();
					
					if ( currentNode.getCharacter() != null ) {
						// Print character to file
						output.write( "" + currentNode.getCharacter().getCharacter() );
						// Reset to root
						currentNode = root;
					}					
				} else {
					System.err.println( "Flagrant SystemError!" );
					System.err.println( aBit );
					System.exit( 0 );
				}
				
				bitsToRead--;
			}
			*/

			output.flush();
			output.close();
			inputBits.close();
		} catch( IOException e ) {
			System.err.println( "HuffmanEncoder: IO Error" );
			System.exit( 0 );			
		}
		
		System.out.println( "Done Decoding" );
	}
	/*
	private static HuffmanBitList getBitRepresentation( HuffmanCharacter character, List allNonNullNodes ) {
		HuffmanBitList bitRepresentation = new HuffmanBitList();
		for ( int i = 0; i < allNonNullNodes.size(); i++ ) {
			HuffmanTreeNode aNode = ( HuffmanTreeNode ) allNonNullNodes.get( i );
			
			if ( character.equals( aNode.getCharacter() ) ) {
				bitRepresentation = aNode.getBits();
			}
		}
		
		return bitRepresentation;
	}
	*/
}

