/**
 * The contents of this file are subject to the license and copyright
 * detailed in the LICENSE and NOTICE files at the root of the source
 * tree and available online at
 *
 * http://www.dspace.org/license/
 */
package org.dspace.usage;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;

import org.dspace.core.Constants;
import org.dspace.services.ConfigurationService;
import org.dspace.services.model.Event;
import org.dspace.utils.DSpace;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Serialize {@link UsageEvent} data to a file as Tab delimited. In dspace.cfg
 * specify the path to the file as the value of
 * {@code usageEvent.tabFileLogger.file}.  If that path is not absolute, it
 * will be interpreted as relative to the directory named in {@code log.dir}.
 * If no name is configured, it defaults to "usage-events.tsv".  If the file is
 * new or empty, a column heading record will be written when the file is opened.
 *
 * @author Mark H. Wood
 * @author Mark Diggory
 */
public class TabFileUsageEventListener
    extends AbstractUsageEventListener {
    /**
     * log category.
     */
    private static final Logger errorLog = LoggerFactory
        .getLogger(TabFileUsageEventListener.class);

    /**
     * ISO 8601 Basic string format for record timestamps.
     */
    private static final SimpleDateFormat dateFormat = new SimpleDateFormat(
        "yyyyMMdd'T'HHmmssSSS");

    /**
     * File on which to write event records.
     */
    private PrintWriter eventLog;

    /**
     * Is this instance initialized?
     */
    private boolean initialized = false;

    /**
     * Set up a usage event listener for writing TSV records to a file.
     */
    private void init() {
        ConfigurationService configurationService
            = new DSpace().getConfigurationService();

        String logPath = configurationService.getPropertyAsType(
            "usageEvent.tabFileLogger.file",
            "usage-events.tsv");

        String logDir = null;
        if (!new File(logPath).isAbsolute()) {
            logDir = configurationService.getProperty("log.report.dir");
        }

        File logFile = new File(logDir, logPath);
        try {
            eventLog = new PrintWriter(new OutputStreamWriter(
                new FileOutputStream(logFile, true)));
            errorLog.debug("Writing to {}", logFile.getAbsolutePath());
        } catch (FileNotFoundException e) {
            errorLog.error("{} cannot open file, will not log events:  {}",
                           TabFileUsageEventListener.class.getName(),
                           e.getMessage());
            throw new IllegalArgumentException("Cannot open event log file", e);
        }

        if (logFile.length() <= 0) {
            eventLog.println("date"
                                 + '\t' + "event"
                                 + '\t' + "objectType"
                                 + '\t' + "objectId"
                                 + '\t' + "sessionId"
                                 + '\t' + "sourceAddress"
                                 + '\t' + "eperson");
        }

        initialized = true;
    }

    @Override
    public synchronized void receiveEvent(Event event) {
        if (!initialized) {
            init();
        }

        if (errorLog.isDebugEnabled()) {
            errorLog.debug("got: {}", event.toString());
        }

        if (!(event instanceof UsageEvent)) {
            return;
        }

        if (null == eventLog) {
            return;
        }

        UsageEvent ue = (UsageEvent) event;

        eventLog.append(dateFormat.format(new Date()))
                .append('\t').append(ue.getName()) // event type
                .append('\t').append(Constants.typeText[ue.getObject().getType()])
                .append('\t').append(ue.getObject().getID().toString())
                .append('\t').append(ue.getRequest().getSession().getId())
                .append('\t').append(ue.getRequest().getRemoteAddr());

        String epersonName = (null == ue.getContext().getCurrentUser()
            ? "anonymous"
            : ue.getContext().getCurrentUser().getEmail());
        eventLog.append('\t').append(epersonName);

        eventLog.println();
        eventLog.flush();
    }
}
