
package de.ugoe.cs.autoquest.htmlmonitor;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.text.DecimalFormat;

/**
 * <p>
 * comment
 * </p>
 * 
 * @author Patrick Harms
 * @version 1.0
 * 
 */
public class HtmlMonitorOutputWriter implements HtmlMonitorComponent, HtmlMonitorMessageListener {
    
    /**
     * 
     */
    private static final int MAXIMUM_LOG_FILE_SIZE = 10000000;

    /**
     * 
     */
    private static final String DEFAULT_LOG_FILE_BASE_DIR = "logs";

    /**
     * 
     */
    private File logFileBaseDir;

    /**
     *
     */
    private String clientId;

    /**
     *
     */
    private File logFile;

    /**
     * <p>
     * Writer for logging events.
     * </p>
     */
    private PrintWriter outputWriter;

    /**
     * 
     */
    private long lastUpdate;

    /**
     * <p>
     * 
     * </p>
     * 
     * @param outputWriter
     *            writer for the logged information
     */
    public HtmlMonitorOutputWriter(String logFileBaseDir, String clientId) {
        if (logFileBaseDir == null) {
            this.logFileBaseDir = new File(DEFAULT_LOG_FILE_BASE_DIR);
        }
        else {
            this.logFileBaseDir = new File(logFileBaseDir);
        }
        
        this.clientId = clientId;
        
        lastUpdate = System.currentTimeMillis();
    }

    /* (non-Javadoc)
     * @see de.ugoe.cs.autoquest.htmlmonitor.HtmlMonitorComponent#init()
     */
    @Override
    public synchronized void init() throws HtmlMonitorException {
        if (outputWriter != null) {
            throw new IllegalStateException("already initialized. Call close() first");
        }
        
        synchronized (HtmlMonitorOutputWriter.class) {
            try {
                File clientLogDir = new File(logFileBaseDir, clientId);
            
                if (!clientLogDir.exists()) {
                    clientLogDir.mkdirs();
                }
                else if (!clientLogDir.isDirectory()) {
                    throw new HtmlMonitorException("client log file directory " + clientLogDir +
                                                   " already exists as a file");
                }
                
                logFile = new File(clientLogDir, getLogFileName(-1));
                
                if (logFile.exists()) {
                    rotateLogFile();
                }
            
                createLogWriter();
            }
            catch (IOException e) {
                throw new HtmlMonitorException("could not open logfile " + logFile, e);
            }
        }
        
        lastUpdate = System.currentTimeMillis();
    }

    /**
     * <p>
     * TODO: comment
     * </p>
     *
     * @param logFileIndex2
     * @return
     */
    private String getLogFileName(int index) {
        String result = "htmlmonitor_" + clientId;
        
        if (index >= 0) {
            result += "_" + new DecimalFormat("000" ).format(index);
        }
        
        result += ".log";
        
        return result;
    }

    /* (non-Javadoc)
     * @see de.ugoe.cs.autoquest.htmlmonitor.HtmlMonitorComponent#start()
     */
    @Override
    public synchronized void start() throws IllegalStateException, HtmlMonitorException {
        lastUpdate = System.currentTimeMillis();
    }

    /* (non-Javadoc)
     * @see de.ugoe.cs.autoquest.htmlmonitor.HtmlMonitorMessageListener#handleMessage(de.ugoe.cs.quest.htmlmonitor.HtmlClientInfos, de.ugoe.cs.quest.htmlmonitor.HtmlEvent[])
     */
    @Override
    public void handleMessage(HtmlClientInfos clientInfos, HtmlEvent[] events) {
        if (outputWriter == null) {
            throw new IllegalStateException("not initialized. Call init() first");
        }
        
        for (HtmlEvent event : events) {
            dumpEvent(event);
        }
        
        outputWriter.flush();
        
        try {
            considerLogRotate();
        }
        catch (IOException e) {
            throw new IllegalStateException("could not perform log rotation: " + e, e);
        }
        
        lastUpdate = System.currentTimeMillis();
    }

    /**
     * <p>
     * TODO: comment
     * </p>
     *
     * @param event
     */
    private void dumpEvent(HtmlEvent event) {
        dumpString(event.getClientInfos().getClientId());
        outputWriter.print(' ');
        dumpString(event.getTime().toString());
        outputWriter.print(' ');
        dumpString(event.getClientInfos().getTitle());
        outputWriter.print(' ');
        dumpString(event.getClientInfos().getUrl().toString());
        outputWriter.print(' ');
        dumpString(event.getClientInfos().getUserAgent());
        outputWriter.print(' ');
        dumpString(event.getEventType());
        outputWriter.print(' ');
        dumpString(event.getPath());

        if (event.getCoordinates() != null) {
            outputWriter.print(' ');
            
            StringBuffer value = new StringBuffer();
            for (int i = 0; i < event.getCoordinates().length; i++) {
                if (i > 0) {
                    value.append(',');
                }
                value.append(event.getCoordinates()[i]);
            }
            
            dumpString(value.toString());
        }

        if (event.getKey() != null) {
            outputWriter.print(' ');
            dumpString(event.getKey().toString());
        }
            
        if (event.getScrollPosition() != null) {
            outputWriter.print(' ');
            dumpString(event.getScrollPosition().toString());
        }
            
        outputWriter.println();
    }

    /**
     * <p>
     * TODO: comment
     * </p>
     *
     * @param clientId2
     */
    private void dumpString(String str) {
        outputWriter.print('"');
        outputWriter.print(str.replaceAll("\\\\", "\\\\").replaceAll("\\\"", "\\\""));
        outputWriter.print('"');
    }

    /**
     * <p>
     * TODO: comment
     * </p>
     *
     */
    private synchronized void considerLogRotate() throws IOException {
        if (logFile.length() > MAXIMUM_LOG_FILE_SIZE) {
            closeLogWriter();
            rotateLogFile();
            createLogWriter();
        }
    }

    /**
     * <p>
     * TODO: comment
     * </p>
     *
     */
    private void rotateLogFile() {
        File clientLogDir = logFile.getParentFile();
        File checkFile;

        int logFileIndex = -1;
        do {
            logFileIndex++;
            
            checkFile = new File(clientLogDir, getLogFileName(logFileIndex));
        }
        while (checkFile.exists());
    
        logFile.renameTo(checkFile);
        logFileIndex++;
        logFile = new File(clientLogDir, getLogFileName(-1));
    }

    /**
     * <p>
     * TODO: comment
     * </p>
     *
     */
    private void createLogWriter() throws IOException {
        FileOutputStream fis = new FileOutputStream(logFile);
        outputWriter = new PrintWriter(new OutputStreamWriter(fis, "UTF-8"));
    }

    /**
     * <p>
     * TODO: comment
     * </p>
     *
     */
    private void closeLogWriter() {
        if (outputWriter != null) {
            outputWriter.flush();
            outputWriter.close();
            outputWriter = null;
        }
    }

    /* (non-Javadoc)
     * @see de.ugoe.cs.autoquest.htmlmonitor.HtmlMonitorComponent#stop()
     */
    @Override
    public synchronized void stop() {
        closeLogWriter();
        rotateLogFile();

        lastUpdate = System.currentTimeMillis();
    }

    /**
     * <p>
     * TODO: comment
     * </p>
     *
     * @return
     */
    public long getLastUpdate() {
        return lastUpdate;
    }
}
