/*
 * Created on Oct 27, 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.profiles;

import java.sql.*;
import java.sql.Connection;
import java.sql.Statement;
import java.util.*;
import java.util.Map;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.greenstone.gsdlas.Constants;
import org.greenstone.gsdlas.database.DatabaseException;
import org.greenstone.gsdlas.database.DatabaseManager;

/**
 * @author schweer
 * 
 * TODO To change the template for this generated type comment go to Window -
 * Preferences - Java - Code Style - Code Templates
 */
public class Subscription implements Comparable {
    private Map map;
    
    private int id;
    private String username;
    private String name;
    private String email;
    private boolean rssNotification;
    private boolean eventsSincePageNotification;
    
    private int numOfNonEqualsPredicates;
    
    public Subscription(Map valueMap) throws DatabaseException, SQLException {
        valueMap = cleanHostCollectionIDs(valueMap);
        map = new TreeMap();
        for (Iterator iter = valueMap.keySet().iterator(); iter.hasNext();) {
            String key = (String) iter.next();
            
            if (!Predicate.isFieldName(key)) {
                continue;
            }
            
            Object value = valueMap.get(key);
            
            if (value instanceof String) {
                Predicate predicate = PredicateFactory.createPredicate(key, (String) value);  
                map.put(key, predicate);

                if (predicate != null && !(predicate instanceof EqualsPredicate)) {
                    numOfNonEqualsPredicates++;
                }
            } else if (value instanceof List) {
                List values = (List)value;
                List predicates = PredicateFactory.createPredicates(key, values);
                map.put(key, predicates);
                for (Iterator iterator = predicates.iterator(); iterator
                        .hasNext();) {
                    Predicate predicate = (Predicate) iterator.next();
                    
                    if (predicate != null && !(predicate instanceof EqualsPredicate)) {
                        numOfNonEqualsPredicates++;
                    }
                }
            } 
            
        }
        
        System.out.println("finished creating predicates");
        
        username = (String) valueMap.get("username");
        name = (String) valueMap.get("subscription_name");
        email = (String) valueMap.get("email");
        rssNotification = valueMap.containsKey("way") && ((List)valueMap.get("way")).contains("rss");
        eventsSincePageNotification = valueMap.containsKey("way") && ((List)valueMap.get("way")).contains("page");
        id = saveToDatabase();
        
        for (Iterator iter = getPredicates().iterator(); iter.hasNext();) {
            Predicate predicate = (Predicate) iter.next();
            if (predicate != null) {
                predicate.addSubscription(id);
            }
        }
    }
    
    /**
     * @param valueMap
     * @return
     */
    private Map cleanHostCollectionIDs(Map valueMap) {
        Map result = new TreeMap();
        Pattern pattern = Pattern.compile("^(.*)\\|(.*)$");
        for (Iterator iter = valueMap.keySet().iterator(); iter.hasNext();) {
            String key = (String) iter.next();
            if (!key.equals(Constants.HOST_COLLECTION_ID_FIELD)) {
                result.put(key, valueMap.get(key));
                continue;
            }
            if (key.equals(Constants.HOST_ID_FIELD)) { continue; }
            
            List values = (List) valueMap.get(key);
            List hostIDs = new Vector();
            List collIDs = new Vector();
            for (Iterator iterator = values.iterator(); iterator.hasNext();) {
                String id = (String) iterator.next();
                Matcher matcher = pattern.matcher(id);
                if (matcher.matches()) {
                    String host = null;
                    String coll = null;
                    try {
                        host = matcher.group(1);
                        coll = matcher.group(2);
                    } catch (IndexOutOfBoundsException e) { /*ignore*/ }
                    if (host != null && host.length() > 0) {
                        if (coll != null && coll.length() > 0) {
                            if (!collIDs.contains(id)) collIDs.add(id);
                        } else {
                            if (!hostIDs.contains(host)) hostIDs.add(host);
                        }
                    }
                }
            }
            result.put(Constants.HOST_ID_FIELD, hostIDs);
            result.put(Constants.COLLECTION_ID_FIELD, collIDs);
        }
        return result;
    }

    public boolean containsKey(Object key) {
        return map.containsKey(key);
    }
    
    public boolean containsValue(Object value) {
        return map.containsValue(value);
    }
    
    public Predicate getPredicate(String field) {
        if (map.get(field) instanceof Predicate) {
            return (Predicate) map.get(field);
        }
        return null;
    }
    
