/*
 * Kompare - Visual Navigation of Source Tree diff using HTML 
 *
 * Released under GPL http://www.gnu.org/copyleft/gpl.html
 *
 * Usage:
 *
 * javac Kompare.java
 * java Komapre <path to source tree 1> <path to source tree 2>
 *
 * All HTML files will be generated in the same directory 
 * from where  Kompare is ran from, Open the generated 
 * "index.html" to start navigating the source diff.
 * 
 * kishan@hackorama.com  www.hackorama.com     2001 APRIL
 *
 */
import java.io.*;
import java.util.*;
import java.text.*;

public class Kompare
{
        public static void main( String args[] )
        {
		if( args.length < 2 ){
			System.out.print("\n Usage: java Kompare " );
			System.out.print(" <path to source directory 1>" );
			System.out.println(" <path to source directory 2>\n" );
		}else{
			String one = args[0];
			String two = args[1];
               	 	Kompare kompare = new Kompare( one, two );
		}
        }

	// modify these to list different source file types , file types not listed
	// here will be ignored while parsing 

	String[] source_types = { ".c" , ".h" , ".cc", ".cpp" , ".C", ".H", ".CC", ".CPP" };

	// if there is any folder in a source tree which need not be parsed list
	// it here and it will not be parsed 

	String[] ignore_folders = { " " };

        private Vector a_dirlist = new Vector();
        private Vector b_dirlist = new Vector();

        private String a_dir;
        private String b_dir;

        private String a_base;
        private String b_base;

        private int[] _a;
        private int[] _b;

        private String[] a_files;
        private String[] b_files;

        private int a_len = 0;
        private int b_len = 0;
        private int a_count = 0;
        private int b_count = 0;

        private int a_match = 0;
        private int b_match = 0;

        private String[] a_column;
        private String[] b_column;

        private String firstpage;
        private int  page_count =  0 ;
        private static DataOutputStream out  = null;
        private static DataOutputStream para  = null;

        private static boolean first_time = true;
	String style = "style=\"text-decoration:none\"";

	private int i_files = 0;
	private int j_files = 0;
	private int k_files = 0;
	private int i_lines = 0;
	private int j_lines = 0;
	private int i_bytes = 0;
	private int j_bytes = 0;

	private int i_total = 0;
	private int j_total = 0;
	private int k_total = 0;

	private int i_tot_lines = 0;
	private int j_tot_lines = 0;

	private int i_tot_bytes = 0;
	private int j_tot_bytes = 0;

	private String green = "#DDFFDD";
	private String red = "#FFDDDD";
	private String bg_one  = "#EEEEEE";

	private String a_start;
	private String b_start;

	private int a_dir_lines = 0;
	private int b_dir_lines = 0;
	private int a_dir_files = 0;
	private int b_dir_files = 0;

	private long elapsed = 0 ;

