/*
 * Created on Feb 1, 2004
 */
package org.lucci.madhoc.network;

import java.io.IOException;
import java.util.Collection;
import java.util.Iterator;
import java.util.Vector;

import javax.swing.Icon;

import org.lucci.madhoc.messaging.Message;
import org.lucci.madhoc.messaging.TransferableByteArray;
import org.lucci.madhoc.messaging.TransferableDouble;
import org.lucci.madhoc.messaging.TransferableObject;
import org.lucci.madhoc.messaging.TransferableString;
import org.lucci.madhoc.network.monitor.NetworkMonitor;
import org.lucci.madhoc.simulation.Configurable;
import org.lucci.madhoc.simulation.Monitor;


public abstract class Application implements Configurable
{
    private Station station;
    private Monitor monitor;


    public void sendMessage(Object o, Station recipient)
    {
        Collection<Station> recipients = new Vector<Station>();
        recipients.add(recipient);
        sendMessage(o, recipients);
    }

    public void sendMessage(Object o, Collection<Station> recipients)
    {
        Message message = new Message();
        message.getMessageInformation().setCreationDate(getSimulatedTime());
        message.setSourceStationApplication(this);
        
        for (Station s : recipients)
        {
            message.getRecipientApplications().add(s.getApplicationMap().getValue(getClass()));
        }
        
        TransferableObject content = createTransferableObject(o);
        message.setContent(content);

        try
        {
            getComputer().getNetworkingUnit().getOutgoingMessageQueue().add(message);
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
    }
    
    
    
    private TransferableObject createTransferableObject(Object o)
    {
        if (o instanceof String)
        {
            TransferableString ts = new TransferableString();
            ts.setValue((String) o);
            return ts;
        }
        else if (o instanceof byte[])
        {
            TransferableByteArray ts = new TransferableByteArray();
            ts.setValue((byte[]) o);
            return ts;
        }
        else if (o instanceof Number)
        {
            TransferableDouble ts = new TransferableDouble();
            ts.setValue((Double) o);
            return ts;
        }
        else
        {
            throw new IllegalArgumentException("cannot handle message contents of type " + o.getClass().getName());
        }
    }
    
    public Collection getNeighborApplications()
    {
        return getComputer().getNetworkingUnit().getApplicationsInTheNeighborhood(this.getClass());
    }

    public Collection<Message> getIncomingMessages()
    {
        Collection<Message> messages = new Vector<Message>();
        
        Iterator<Message> i = getComputer().getNetworkingUnit().getIncomingMessageQueue().internalList().iterator();
        
        while (i.hasNext())
        {
            Message msg = (Message) i.next();
            
            if (
                    // the message is explicitely targeted to this application
                    msg.getRecipientApplications().remove(this)
                    ||
                    // the message comes from the same kind of application but has no explicit recipient -> broadcast
                    (msg.getRecipientApplications().isEmpty() && getClass() == msg.getSourceStationApplication().getClass()))
            {
                messages.add(msg);

                // if no other application is supposed to take the message
                if (msg.getRecipientApplications().isEmpty())
                {
                    // just remove it from the input buffer
                    i.remove();
                }
            }
        }
        
        return messages;
    }


    
    public Monitor getMonitor()
    {
        return monitor;
    }

    public void setMonitor(Monitor monitor)
    {
        if (monitor == null)
            throw new IllegalStateException();

        this.monitor = monitor;
    }

    
    public abstract String getName();

    public Icon getIcon()
    {
        return monitor.getIcon();
    }

    public String getHTMLDescription()
    {
        String s = "<html>Application " + getName();
        Monitor networkMonitor = getMonitor().getNetwork().getMonitorMap().get(NetworkMonitor.class);
        getComputer();
//        networkMonitor.get
        return s;
    }

    
    public Station getComputer()
    {
        return station;
    }

    public void setComputer(Station computer)
    {
        if (computer == null)
            throw new IllegalStateException();

        this.station = computer;
    }


    public double getSimulatedTime()
    {
        return getMonitor().getNetwork().getSimulation().getSimulatedTime();
    }
    
    public abstract void doIt(double time);

}