package org.lucci.madhoc.network;

import java.lang.reflect.InvocationTargetException;

import org.lucci.madhoc.env.MobilityModel;
import org.lucci.madhoc.network.cpu.ComputationalUnit;
import org.lucci.madhoc.network.net.Location;
import org.lucci.madhoc.network.net.NetworkingUnit;
import org.lucci.madhoc.simulation.Configurable;
import org.lucci.madhoc.simulation.Monitor;
import org.lucci.math.relation.DefaultRelation;
import org.lucci.math.relation.Relation;

/*
 * Created on May 3, 2005
 */

/**
 * @author luc.hogie
 */
public class Station implements Configurable
{
    private int identifier = -1;

    private NetworkingUnit networkingUnit = new NetworkingUnit();

    private MobilityModel mobilityModel;

    private ComputerType type = ComputerType.MOBILE_PHONE;

    private Network network;

    private Relation<Class, Application> applicationMap = new DefaultRelation<Class, Application>();

    private Location point;

    private ComputationalUnit cpu = new ComputationalUnit();

    private boolean switchedOn = true;

    private VolatilityModel volatilityModel;

    public VolatilityModel getVolatilityModel()
    {
        return volatilityModel;
    }

    public void setVolatilityModel(VolatilityModel volatilityModel)
    {
        this.volatilityModel = volatilityModel;
    }

    public int getIdentifier()
    {
        return this.identifier;
    }

    public void setIdentifier(int id)
    {
        if (id < 0)
            throw new IllegalArgumentException();

        this.identifier = id;
    }

    public int hashCode()
    {
        return getIdentifier();
    }

    /**
     * @return Returns the mobilityModel.
     */
    public MobilityModel getMobilityModel()
    {
        return mobilityModel;
    }

    /**
     * @param mobilityModel
     *            The mobilityModel to set.
     */
    public void setMobilityModel(MobilityModel mobilityModel)
    {
        this.mobilityModel = mobilityModel;
    }

    public void configure() throws Throwable
    {
        try
        {
            MobilityModel mobility = (MobilityModel) getNetwork().getNetworkEnvironment().getDefaultMobilityModel().newInstance();
            mobility.setMobileNode(this);
            mobility.configure();
            setMobilityModel(mobility);
        }
        catch (InvocationTargetException ex)
        {
            ex.getCause().printStackTrace();
            throw new IllegalStateException();
        }

        this.volatilityModel = (VolatilityModel) getNetwork().getSimulation().getConfiguration().getInstantiatedClass("volatility_model");

        getNetworkingUnit().setStation(this);
        getNetworkingUnit().configure();
        getComputationalUnit().setStation(this);
        getComputationalUnit().configure();
    }

    public Location getLocation()
    {
        return point;
    }

    public void setLocation(Location point)
    {
        if (point == null)
            throw new IllegalArgumentException("null point");

        this.point = point;
        point.setComputer(this);
        point.setCel(getNetwork().getNetworkEnvironment().getGrid().getCelAt(point));
    }

    public Relation<Class, Application> getApplicationMap()
    {
        return applicationMap;
    }

    public Application deploy(Class clazz, Monitor simulationApp) throws Throwable
    {
        if (getApplicationMap().getValue(clazz) != null)
            throw new IllegalStateException("an application of class " + clazz.getName() + " is already running on this computer");

        Application app = (Application) clazz.newInstance();
        app.setMonitor(simulationApp);
        app.setComputer(this);
        app.configure();

        while (clazz != Application.class)
        {
            applicationMap.add(clazz, app);
            clazz = clazz.getSuperclass();
        }

        return app;
    }

    public ComputerType getType()
    {
        return type;
    }

    public Network getNetwork()
    {
        return network;
    }

    public void setNetwork(Network network)
    {
        if (this.network != null)
            throw new IllegalStateException("network is already defined");

        this.network = network;
    }

    public void setType(ComputerType type)
    {
        this.type = type;
    }

    public ComputationalUnit getComputationalUnit()
    {
        return cpu;
    }

    public NetworkingUnit getNetworkingUnit()
    {
        return networkingUnit;
    }

    public boolean isSwitchedOn()
    {
        return switchedOn;
    }

    public void setSwitchedOn(boolean switchedOn)
    {
        this.switchedOn = switchedOn;
    }

}