        public Kompare( String one , String two )
        {

		while ( one.endsWith( "/" ) || one.endsWith( "\" ) ){
			one = one.substring( 0 , one.length() - 1 );
		}
		while ( two.endsWith( "/" ) || two.endsWith( "\" ) ){
			 two = two.substring( 0 , two.length() - 1 );
		}

		a_dirlist.addElement( one );
		b_dirlist.addElement( two );
		a_base = one.substring(  0, one.lastIndexOf("/") );
		b_base = two.substring(  0, two.lastIndexOf("/") );


		firstpage = makeFilename( one );

		a_start = one;	
		b_start = two;

		open_para( "complete.html" );

		init_para();

		long startTime = java.lang.System.currentTimeMillis();
                ignite();
		elapsed = java.lang.System.currentTimeMillis() - startTime;
		
		final_report();

		end_para();
		close_para();

        }

        private String makeFilename( String str)
	{
		str = str.replace('/', '_')+".html";
		str = str.replace('\\', '_');

		if( str.startsWith( ".." ) )
			str = str.substring( 2 );
		if( str.startsWith( "." ) )
			str = str.substring( 1 );
		
		return str ;
	}

        public void ignite()
        {
                for( int i = 0 ; i < a_dirlist.size() ; i++ ){
                        int match =  dir_matching(i);
                        if( match >= 0 ){
                                String a = (String) a_dirlist.elementAt(i);
                                String b = (String) b_dirlist.elementAt(match);
                                a_dirlist.removeElementAt(i);
                                b_dirlist.removeElementAt(match);
                                step( a , b );
                        }else{
                                //a_dirlist.removeElementAt(i);
                        }
                }
                //b_dirlist.removeAllElements();
        }

	private Vector x_list = new Vector();

        public void dir_parse( String dir , int option )
	{

		print( "dir parsing "+ dir +" ("+ option +")");
                File x = new File( dir );
                String[] x_files = x.list();
                for ( int i=0; i < x_files.length; i++) {
			String the_file = dir+"/"+x_files[i];
			if( is_directory(the_file) ){
				x_list.addElement(the_file );
			}else{
				int lines = get_lines(the_file);
				if(option == 1){
				 	i_lines+=lines;
					a_dir_lines+=lines;
				}else if (option == 2 ){
					j_lines+=lines;
					b_dir_lines+=lines;
				}
			}
			if(option == 1) {
				j_files++;
				a_dir_files++;
			}else if (option == 2 ) {
				k_files++;
				b_dir_files++;
			}
		}

		//recurse subdirectories 
                for ( int i=0; i < x_list.size() ; i++) {
			String the_file = (String) x_list.elementAt(i);
			x_list.removeElementAt(i);
			dir_parse( the_file , option );
		}

	}

        private int  dir_matching( int i )
        {
                if( first_time ){
                        first_time = false ;
                        return 0;
                }

                for( int j = 0 ; j < b_dirlist.size() ; j++ ){

			String one = (String) a_dirlist.elementAt(i);
			String two = (String) b_dirlist.elementAt(j);

			one = one.substring(  one.lastIndexOf("/") );
			two = two.substring(  two.lastIndexOf("/") );

                        if( one.equals(two) )
                                return j;
                }
                return -1 ;
        }

        public void step( String _a_dir , String _b_dir )
        {

                print( "working "+_a_dir +" : " + _b_dir );

                a_len = 0;
                b_len = 0;
        	a_count = 0;
        	b_count = 0;

                a_dir = _a_dir;
                b_dir = _b_dir;

                File a = new File( a_dir );
                File b = new File( b_dir );
                a_files = a.list();
                b_files = b.list();
                a_len =  a_files.length;
                b_len =  b_files.length;

                _a = new int[ a_len ] ;
                _b = new int[ b_len ] ;
                a_column = new String[ a_len ];
                b_column = new String[ b_len ];

                for( int i = 0  ; i < _a.length ; i++ )
                        _a[i]  = 0;
                for( int i = 0  ; i < _b.length ; i++ )
                        _b[i]  = 0;

                parse();

                //recurse through the directories
                ignite();
        }

        public void parse()
        {
                parse_matching();
                parse_missing();
                parse_additions();
                report();
        }

        private void  report()
        {
		String thispage = makeFilename( a_dir );

                open_page(thispage); page_count++;

		i_files = 0;
		j_files = 0;
		k_files = 0;

		i_lines = 0;
		j_lines = 0;

		i_bytes = 0;
		j_bytes = 0;

                init_page();

                for( int i = 0 ; i < a_column.length ; i ++ ){
                        if( i < b_column.length ){
                                print_elem( i , a_column[i] , b_column[i] );
				if( i >= a_match ) {
					j_files++;
					k_files++;
				}else{
				   i_files++;
				}
                        }else{
                                print_elem( i , a_column[i] , null );
				j_files++;
                        }
                }
                if( b_column.length > a_column.length ){
                        for( int i = a_column.length ; i < b_column.length ; i++ ){
                                print_elem( i ,  null , b_column[i] );
				k_files++;
			}
                }

		i_total+=i_files;
		j_total+=j_files;
		k_total+=k_files;

		i_tot_lines+=i_lines;
		j_tot_lines+=j_lines;

		i_tot_bytes+=i_bytes;
		j_tot_bytes+=j_bytes;

                end_page();

		int change = i_lines-j_lines;
		double percent = 0.0 ;
		String color = "white" ;
		if( change > 0 ){
			percent = ( (float)change/(float)i_lines ) * 100.0  ;
			color = "green";
			pprint("<tr><td><a href="+thispage+">"+a_dir+"</a> </td><td>"+j_files+"</td><td>"+k_files+"</td><td>"+i_lines+"</td><td>"+j_lines+"</td><td>+"+change+"</td>");
		}else{
			percent = ( ((float)change*-1.0)/(float)j_lines ) * 100.0  ;
			color = "red";
			pprint("<tr><td><a href="+thispage+">"+a_dir+"</a> </td><td>"+j_files+"</td><td>"+k_files+"</td><td>"+i_lines+"</td><td>"+j_lines+"</td><td>"+change+"</td>");
		}
		pprint_graph( percent , color );
		pprint("</tr>");

                close_page();

        }

	private void init_para()
	{
		pprint("<html><body bgcolor=white link=black vlink=black>");

		pprint("<br><br>");

                pprint("<table border=0><tr><td bgcolor="+bg_one+">");
		pprint("<b>|<a "+style+" href=index.html>MAIN |</a></b>");
                pprint("</td></tr><tr><td>");

		pprint("<br><br><table border=1>");
		pprint("<tr><td>dir</td><td>files(-)</td><td>files(+)</td><td>lines(old)</td><td>lines(new)</td><td>lines(change)</td><td>lines %</td></tr>");
	}
	private void end_para()
	{
		pprint("</table><br><br>");

                pprint("</td></tr><tr><td bgcolor="+bg_one+">");
		pprint("<b>|<a "+style+" href=index.html>MAIN |</a></b>");
		pprint("</td></tr></table>");
		pprint("<br><br>");

		pprint("</body></html>");
	}
	private void clean_leftout()
	{

                for( int i = 0 ; i < a_dirlist.size() ; i++ ){
			print("removed:" +a_dirlist.elementAt(i) );
		}
                for( int i = 0 ; i < b_dirlist.size() ; i++ ){
			print("added:" +b_dirlist.elementAt(i) );
		}
	}

	private void final_report()
	{
		int a_total = i_total + j_total;
		int b_total = i_total + k_total;
		//int line_change = i_tot_lines - j_tot_lines ;
		int line_change = j_tot_lines - i_tot_lines ;

		SimpleDateFormat format = new SimpleDateFormat( "MM/dd/yyyy" );
		

                open_page("index.html"); 

		fprint("<html><body bgcolor=white link=black vlink=black alink=red><br><br>");

                fprint("<table border=0><tr><td bgcolor="+bg_one+">");

		fprint("<b>KOMPARE SOURCE DIFF</b>");

                fprint("</td></tr><tr><td>");

                fprint("<br><br>");
                fprint("Source location:");
                fprint("<table border=1 width=100%>");
                fprint("<tr><td>");
		fprint("OLD");
                fprint("</td><td>");
                fprint("<b>"+a_start+"</b><br>");
                fprint("</td></tr>");
                fprint("<tr><td>");
		fprint("NEW");
                fprint("</td><td>");
                fprint("<b>"+b_start+"</b><br>");
                fprint("</td></tr>");
                fprint("</table>");

                fprint("<br><br>");
                fprint("Diff Summary");
                fprint("<table border=1 width=100%>");

                fprint("<tr><td>");
		fprint("total files ( old - new )");
                fprint("</td><td>");
                fprint("<b>"+ a_total +"-"+ b_total +"</b><br>");
                fprint("</td></tr>");

                fprint("<tr><td bgcolor="+red+">");
		fprint("removed files");
                fprint("</td><td>");
                fprint("<b>"+j_total+"</b><br>");
                fprint("</td></tr>");

                fprint("<tr><td bgcolor="+green+">");
		fprint("added files");
                fprint("</td><td>");
                fprint("<b>"+k_total+"</b><br>");
                fprint("</td></tr>");

                fprint("<tr><td>");
		fprint("total source lines ( old - new = changed )");
                fprint("</td><td>");
                fprint("<b>"+i_tot_lines+"-"+j_tot_lines+"="+line_change+"</b><br>");
                fprint("</td></tr>");

                fprint("</table>");

		fprint("<br>");
                fprint("Diff Details");
                fprint("</td></tr><tr><td bgcolor="+bg_one+">");
                fprint("<a href="+firstpage+">NAVIGATE SOURCE TREE</a>");
                fprint("</td></tr><tr><td>");
                fprint("<br><br>");
	

                fprint("Quick View");
                fprint("</td></tr><tr><td bgcolor="+bg_one+">");
		fprint("<a href=complete.html>DIRECTORY ONLY LISTING</a>");
                fprint("</td></tr><tr><td>");
                fprint("<br><br>");


		int g_files = a_total + b_total;
		int g_lines = i_tot_lines + j_tot_lines;

                fprint("Parse Info");
		fprint("<table border=1 width=100%>");
 
                fprint("<tr><td>");
                fprint("total files");
                fprint("</td><td>");
                fprint("<b>"+g_files +"</b><br>");
                fprint("</td></tr>");

		fprint("<tr><td>");
                fprint("total source lines");
                fprint("</td><td>");
                fprint("<b>"+g_lines +"</b><br>");
                fprint("</td></tr>");

		String legend = "milliseconds" ;
		double ela = 0.0 ;

		if( elapsed > 1000 ){
			ela = (double)elapsed /1000.0;
			legend = "seconds" ;
			if( ela > 60.0 ){
				ela = ela / 60.0 ;
				legend = "minutes" ;
			}	
		}

		fprint("<tr><td>");
                fprint("elapsed time");
                fprint("</td><td>");
                fprint("<b>"+ela+" "+legend+"</b><br>");
                fprint("</td></tr>");


                fprint("</table>");
                fprint("<br><br>");
                fprint("Source file types<br>");
		for( int i = 0 ; i < source_types.length ; i++ ){
			fprint(source_types[i] +", ");
		}
		
                fprint("<br><br>");

                fprint("</td></tr><tr><td align=right bGcolor="+bg_one+">");
		fprint("<font size=-2>");	
		fprint("GENEARTED USING <a href=http://www.hackorama.com/kompare>KOMPARE</a> ON ");	
		fprint(format.format( new Date()));	
		fprint("</font>");	
                fprint("</td></tr></table>");

		fprint("<br></body></html>");

                close_page();

	}

        private boolean is_directory( String filename)
        {
		for( int i = 0 ; i < ignore_folders.length ; i++ ){
			if( filename.endsWith("/"+ignore_folders[i]) ) return false;
		}
                File temp = new File(filename);
                return temp.isDirectory();
        }

        private int  get_size( String filename)
        {
		int size = 0;
                File temp = new File(filename);
		try{
		FileInputStream in = new FileInputStream( temp );
                size  =  in.available();
		in.close();
                }catch( IOException e ){ }
		return size;
        }

        private int  get_lines( String filename)
        {
		if( is_source(filename)){
                	int count  = 0;
                	File temp = new File(filename);
                	try{
                	FileReader in = new FileReader( temp );
			LineNumberReader lines = new LineNumberReader( in );
			while( lines.readLine() != null )
				count = lines.getLineNumber();
                	in.close();
                	}catch( IOException e ){ }
                	return count;
		}else{
			return 0 ;
		}
        }

        private boolean is_source( String filename )
	{
		for( int i = 0 ; i < source_types.length ; i++ ){
			if( filename.endsWith(source_types[i])) return true;
		}
		return false;
	}
		
	static boolean once = true;	

        private void  init_page()
        {
                fprint("<html><head><title>diff</title></head>");
                fprint("<body bgcolor=white text=black link=black vlink=black alink=orange>");
	
		String last = a_dir.substring(  0, a_dir.lastIndexOf("/") );
		if( once ){
			last = "index.html" ;	
			once = false;
		}else{
			last  = makeFilename( last );
		}

                fprint("<table border=0><tr><td bgcolor="+bg_one+">");

		fprint("<b><a "+style+" href="+last+">| BACK</a> | <a "+style+" href=index.html>MAIN |</a></b>");

                fprint("</td></tr><tr><td>");

                fprint("<br><br>");
                fprint("<table border=1 width=100%>");
                fprint("<tr><td>");
		fprint("OLD");
                fprint("</td><td>");
                fprint("<b>"+a_dir+"</b><br>");
                fprint("</td></tr>");
                fprint("<tr><td>");
		fprint("NEW");
                fprint("</td><td>");
                fprint("<b>"+b_dir+"</b><br>");
                fprint("</td></tr>");
                fprint("</table>");

                fprint("<br>");

                fprint("<table border=1>");
                fprint("<tr>");
                fprint("<td>#</td><td>OLD</td><td>NEW</td><td>lines</td><td>%</td>");
                fprint("</tr>");
        }

	static boolean b_once = true;	

        private void  end_page()
        {

		int a_total = i_files + j_files ;
		int b_total = i_files + k_files ;

		//int file_change = a_total - b_total ;
		//int line_change = i_lines - j_lines ;
		int file_change = b_total - a_total ;
		int line_change = j_lines - i_lines ;

		String last = a_dir.substring(  0, a_dir.lastIndexOf("/") );
		if( b_once ){
			last = "index.html" ;	
			b_once = false;
		}else{
			last  = makeFilename( last );
		}

                fprint("</table>");
                fprint("<br><br>");
                fprint("<table border=1 width=100%>");

                fprint("<tr><td>");
		fprint("total files ( old - new )");
                fprint("</td><td>");
                //fprint("<b>"+ a_total +"-"+ b_total +"="+file_change+"</b><br>");
                fprint("<b>"+ a_total +"-"+ b_total +"</b><br>");
                fprint("</td></tr>");

                fprint("<tr><td bgcolor="+red+">");
		fprint("files removed");
                fprint("</td><td>");
                fprint("<b>"+j_files+"</b><br>");
                fprint("</td></tr>");

                fprint("<tr><td bgcolor="+green+">");
		fprint("files added");
                fprint("</td><td>");
                fprint("<b>"+k_files+"</b><br>");
                fprint("</td></tr>");

                fprint("<tr><td>");
		fprint("source lines ( old - new = changed )");
                fprint("</td><td>");
                fprint("<b>"+i_lines+"-"+j_lines+"="+line_change+"</b><br>");
                fprint("</td></tr>");

                fprint("</table>");
                fprint("<br><br>");

                fprint("</td></tr><tr><td bgcolor="+bg_one+">");

		fprint("<b><a "+style+" href="+last+">| BACK</a> | <a "+style+" href=index.html>MAIN |</a></b>");

                fprint("</td></tr></table>");

                fprint("</body>");
                fprint("</html>");

        }

        private void  open_page( String pagename )
        {
                try {
                        out = new DataOutputStream( new FileOutputStream(new File(pagename)));
                }catch( IOException e ){ }
        }

	private void  open_para( String pagename )
        {
                try {
                        para = new DataOutputStream( new FileOutputStream(new File(pagename)));
                }catch( IOException e ){ }
        }


        private void  close_page()
        {
                if ( out != null ) try { out.close(); } catch ( IOException e) { }
        }

	private void  close_para()
        {
                if ( para != null ) try { para.close(); } catch ( IOException e) { }
        }


        private void  print_elem( int i , String one , String two )
        {

		a_dir_lines = 0;
		b_dir_lines = 0;
		a_dir_files = 0;
		b_dir_files = 0;

		boolean matching = false;
		boolean a_directory = false;
		boolean b_directory = false;
                String a_color = "white";
                String b_color = "white";

                String a_file = a_dir+"/"+one;
                String b_file = b_dir+"/"+two;

		if( one != null && two!= null ){
			if( one.equals(two) ) matching = true ;
		}

		a_directory = is_directory(a_file);

		String next = makeFilename( a_file );
                if( a_directory && matching ){
                        a_dirlist.addElement( a_file );
                        one = "<b>"+one+"</b>";
                	if( one != null ) one = "<a "+style+" href="+next+">"+one+"</a>";
                }else{
			if(a_directory){
                        	//a_dirlist.addElement( a_file );
				dir_parse( a_file , 1);
				one = "<b>"+one+"</b>  ("+a_dir_files+"/"+a_dir_lines+")";
			}
			if( one != null ){
				if( is_source( a_file ) ){
				 	one = "<a href="+a_file+">"+one+"</a> ("+get_lines(a_file)+")";
				}else{
				 	one = "<a "+style+" href="+a_file+">"+one+"</a>";
				}
			}
		}

		b_directory = is_directory(b_file);

                if( b_directory && matching ){
                        b_dirlist.addElement( b_file );
                        two = "<b>"+two+"</b>";
                	if( two != null ) two = "<a "+style+" href="+next+">"+two+"</a>";
                }else{
			if( b_directory ){
				//b_dirlist.addElement( b_file );
				dir_parse( b_file , 2);
				//two = "<b>"+two+"</b>";
				two = "<b>"+two+"</b> ("+b_dir_files+"/"+b_dir_lines+")";
			}
                	if( two != null ) {
				if( is_source( b_file ) ){
					two = "<a href="+b_file+">"+two+"</a> ("+get_lines(b_file)+")";
				}else{
					two = "<a "+style+" href="+b_file+">"+two+"</a>";
				}
			}
		}


                if( i >= a_match && one != null) a_color = red;
                if( i >= b_match && two != null) b_color = green;


                fprint("<tr>");
                if ( one == null ){
                        fprint( "<td>"+i+"</td><td>&nbsp;</td><td bgcolor="+b_color+">"+two+"</td>");
                }else if ( two == null ){
                        fprint( "<td>"+i+"</td><td bgcolor="+a_color+">"+ one +"</td><td>&nbsp;</td>" );
                }else{
                        fprint( "<td>"+i+"</td><td bgcolor="+a_color+">"+ one +"</td><td bgcolor="+b_color+">"+two+ "</td>" );
                }

	/*******
		if( !a_directory && !b_directory && matching ){
			//int change = get_size(a_file) - get_size(b_file);
			int change = get_size(b_file) - get_size(a_file);
			if(change > 0 )
                        	fprint( "<td bgcolor="+green+">+"+change+"</td>" );
			else if(change < 0 )
                        	fprint( "<td bgcolor="+red+">"+change+"</td>" );
			else
                        	fprint( "<td>&nbsp;</td>" );
		}else{
                        fprint( "<td>&nbsp;</td>" );
		}
        ********/


		if( !a_directory && !b_directory && matching ){
			int a_lines =  get_lines(a_file) ;
			int b_lines =  get_lines(b_file) ;
			
			i_lines += a_lines ;
			j_lines += b_lines ;

                        //int change = a_lines - b_lines ;
                        int change = b_lines - a_lines ;
                        if(change > 0 ){
                                fprint( "<td bgcolor="+green+">+"+change+"</td>" );
				double  percent = ((float)change/(float)b_lines ) * 100.0;
				print_graph( percent , "green" );
                        }else if(change < 0 ){
                                fprint( "<td bgcolor="+red+">"+change+"</td>" );
				double  percent = ( ((float)change*-1.0)/(float)a_lines ) * 100.0;
				print_graph( percent ,"red" );
                        }else{
                                fprint( "<td>&nbsp;</td>" );
                                fprint( "<td>&nbsp;</td>" );
			}
                }else{
                        fprint( "<td>&nbsp;</td>" );
			if( one != null && !a_directory ) i_lines += get_lines(a_file)  ;
			if( two != null && !b_directory ) j_lines += get_lines(b_file)  ;
                        fprint( "<td>&nbsp;</td>" );
                }

		

                fprint("</tr>");

        }

        private void  print_graph( double x , String color )
	{ 
			//x = 100.0 - x ;

                fprint2( "<td width=100>" );
		if( x > 1.0 ){
                        fprint2( "<table border=0 width=100 cellpadding=0 cellspacing=0><tr><td bgcolor="+color+" width="+x+"%>" );
                        fprint2( "&nbsp;</td><td>&nbsp;</td></tr></table>" );
		} else {
                        fprint2( "&nbsp;" );
		}
                fprint2( "</td>" );
	}

	private void  pprint_graph( double x , String color )
        {
                //x = 100.0 - x ;

                pprint( "<td width=100>" );
		if( x > 1.0 ){
                        pprint( "<table border=0 width=100 cellpadding=0 cellspacing=0><tr><td bgcolor="+color+" width="+x+"%>" );
                        pprint( "&nbsp;</td><td>&nbsp;</td></tr></table>" );
		} else {
                        pprint( "&nbsp;" );
		}
                pprint( "</td>" );
        }


        private void  parse_missing()
        {
                for( int i = 0  ; i < _a.length ; i++ ){
                        if( _a[i] == 0 ){
                                a_column[ a_count ] = a_files[i] ; a_count++;
                        }else {
                                //print ( "**"+ i +" "+ _a.length +" "+  a_count );
                        }
                }
        }

        private