/*
 * Created on Nov 23, 2004
 * Copyright (C) Andrea Schweer, 2004
 *
 * This file is part of the Greenstone Alerting Service.
 * Refer to the COPYING file in the base directory of this package
 * for licensing information.
 */
package org.greenstone.gsdlas;

import java.sql.*;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.Date;

import org.greenstone.gsdlas.database.DatabaseException;
import org.greenstone.gsdlas.database.DatabaseManager;
import org.greenstone.gsdlas.profiles.Subscription;

/**
 * @author schweer
 *
 * TODO To change the template for this generated type comment go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
public class EventStore {

    private static EventStore instance;
    
    private EventStore() {
    }
    
    /**
     * @return
     */
    public static EventStore getInstance() {
        if (instance == null) instance = new EventStore();
        return instance;
    }
    
    public void add(Map event, Set subscriptions) throws DatabaseException, SQLException, ParseException {
        Connection conn = DatabaseManager.getInstance().getDatabaseConnection();
        Statement statement = conn.createStatement();

        int eventID = saveEventToDatabase(event);
        
        for (Iterator iter = subscriptions.iterator(); iter.hasNext();) {
            Subscription sub = (Subscription) iter.next();
            int subID = sub.getId();
            String sqlString = "INSERT INTO events_to_subs (event,subscription) " +
            		"VALUES (" + eventID + "," + subID + ");";
            statement.executeUpdate(sqlString);
        }
    }

    /**
     * @param event
     * @return
     * @throws DatabaseException
     * @throws SQLException
     * @throws ParseException
     */
    private int saveEventToDatabase(Map event) throws DatabaseException, SQLException, ParseException {
        String eventString = mapAsString(event);
        String timestamp = (String) event.get("timestamp");
        timestamp = convertToSQLDatetime(timestamp);
        
        Connection conn = DatabaseManager.getInstance().getDatabaseConnection();
        Statement statement = conn.createStatement();
        String query = "SELECT id FROM events WHERE timestamp = " + timestamp + 
        	" AND content = '" + eventString + "';";
        ResultSet results = statement.executeQuery(query);
        if (results.next()) {
            return results.getInt("id");
        }
        
        String insert = "INSERT INTO events (timestamp,content) " +
        		"VALUES (" + timestamp + ",'" + eventString + "');"; 
        statement.executeUpdate(insert);
        results = statement.executeQuery(query);
        if (results.next()) {
            return results.getInt("id");
        }
        throw new DatabaseException("couldn't save event to database");
    }

    /**
     * converts a timestamp from an event to a format recognised by MySQL
     * @param timestamp
     * @return
     * @throws ParseException
     */
    private String convertToSQLDatetime(String timestamp) throws ParseException {
        Date date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z").parse(timestamp);
        return new SimpleDateFormat("yyyyMMddHHmmss").format(date);
    }

    /**
     * @param event
     */
    private String encodeSpecialChars(String string) {
        String encodedString = string.replaceAll("&", "&amp;");
        encodedString = encodedString.replaceAll("<", "&lt;");
        encodedString = encodedString.replaceAll(">", "&gt;");
        encodedString = encodedString.replaceAll("\'", "&apos;");
        encodedString = encodedString.replaceAll("\"", "&quot;");
        return encodedString;
    }

    /**
     * @param subscriptionID
     * @return
     * @throws DatabaseException
     * @throws SQLException
     */
    public Set getEvents(Integer subscriptionID) throws DatabaseException, SQLException {
        Set result = new HashSet(); // can't use TreeSet, because we put Maps in there, and Map isn't instanceof Comparable
        Connection conn = DatabaseManager.getInstance().getDatabaseConnection();
        Statement statement = conn.createStatement();
        String query = "SELECT content " +
        		"FROM events e JOIN events_to_subs ets ON (e.id = ets.event) " +
        		"WHERE ets.subscription = " + subscriptionID.intValue() + ";";
        ResultSet results = statement.executeQuery(query);
        while (results.next()) {
            Map event = stringAsMap(results.getString("content"));
            result.add(event);
        }
        return result;
    }

    private String mapAsString(Map map) {
        StringBuffer buffer = new StringBuffer();
        for (Iterator iter = map.keySet().iterator(); iter.hasNext();) {
            String key = (String) iter.next();
            String value = (String) map.get(key);
            String encodedValue = encodeSpecialChars(value);
            buffer.append(key);
            buffer.append(">"); // > has been encoded to &gt; in the original String
            buffer.append(encodedValue);
            if (iter.hasNext()) {
                buffer.append(">>");
            }
        }
        return buffer.toString();
    }
    
    private Map stringAsMap(String string) {
        Map map = new HashMap();
        String[] lines = string.split(">>");
        for (int i = 0; i < lines.length; i++) {
            String line = lines[i];
            String[] columns = line.split(">");
            String key = columns[0];
            String value = null;
            if (columns.length > 1) {
                value = columns[1];
            }
            map.put(key, value);
        }
        return map;
    }
    
    public void cleanUp() {
        // TODO implement method 
        // delete all events that are too old (?)
        // delete all events that don't match any subscriptions
    }
}
