/*                          
 * mBench: The Open Source Micro Benchmark Tool 
 *                                             
 * Distributable under GPL license. 
 * See terms of license at gnu.org.                 
 *
 * Copyright (C) 2005 Sumisho Computer Systems Corp.
 */
package jp.co.scs.mbench;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.LinkedList;

/**
 *      System log handler (writer)
 * 
 *      @author Tetsuro Ikeda
 */
public class LogWriter extends Thread {
    /**
     *  Log levelFDebug
     */
    public static final int DEBUG = 0;
    
    /**
     *  Log levelFInfo
     */
    public static final int INFO = 1;
    
    /**
     *  Log levelFError
     */
    public static final int ERROR = 2;
    
    /**
     *  Label of Debug
     */
    protected static String debug_label = "[DEBUG]";
    
    /**
     *  Label of Info
     */
    protected static String info_label = "[INFO]";
    
    /**
     *  Label of Error
     */
    protected static String error_label = "[ERROR]";
    
    /**
     *  Level of this log
     */
    private int logLevel = INFO;
    
    /**
     *  Name of log file name
     */
    private String logName = null;
    
    /**
     *  BufferedWriter
     */
    private BufferedWriter writer = null;
    
    /**
     *  Flag for initialize
     */
    private boolean isInitialized = false;
    
    /**
     *  Flag for running status
     */
    private boolean isRunning = false;
    
    /**
     *  List of log
     */
    private LinkedList logQueue = new LinkedList();
    
    /**
     *  command for "remove"
     */
    private int remove = 0;

    /**
     *  command for "add"
     */
    private int add = 1;
    
    /**
     *  Creates a log writer instance with the log file name.
     * 
     *  @param logName log file name
     */
    public LogWriter(String logName) {
        this.logName = logName;
    }    
    
    /**
     *  Gets the log file name.
     * 
     *  @return log file name
     */
    public String getLogName() {
        return this.logName;
    }
    
    /**
     *  Sets the log file name.
     *
     *  @param logName log file name
     */
    public void setLogName(String logName) {
        this.logName = logName;
    }
    
    /**
     *  Gets the level of log.
     * 
     *  @return level of log
     */
    public int getLogLevel() {
        return this.logLevel;
    }
    
    /**
     *  Sets the level of log.
     * 
     *  @param logLevel level of log
     */
    public void setLogLevel(int logLevel) {
        this.logLevel = logLevel;
    }
    
    /**
     *  Gets the flag for initialize.
     *
     *  @return flag for initialize
     */
    public boolean isInitialized() {
        return this.isInitialized;
    }

    /**
     *  Gets the flag for initialize.
     *
     *  @return flag for initialize
     */
    public boolean isRunning() {
        return this.isRunning;
    }

    /**
     *  Gets the size of log list.
     *
     *  @return size of log list.
     */
    public int getLogQueueSize() {
        return this.logQueue.size();
    }
    
    /**
     *  initializes
     * 
     *  @throws BenchmarkTerminateException benchmark should be terminated
     */
    public void init() throws BenchmarkTerminateException {
        try {
            this.writer = new BufferedWriter(new FileWriter(this.logName));
            this.start();
            this.isInitialized = true;
        } catch (IOException ex) {
            ex.printStackTrace();
            throw new BenchmarkTerminateException(ex);
        }
    }
    
    /**
     *  Add system log to the queue.
     * 
     *  @param level log level for this log
     *  @param desc matter of log
     */
    public void write(int level, String desc) {
        if (level < this.logLevel) {
            return;
        }
        Date registTime = Calendar.getInstance().getTime();
        manipulateQueue(this.add, new LogHolder(registTime, level, desc));
    }
    
    /**
     *  Write log to the file, stdout then repeat.
     */
    public void run() {
        try {
            this.isRunning = true;
            while (this.isRunning || this.logQueue.size() > 0) {
                Object obj = manipulateQueue(this.remove, null);
                if (obj != null) {
                    LogHolder holder = (LogHolder) obj;
                    String pattern = "HH:mm:ss:SS";
                    SimpleDateFormat dateFormat = new SimpleDateFormat(pattern);
                    String timeHeader =  "["
                        + dateFormat.format(holder.getDate()) + "]";
                    if (holder.getLogLevel() == INFO) {
                        System.out.println(timeHeader
                            + info_label + holder.getLogDesc());
                        this.writer.write(timeHeader
                            + info_label + holder.getLogDesc() + "\n");
                    } else if (holder.getLogLevel() == ERROR) {
                        System.err.println(timeHeader
                            + error_label + holder.getLogDesc());
                        this.writer.write(timeHeader
                            + error_label + holder.getLogDesc() + "\n");
                    } else if (holder.getLogLevel() == DEBUG) {
                        System.out.println(timeHeader
                            + debug_label + holder.getLogDesc());
                        this.writer.write(timeHeader
                            + debug_label + holder.getLogDesc() + "\n");
                    }
                // wait if queue is empty
                } else {
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException ex) {
                        // ignore
                    }
                }
            }
        } catch (IOException ex) {
            this.isRunning = false;
            this.isInitialized = false;
            ex.printStackTrace();
        }
    }
    
    /**
     *  clean
     */
    public void clean() {
        try {
            // wait if queue isn't empty yet
            while (this.isInitialized && this.logQueue.size() > 0) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException ex) {
                    // ignore
                }
            }
            // wait for the writing a last log
            try {
                Thread.sleep(1000);
            } catch (InterruptedException ex) {
                // ignore
            }
            this.isRunning = false;
            this.writer.close();
        } catch (IOException ex) {
            ex.printStackTrace();
        } finally {
            this.isInitialized = false;
        }
    }
    
    /**
     *  add log to the queue or remove log from the queue.
     *  this method is synchronized.
     *  If the command isn't "remove" or is "remove" but queue is empty,
     *  null is returned.
     * 
     *  @param command command for manipulate, 1 for add, 0 for remove
     *  @param obj log
     *  @return log or null
     */
    private synchronized Object manipulateQueue(int command, Object obj) {
        if (command == this.add) {
            this.logQueue.addLast(obj);
            return null;
        } else if (command == this.remove) {
            if (this.logQueue.size() > 0) {
                return this.logQueue.removeFirst();
            } else {
                return null;
            }
        }
        return null;
    }
}
