/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 2012 Bull S.A.S.
 * Contact: jonas-team@ow2.org
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 *
 * --------------------------------------------------------------------------
 * $Id$
 * --------------------------------------------------------------------------
 */
package org.ow2.jonas.event.provider.internal;

import org.ow2.jonas.event.provider.api.Event;
import org.ow2.jonas.event.provider.api.EventLevel;
import org.ow2.jonas.event.provider.api.IEventProvider;
import org.ow2.jonas.log.provider.api.ILogProvider;
import org.ow2.util.log.Log;
import org.ow2.util.log.LogFactory;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.logging.Level;

/**
 * {@inheritDoc}
 */
public class EventProviderComponent implements IEventProvider {

    /**
     * Path to the JONAS_BASE
     */
    public static final String JONAS_BASE = System.getProperty("jonas.base");

    /**
     * Conf directory
     */
    public static final String CONF_DIR = "conf";

    /**
     * The event provider properties file
     */
    public static final String EVENT_PROVIDER_FILE = "event-provider.properties";

    /**
     * The logger
     */
    public static final Log logger = LogFactory.getLog(EventProviderComponent.class);

    /**
     * INFO Event window token
     */
    public static final String INFO_EVENT_WINDOW_TOKEN = "jonas.service.event.info.window";

    /**
     * FINE event window token
     */
    public static final String FINE_EVENT_WINDOW_TOKEN = "jonas.service.event.fine.window";

    /**
     * FINEST Event window token
     */
    public static final String FINEST_EVENT_WINDOW_TOKEN = "jonas.service.event.finest.window";

    /**
     * FINER Event window token
     */
    public static final String FINER_EVENT_WINDOW_TOKEN = "jonas.service.event.finer.window";

    /**
     * WARNING Event window token
     */
    public static final String WARNING_EVENT_WINDOW_TOKEN = "jonas.service.event.warning.window";

    /**
     * SEVERE Event window token
     */
    public static final String SEVERE_EVENT_WINDOW_TOKEN = "jonas.service.event.severe.window";

    /*
     * EXCEPTION Event window token
     */
    public static final String EXCEPTION_EVENT_WINDOW_TOKEN = "jonas.service.event.exception.window";

    /**
     * Default window value of events to keep in memory
     */
    public static final int DEFAULT_WINDOW_VALUE = 25;

    /**
     * The {@link ILogProvider}
     */
    private ILogProvider logProvider;

    /**
     * Event provider properties
     */
    private Properties eventProviderProperties;
    
    /*
     * List of info events
     */
    private List<Event> infoEvents;

    /**
     * List of finest events
     */
    private List<Event> finestEvents;

    /**
     * List of fine events
     */
    private List<Event> fineEvents;

    /**
     * List of finer events
     */
    private List<Event> finerEvents;

    /**
     * List of warning events
     */
    private List<Event> warningEvents;

    /**
     * List of sever events
     */
    private List<Event> severEvents;

    /**
     * List of exception events
     */
    private List<Event> exceptionEvents;
    
    /**
     * Default constructor
     */
    public EventProviderComponent() {
        this.infoEvents = new ArrayList<Event>();
        this.exceptionEvents = new ArrayList<Event>();
        this.warningEvents = new ArrayList<Event>();
        this.finerEvents = new ArrayList<Event>();
        this.finestEvents = new ArrayList<Event>();
        this.fineEvents = new ArrayList<Event>();
        this.severEvents = new ArrayList<Event>();
    }

    /**
     * Validate method
     */
    public void start() {
        File eventProviderFile = new File(new File(JONAS_BASE, CONF_DIR), EVENT_PROVIDER_FILE);
        this.eventProviderProperties = new Properties();
        try {
            this.eventProviderProperties.load(new FileInputStream(eventProviderFile));
        } catch (IOException e) {
            logger.error("Cannot load properties of file " + eventProviderFile.getAbsolutePath(), e);
        }
    }

