/*
 * Copyright 1996-2011 Niclas Hedhman.
 *
 * Licensed  under the  Apache License,  Version 2.0  (the "License");
 * you may not use  this file  except in  compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed  under the  License is distributed on an "AS IS" BASIS,
 * WITHOUT  WARRANTIES OR CONDITIONS  OF ANY KIND, either  express  or
 * implied.
 *
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.qi4j.library.alarm;

import java.util.List;
import java.util.Map;
import org.qi4j.api.common.UseDefaults;
import org.qi4j.api.mixin.Mixins;
import org.qi4j.api.property.Property;

/**
 * History of an AlarmPoint.
 * AlarmPoint system <i>should</i> implement <code>AlarmHistory</code> classes to
 * record the events of an <code>AlarmPoint</code>.
 *
 */
@Mixins( AlarmHistory.AlarmHistoryMixin.class )
public interface AlarmHistory
{

    /**
     * Returns the newest recorded <code>AlarmEvent</code>.
     *
     * @return the last AlarmEvent in the buffer. This is the newest event.
     */
    AlarmEvent lastEvent();

    /**
     * Returns the oldest recorded <code>AlarmEvent</code>.
     *
     * @return the first AlarmEvent in the buffer. This is the oldest event.
     */
    AlarmEvent firstEvent();

    /**
     * Returns the recorded <code>AlarmEvent</code> at the given position
     * in the buffer.
     *
     * @param position the position in the buffer, counted from the beginning to obtain the AlarmEvent from.
     *                 0 means the first element.
     *
     * @return the recorded <code>AlarmEvent</code> at the given position in the buffer.
     */
    AlarmEvent eventAt( int position );

    /**
     * Returns the recorded <code>AlarmEvent</code> at the given position
     * in the buffer counted from the end.
     *
     * @param position the position in the buffer, counted from the end to obtain the AlarmEvent from.
     *                 0 means the last element.
     *
     * @return the recorded <code>AlarmEvent</code> at the given position in the buffer counted from the end.
     */
    AlarmEvent eventAtEnd( int position );

    /**
     * Returns a <code>java.util.List</code> of all recorded <code>AlarmEvents</code>.
     *
     * @return a <code>java.util.List</code> of all recorded <code>AlarmEvents</code>.
     */
    @UseDefaults
    Property<List<AlarmEvent>> allAlarmEvents();

    /**
     * The maximum size of the history buffer.
     * If the sizes shrinks, the oldest <code>AlarmEvents</code> should be removed
     * so that the number of stored events are equal to the new <i>MaxSize</i>.
     *
     * @return The maxSize Property instance.
     */
    @UseDefaults
    Property<Integer> maxSize();

    /**
     * Returns all the Counters of triggers.
     * Each time the <code>trigger()</code> method is called,
     * a Counter is incremented. That means that after the first
     * time the following the sequence is called
     * <pre>
     * <code>activate();
     * acknowledge();
     * deactivate();
     * </code></pre>, the Map contains
     * <table summary="Triggers">
     * <tr><td>(String) activate</td><td>(Integer) 1</td></tr>
     * <tr><td>(String) deactivate</td><td>(Integer) 1</td></tr>
     * <tr><td>(String) acknowledge</td><td>(Integer) 1</td></tr>
     * </table>
     *
     * @return all the Counters of triggers.
     */
    @UseDefaults
    Property<Map<String, Integer>> counters();

    /**
     * Resets all counters.
     */
    void resetAllCounters();

    /**
     * Returns the Counter of activate triggers.
     * This method will return the number of times the
     * <code>activate()</code> method and the <code>trigger()</code>
     * method with an activate trigger is called.
     *
     * @return the Counter of activate triggers.
     */
    int activateCounter();

    /**
     * Resets the Activate counter.
     */
    void resetActivateCounter();

    void addEvent( AlarmEvent event, String trigger );

    abstract class AlarmHistoryMixin
        implements AlarmHistory
    {
        @Override
        public AlarmEvent firstEvent()
        {
            List<AlarmEvent> eventList = allAlarmEvents().get();
            if( eventList.isEmpty() )
            {
                return null;
            }
            return eventList.get( 0 );
        }

        @Override
        public AlarmEvent lastEvent()
        {
            List<AlarmEvent> eventList = allAlarmEvents().get();
            if( eventList.isEmpty() )
            {
                return null;
            }
            return eventList.get( eventList.size() - 1 );
        }

        @Override
        public AlarmEvent eventAt( final int position )
        {
            List<AlarmEvent> eventList = allAlarmEvents().get();
            if( eventList.size() <= position || position < 0 )
            {
                return null;
            }
            return eventList.get( position );
        }

        @Override
        public AlarmEvent eventAtEnd( final int position )
        {
            List<AlarmEvent> eventList = allAlarmEvents().get();
            int size = eventList.size();
            if( size <= position || position < 0 )
            {
                return null;
            }
            return eventList.get( size - position - 1 );
        }

        @Override
        public void addEvent( AlarmEvent event, String trigger )
        {
            List<AlarmEvent> eventList = allAlarmEvents().get();
            eventList.add( event );
            purge();
            allAlarmEvents().set( eventList );
            Map<String, Integer> counters = counters().get();
            Integer counter = counters.get( trigger );
            if( counter == null )
            {
                counter = 1;
            }
            else
            {
                counter = counter + 1;
            }
            counters.put( trigger, counter );
            counters().set( counters );
        }

        @Override
        public String toString()
        {
            StringBuilder buf = new StringBuilder();
            buf.append( "history[maxsize=" );
            buf.append( maxSize().get() );
            buf.append( ", size=" );
            buf.append( allAlarmEvents().get().size() );

            for( Map.Entry<String, Integer> entry : counters().get().entrySet() )
            {
                String type = entry.getKey();
                Integer counts = entry.getValue();
                buf.append( ", " );
                buf.append( type );
                buf.append( "=" );
                buf.append( counts );
            }
            buf.append( "]" );
            return buf.toString();
        }

        private void purge()
        {
            List<AlarmEvent> eventList = allAlarmEvents().get();
            Integer maxSize = maxSize().get();
            if( maxSize <= 0 )
            {
                eventList.clear();
            }

            while( eventList.size() > maxSize )
            {
                eventList.remove( 0 );
            }
        }

        @Override
        public void resetAllCounters()
        {
            Map<String, Integer> counters = counters().get();
            counters.clear();
            counters().set(counters);
        }

        @Override
        public int activateCounter()
        {
            Integer counter = counters().get().get( AlarmPoint.TRIGGER_ACTIVATE );
            if( counter == null )
            {
                return 0;
            }
            return counter;
        }

        @Override
        public void resetActivateCounter()
        {
            Map<String, Integer> counters = counters().get();
            counters.remove( AlarmPoint.TRIGGER_ACTIVATE );
            counters().set( counters );
        }
    }
}
