package edu.vt.marian.common;

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



/** Class name: Debug
	<P>Class description: As the name said, this class is purely for the convienece
		of debugging a big system, user don't need to recompile the
		system to change the debug configuration (where to print out 
		message, where not), user even don't need to rerun the system.
	<P>Author: Jianxin Zhao
	<P>Finished time: June 2, 1998
	<P>Known bugs: none
	<P>Platform: jdk1.1.5 under UNIX

	<P>Time: June 22, 1998
	<P>Modified by: Jianxin Zhao	
	<P>Description: add a new method -- getTraceFile() which will return the current
		trace file name, this will make this class easier to use (more flexible). 
*/
  
public class Debug
{

	/** this vector store the strings whose values are set to ON/true
	*/
	private Vector debug_information;
	
	/** this variable indicate whether or not user want to change debug configuration on the fly
	*/
	private boolean online_flag;
	
	/** this is the debug configuration file
	*/
	private File debug_file;

	/** this is the file to store trace information
	*/
	private PrintWriter trace_pw;

	/** this is the trace file name
	*/
	private String trace_file_name;

	/** this object will be used to measure the performance of a system, add it here will
		result in smallest change in the system
	*/
	public PerformanceMeasurement pm = null;


	/** This constructor will create a Debug object based on the information 
		in the specified file, also the stream for trace is set to according
		to the trace file name and append mode 
	*/
	public Debug(String debug_filename, String trace_filename, boolean append)
	{
		String line;
		String s;
		String s1;
		StringTokenizer st;
		BufferedReader br;

		// initialize data members
		init();

		// read debug configurations from the specified file
		debug_file = new File(debug_filename);
		debug_information = new Vector();
		
		try
		{
			FileReader fr = new FileReader(debug_file);
			br = new BufferedReader(fr);
			line = br.readLine();
			while (line != null)
			{
				st = new StringTokenizer(line);
				if (st.hasMoreTokens())  // in case an empty line is encoutered
				{
					s = st.nextToken();
					if (! s.startsWith("#")) // lines begin with "#" will be ignored
					{
						if (s.equals("Online_Flag")) // process online flag
						{
							s1 = st.nextToken();
							if (s1.equals("ON"))
							{
								SetOnlineFlag();
							}
							else
							{
								UnsetOnlineFlag();
							}
						}
						else // process variant debug configuratons
						{
							s1 = st.nextToken();
							if (s1.equals("ON"))
							{
								SetValue(s);
							}
							else
							{
								UnsetValue(s);
							}
						}
					}  
				}
				line = br.readLine();
			} // end while
			br.close();
		}
		catch (IOException e2)
		{
			System.err.println("error reading data from debug file");
		}

		// creat the trace file stream
		trace_file_name = trace_filename;
		File trace_file = new File(trace_file_name);
		FileOutputStream trace_writer = null;
		try
		{
			trace_writer = new FileOutputStream(trace_file.toString(), append);		
		}
		catch (IOException e3)
		{
			System.err.println("error opening trace file to write");
		}
		trace_pw = new PrintWriter(trace_writer);

		// finally create the object to measure the performance of the system
		pm = new PerformanceMeasurement();
	}


	/** this method may change the trace file name, if append is true the new trace file
		will be appended instead of rewrite
	*/
	public void setTraceFile(String trace_filename, boolean append)
	{
		trace_file_name = trace_filename;
		File trace_file = new File(trace_file_name);
		FileOutputStream trace_writer = null;
		try
		{
			trace_writer = new FileOutputStream(trace_file.toString(), append);		
		}
		catch (IOException e3)
		{
			System.err.println("error opening trace file to write");
		}
		trace_pw = new PrintWriter(trace_writer);
	}


	/** this method will return the current trace file name
	*/
	public String getTraceFile()
	{
		return trace_file_name;
	}


	/** this method will dump the data to the current trace file
	*/
	public synchronized void dumpTrace(String data) 
	{
		trace_pw.println(data);
		trace_pw.flush();
	}


	/** This method will set the value to true for the specified name
	*/
	private void SetValue(String name)
	{
		String temp;
		int i;

		for (i = 0; i < debug_information.size(); i++)
		{
			temp = (String) debug_information.elementAt(i);
			if (temp.equals(name)) // this is a duplicated set, do nothing and return
			{
				return;
			}
		}

		// This is a new name, add it to the vector
		debug_information.addElement(name);
	}


	/** This method will set the value to false for the specified name
	*/
	private void UnsetValue(String name)
	{
		String temp;
		int i;

		for (i = 0; i < debug_information.size(); i++)
		{
			temp = (String) debug_information.elementAt(i);
			if (temp.equals(name))  // the name is in the vector, delete it
			{
				debug_information.removeElementAt(i);
				return;
			}
		}

		// The name is not in the vector at all, do nothing and return
		return;
	}


	/** This method will return the value of the specified name, if there is
		no such name in the object false will be returned
	*/
	public boolean GetValue(String name)
	{
		if (GetOnlineFlag()) // user want to change debug configuration on the fly, so read it from the config file 
		{
			String line;
			String s;
			String s1;
			StringTokenizer st;
			BufferedReader br;
			boolean return_value;

			// in case there is no such name in the file, return false
			return_value = false;

			try
			{
				FileReader fr = new FileReader(debug_file);
				br = new BufferedReader(fr);
				line = br.readLine();
				while (line != null)
				{
					st = new StringTokenizer(line);
					if (st.hasMoreTokens())  // in case an empty line is encoutered
					{
						s = st.nextToken();
						if (! s.startsWith("#")) // lines begin with "#" will be ignored
						{
							if (s.equals(name)) // find a name match from the file
							{
								s1 = st.nextToken();
								if (s1.equals("ON"))  // the name is set to "ON"
								{
									return_value = true;
								}
								else // none "ON" is regarded as OFF
								{
									return_value = false;
								}
							}
						}
					}
					line = br.readLine();
				} // end while
				br.close();
			}
			catch (IOException e2)
			{
				System.err.println("error reading data from srm file");
			}

			// the last appearance of the name in the file will be effective, or false will be returned
			return return_value;
		}

		// return the value stored upon the initialization 
		String temp;
		int i;

		for (i = 0; i < debug_information.size(); i++)
		{
			temp = (String) debug_information.elementAt(i);
			if (temp.equals(name)) // the name is in the vector
			{
				return true;
			}
		}

		// The name is not in the vector
		return false;
	}


	/** This method will set the value to true for the online flag of this
		object, if online flag is true, everytime the GetValue is called,
		this object will read the specified debug config file, so the system
		need not to be rerun to change such kind of configuration, of course
		the cost is lower performance since there are many file operations
	*/
	private void SetOnlineFlag()
	{
		online_flag = true;
	}


	/** This method will set the value to false for the online flag of this
		object
	*/
	private void UnsetOnlineFlag()
	{
		online_flag = false;
	}


	/** This method will return the value of the online flag, the value is 
		false upon initialization
	*/
	private boolean GetOnlineFlag()
	{
		return online_flag;
	}


	/** This method will emptify the data members of this class, called
		in the constructor
	*/
	private void init()
	{
		online_flag = false;
		debug_information = null;
		debug_file = null;
		trace_pw = null;
	}
}