    /**
     * @param eventLevels the list of {@link EventLevel} associated to events to retrieve
     * @return the list of {@link Event}
     */
    public List<Event> getEvents(final List<EventLevel> eventLevels) {
        List<Event> events = new ArrayList<Event>();

        if (eventLevels.contains(EventLevel.ALL)) {
            events.addAll(this.exceptionEvents);
            events.addAll(this.infoEvents);
            events.addAll(this.severEvents);
            events.addAll(this.warningEvents);
            events.addAll(this.fineEvents);
            events.addAll(this.finerEvents);
            events.addAll(this.finestEvents);
        } else {
           for (EventLevel eventLevel: eventLevels) {
               switch (eventLevel) {
                   case EXCEPTION:
                       events.addAll(this.exceptionEvents);
                       break;
                   case INFO:
                       events.addAll(this.infoEvents);
                       break;
                   case SEVERE:
                       events.addAll(this.severEvents);
                       break;
                   case WARNING:
                       events.addAll(this.warningEvents);
                       break;
                   case FINE:
                       events.addAll(this.fineEvents);
                       break;
                   case FINER:
                       events.addAll(this.finerEvents);
                       break;
                   case FINEST:
                       events.addAll(this.finestEvents);
                       break;
               }
           }
        }
        
        return events;
    }

    /**
     * @param eventLevel The {@link EventLevel}
     * @return its associated {@link Level}
     */
    public Level getLevel(final EventLevel eventLevel) {
        if (Level.INFO.toString().equals(eventLevel.toString())) {
            return Level.INFO;
        } else if (Level.FINEST.toString().equals(eventLevel.toString())) {
            return Level.FINEST;
        } else if (Level.FINER.toString().equals(eventLevel.toString())) {
            return Level.FINER;
        } else if (Level.FINE.toString().equals(eventLevel.toString())) {
            return Level.FINE;
        } else if (Level.SEVERE.toString().equals(eventLevel.toString())) {
            return Level.SEVERE;
        } else if (Level.WARNING.toString().equals(eventLevel.toString())) {
            return Level.WARNING;
        } else {
            return null;
        }
    }

    /**
     * @param event An {@link Event} to add
     */
    public void addEvent(final Event event) {
        if (event != null) {
            if (EventLevel.INFO.toString().equals(event.getType())) {
                this.infoEvents.add(event);
                checkWindowValidity(this.eventProviderProperties.getProperty(INFO_EVENT_WINDOW_TOKEN), this.infoEvents);
            } else if (EventLevel.EXCEPTION.toString().equals(event.getType())) {
                this.exceptionEvents.add(event);
                checkWindowValidity(this.eventProviderProperties.getProperty(EXCEPTION_EVENT_WINDOW_TOKEN), this.exceptionEvents);
            } else if (EventLevel.SEVERE.toString().equals(event.getType())) {
                this.severEvents.add(event);
                checkWindowValidity(this.eventProviderProperties.getProperty(SEVERE_EVENT_WINDOW_TOKEN), this.severEvents);
            } else if (EventLevel.FINE.toString().equals(event.getType())) {
                this.fineEvents.add(event);
                checkWindowValidity(this.eventProviderProperties.getProperty(FINE_EVENT_WINDOW_TOKEN), this.fineEvents);
            } else if (EventLevel.FINER.toString().equals(event.getType())) {
                this.finerEvents.add(event);
                checkWindowValidity(this.eventProviderProperties.getProperty(FINER_EVENT_WINDOW_TOKEN), this.finerEvents);
            } else if (EventLevel.FINEST.toString().equals(event.getType())) {
                this.finestEvents.add(event);
                checkWindowValidity(this.eventProviderProperties.getProperty(FINEST_EVENT_WINDOW_TOKEN), this.finestEvents);
            } else if (EventLevel.WARNING.toString().equals(event.getType())) {
                this.warningEvents.add(event);
                checkWindowValidity(this.eventProviderProperties.getProperty(WARNING_EVENT_WINDOW_TOKEN), this.warningEvents);
            }
        }
    }

    /**
     * Check window validity of an event
     * @param value Window value
     * @param events List of events of the same type
     */
    private void checkWindowValidity(final String value, final List<Event> events) {
        Integer window = null;
        if (value != null) {
            window = Integer.valueOf(value);
        }
        if (window == null) {
            window = DEFAULT_WINDOW_VALUE;
        }
        int diff = events.size() - window;
        if (diff > 0) {
            for (int i = 0;i < diff; i++) {
                events.remove(0);
            }
        }
    }

    /**
     * @param logProvider The {@link ILogProvider} to bind
     */
    public void bindLogProvider(final ILogProvider logProvider) {
        this.logProvider = logProvider; 
    }

    /**
     * @param logProvider The {@link ILogProvider} to unbind
     */
    public void unbindLogProvider(final ILogProvider logProvider) {
        this.logProvider = null;
    }
}