    public List getPredicateList(String field) {
        if (map.get(field) instanceof List) {
            return (List) map.get(field);
        }
        return new Vector();
    }
    
    public Set keySet() {
        return map.keySet();
    }
    
    public Set getPredicates() {
        Set result = new HashSet();
        for (Iterator iter = map.values().iterator(); iter.hasNext();) {
            Object next = iter.next();
            if (next == null) {
                continue;
            } else if (next instanceof Predicate) {
                result.add(next);
            } else if (next instanceof List) {
                result.addAll((Collection) next);
            }
        }
        return result;
    }
    
    /**
     * @return
     */
    public int getId() {
        return id;
    }
    
    public int getNumOfNonEqualsPredicates() {
        return numOfNonEqualsPredicates;
    }
    
    public String toString() {
        StringBuffer result = new StringBuffer("Subscription ");
        result.append(name != null ? name : "(no name) ");
        result.append(" (id ");
        result.append(id);
        result.append("):\n");
        for (Iterator iter = map.keySet().iterator(); iter.hasNext();) {
            String key = (String) iter.next();
            Object value = map.get(key);
            if (value instanceof Predicate) {
                Predicate pred = (Predicate) value;
                result.append(pred);
            }
            if (value instanceof List) {
                List list = (List) value;
                for (Iterator iterator = list.iterator(); iterator.hasNext();) {
                    Predicate pred = (Predicate) iterator.next();
                    result.append(pred);
                    if (iterator.hasNext()) {
                        result.append(" or ");
                    }
                }
            }
            result.append("\n");
        }
        return result.toString();
    }
    
    public boolean equals(Object other) {
        if (other == null || !(other instanceof Subscription)) return false;
        return id == ((Subscription)other).id;
    }

    /* (non-Javadoc)
     * @see java.lang.Comparable#compareTo(java.lang.Object)
     */
    public int compareTo(Object arg0) {
        Subscription other = (Subscription) arg0;
        return new Integer(id).compareTo(new Integer(other.id));
    }
    
    private int saveToDatabase() throws DatabaseException, SQLException {
        Connection conn = DatabaseManager.getInstance().getDatabaseConnection();
        Statement statement = conn.createStatement();
        String sqlString;
        boolean initial = true;
        
        sqlString = "SELECT id FROM subscriptions WHERE name like '" + name + 
        			"' AND email like '" + email + "' AND rss=" + (rssNotification ? 1 : 0) 
        			+ " AND page=" + (eventsSincePageNotification ? 1 : 0) + " AND user like '" +
        			username + "';";
        ResultSet result = statement.executeQuery(sqlString);
        if (result.next()) {
            initial = false;
            this.id = result.getInt("id");
        }
        
        if (initial) {
            sqlString = "INSERT INTO subscriptions (name, email, rss, page, user) " +
            		"VALUES ('" + name + "','" + email + "'," + (rssNotification ? 1 : 0) +
            		"," + (eventsSincePageNotification ? 1: 0)+ ", '" + username + "');";
        } else {
            sqlString = "UPDATE subscriptions SET name='" + name + "', email='" +
            		email + "', rss=" + (rssNotification ? 1 : 0) + ",page=" + 
            		(eventsSincePageNotification ? 1 : 0)+ " WHERE id=" + this.id + ";";
            // cannot change user
        }
        statement.executeUpdate(sqlString);
        
        sqlString = "SELECT id FROM subscriptions WHERE name like '" + name + 
        			"' AND email like '" + email + "' AND rss=" + (rssNotification ? 1 : 0) 
        			+ " AND page=" + (eventsSincePageNotification ? 1 : 0) + " AND user like '" +
        			username + "';";
        System.out.println(sqlString);
        result = statement.executeQuery(sqlString);
        
        if (result.next()) {
            id = result.getInt("id");
        } else {
            throw new DatabaseException("Couldn't save subscription");
        }
        
        if (initial) {
           for (Iterator iter = getPredicates().iterator(); iter.hasNext();) {
               Predicate predicate = (Predicate) iter.next();
               sqlString = "INSERT INTO subs_to_predicates (subscription, predicate) " +
               		"VALUES (" + id + "," + predicate.getID() + ");";
               statement.executeUpdate(sqlString);
           }
        }
        return id;
    }

    /**
     * @return
     */
    public boolean wantsEMailNotification() {
        return email != null && email.length() > 1;
    }

    /**
     * @return
     */
    public String getMailAddress() {
        return email;
    }
    
    public String getName() {
        return name;
    }
    
}