package logging;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

/**
 * An object of this class can log events on a file. 
 * 
 * @author Majd Kokaly
 *
 */
public class Logger implements Serializable {

	/**
	 * The serialVersionUID is used to universally identify this version of this class. 
	 */
	private static final long serialVersionUID = 8644243290739780799L;
	
	/**
	 * This is the file name that the Logger object stores the events on. 
	 */
	private String fileName;
	
	/**
	 * A FileWriter object used to write on files.
	 */
	private FileWriter fileWriter;
	
	/**
	 * A BufferedWriter object used to buffer the operation of writings
	 */
	private BufferedWriter bufferedWriter;
	
	/**
	 * The logger can log events as Text (textMode = true) or as objects in binary format (textMode = false)
	 *  
	 */
	private boolean textMode = true;
	
	/**
	 * Hard coded Folder name that all Logs should be stored in.
	 */
	final private String LOGS_FOLDER = new String("Logs");
	
	/**
	 * Default constructor
	 * @param fileName is the name of the file that the Logger object will store events on.
	 */
	public Logger(String fileName) {
		super();
		this.setFileName(fileName);
		try {
			this.setFileWriter( new FileWriter( LOGS_FOLDER + "/" + this.getFileName()+".log" , true )) ;
			this.setBufferedWriter(new BufferedWriter(this.getFileWriter()));
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
		
	private FileWriter getFileWriter() {
		return fileWriter;
	}

	private void setFileWriter(FileWriter fileWriter) {
		this.fileWriter = fileWriter;
	}

	private BufferedWriter getBufferedWriter() {
		return bufferedWriter;
	}

	private void setBufferedWriter(BufferedWriter bufferedWriter) {
		this.bufferedWriter = bufferedWriter;
	}

	public String getFileName() {
		return fileName;
	}

	private void setFileName(String fileName) {
		this.fileName = fileName;
	}
	
	public boolean isTextMode() {
		return textMode;
	}

	public void setTextMode(boolean textMode) {
		this.textMode = textMode;
	}
	
	
	/**
	 * 
	 * @param eventString is the event Description
	 */
	public void log(String eventString){
		logEvent(new Event(eventString));
	}
	
	/**
	 * @param ev is the event object to be logged
	 */
	public void logEvent(Event ev) {	
		if (this.isTextMode()) {
			logEventTM(ev);
		} else
			logEventOM(ev);
	}
	
	/**
	 * @param ev is the event object to be logged
	 * This function logs using the Text Mode (TM)
	 */
	private void logEventTM(Event ev) {	
		try {
			this.getBufferedWriter().append(ev.toString()+"\n");
			this.getBufferedWriter().flush();
		} catch (IOException e1) {
			e1.printStackTrace();
		}	
	}
		
	/**
	 * @param ev is the event object to be logged
	 * This function logs using the Object Mode (OM)
	 */
	private void logEventOM(Event ev) {
			ObjectOutputStream output = null;
			try {
				output = new ObjectOutputStream(new FileOutputStream( new File(LOGS_FOLDER + "/" + fileName), true ));
				output.writeObject(ev);
				System.out.println(ev.toString() + " was added.");
			}  catch (IOException e) {
				e.printStackTrace();
			}
			finally {
				if (output != null) {
					try {
						output.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			}
		}
	

	/**
	 * This method prints the events stored in the file that this logger is attached to on the standard output.
	 */
	public void printAllEvents() {
		ObjectInputStream input = null;
		Object o = null;
		try {
			input = new ObjectInputStream(new FileInputStream( new File(this.getFileName()) ));
			while ( true ) {
				o = input.readObject();
				System.out.print( ((Event) o).toString() );		
			}
		}  catch (IOException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e1) {
			e1.printStackTrace();
		}
		finally {
			if (input != null) {
				try {
					input.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

	/**
	 * This method is used to close all the input/output streams
	 */
	public void closeStreams() {
		try {
			// They cannot be null since the setter is private and they are initiated in the constructor.
			this.getFileWriter().close();
			this.getBufferedWriter().close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	
}
