/*
 * Created on Feb 1, 2004
 *
 * To change the template for this generated file go to
 * Window>Preferences>Java>Code Generation>Code and Comments
 */
package org.lucci.madhoc.simulation;



import java.awt.Color;
import java.net.URL;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Vector;

import javax.swing.Icon;
import javax.swing.ImageIcon;

import org.lucci.gui.ColorList;
import org.lucci.gui.Utilities;
import org.lucci.madhoc.gui.ProjectionComponent;
import org.lucci.madhoc.network.Application;
import org.lucci.madhoc.network.Network;
import org.lucci.madhoc.network.Station;
import org.lucci.madhoc.simulation.measure.DistributionSensor;
import org.lucci.madhoc.simulation.measure.MeasureHistory;
import org.lucci.madhoc.simulation.measure.NumericalSensor;
import org.lucci.madhoc.simulation.measure.Sensor;
import org.lucci.madhoc.simulation.projection.Projection;

public abstract class Monitor implements Configurable
{
    private Map<Class, Sensor> sensorMap = new HashMap<Class, Sensor>();
    private List<NumericalSensor> numericalMeasures;
    private Collection<TerminationCondition> terminationConditions;
    private Icon icon;
    private Network network;
    private Collection<Application> deployedApplications = new Vector<Application>();
    
    private Class stationApplicationClass;

    
    
    public Collection<Class> getMonitorViewClasses()
    {
        return new Vector<Class>();
    }

    public abstract String getName();
    public abstract String getFriendlyName(); 

    private final static ColorList colorList = new ColorList();
    private Color color = null;
    private MonitorInitializer initializer;
    
    
    public boolean hasCompleted()
    {
        for (TerminationCondition tc : getTerminationConditions())
        {
            if (!tc.applicationHasCompleted(this))
            {
                return false;
            }
        }
        
        return true;
    }
    
    public final Color getColor()
    {
        return this.color;
    }
    
    
    
    public void configure()
        throws Throwable
    {
        setStationApplicationClass(getNetwork().getSimulation().getConfiguration().getClass(getName() + "_protocol_class"));

        this.terminationConditions = (Collection<TerminationCondition>) getNetwork().getSimulation().getConfiguration().getInstantiatedClasses(getName() + "_termination_conditions");

        for (TerminationCondition terminationCondition : this.terminationConditions)
        {
            terminationCondition.setMonitor(this);
            terminationCondition.configure();
        }


        this.initializer = (MonitorInitializer) getNetwork().getSimulation().getConfiguration().getInstantiatedClass(getName() + "_initializer");

        if (this.initializer != null)
        {
            this.initializer.setMonitor(this);
            this.initializer.configure();
        }


        // get the regex that describes the measures wanted by the user  
        Collection<String> disableSensorsName = getNetwork().getSimulation().getConfiguration().getStrings(getName() + "_disabled_sensors");

        for (Sensor sensor : new Vector<Sensor>(getSensorMap().values()))
        {
            if (!disableSensorsName.contains(sensor.getName()) && !disableSensorsName.contains(sensor.getClass().getName()))
            {
                for (Projection projection : getNetwork().getProjectionMap().values())
                {
                    MeasureHistory history = sensor.createMeasureHistory();
                    history.setProjection(projection);
                    projection.getMeasureHistoryMap().put(sensor, history);
                }
            }
            else
            {
                getSensorMap().remove(sensor.getClass());
            }
        }

        int alpha = getNetwork().getSimulation().getConfiguration().getInteger("monitor_color_alpha");
        this.color = Utilities.setAlpha(colorList.getNextColor(), alpha);
    }
    
    /**
     * This is a convenience method that stands for:
     * <code>getMeasureMap().put(MeasureClass, new Measure(this));</code>
     * @param sensor
     */
    public void addMeasure(Sensor sensor)
    {
        sensor.setMonitor(this);
        sensorMap.put(sensor.getClass(), sensor);
    }

    /**
     * @return Returns the measures.
     */
    public Map<Class, Sensor> getSensorMap()
    {
        return sensorMap;
    }

    public Collection<NumericalSensor> getNumericalMeasures()
    {
        if (numericalMeasures == null)
        {
            numericalMeasures = new Vector<NumericalSensor>();
            
            for (Sensor m : getSensorMap().values())
            {
                if (m instanceof NumericalSensor)
                {
                    numericalMeasures.add((NumericalSensor) m);
                }
            }
        }
        
        return numericalMeasures;
    }

    public Collection<DistributionSensor> findDistributionMeasures()
    {
        Collection<DistributionSensor> distributionMeasures = new Vector<DistributionSensor>();
        
        for (Sensor m : getSensorMap().values())
        {
            if (m instanceof DistributionSensor)
            {
                distributionMeasures.add((DistributionSensor) m);
            }
        }
        
        return distributionMeasures;
    }

    
    /**
     * deploys the application on each station
     */
    public void deploy()
        throws Throwable
    {
        if (getStationApplicationClass() == null)
            throw new IllegalStateException("station application class is not defined, this method shouldn't be invoked");

        for (Station computer : getNetwork().getStations())
        {
            Application stationApp = computer.deploy(getStationApplicationClass(), this);
            stationApp.configure();
            deployedApplications.add(stationApp);
        }
    }



    public Icon getIcon()
    {
        if ( icon == null )
        {
            URL url = getClass().getResource("icon.png");
            
            if (url == null)
            {
                url = ProjectionComponent.class.getResource("simulationApplicationDefaultIcon.png");
            }
            
            icon = new ImageIcon(url);
        }
        
        return icon;
    }



    public Collection<Application> findApplications(Collection<Station> computers)
    {
        Collection<Application> apps = new Vector<Application>();
        
        for(Station computer : computers)
        {
            Application app = (Application) computer.getApplicationMap().getValue(getStationApplicationClass());
            
            if (app != null)
            {
                apps.add(app);
            }
        }

        return apps;
    }
    


    /**
     * @return Returns the stationApplicationClass.
     */
    public Class getStationApplicationClass()
    {
        return stationApplicationClass;
    }

    /**
     * @param stationApplicationClass The stationApplicationClass to set.
     */
    public void setStationApplicationClass(Class stationApplicationClass)
    {
        this.stationApplicationClass = stationApplicationClass;
    }

    /**
     * 
     */
    public abstract void resetIterationScopedValues();

    public Network getNetwork()
    {
        return network;
    }
    public void setNetwork(Network network)
    {
        this.network = network;
    }
    public Collection<TerminationCondition> getTerminationConditions()
    {
        return terminationConditions;
    }
    public void setTerminationConditions(Collection<TerminationCondition> terminationCondition)
    {
        this.terminationConditions = terminationCondition;
    }
    public MonitorInitializer getInitializer()
    {
        return initializer;
    }
    public Collection<Application> getDeployedApplications()
    {
        return java.util.Collections.unmodifiableCollection(deployedApplications);
    }
}