package org.lucci.madhoc.network.net;

import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Vector;

import org.lucci.madhoc.network.Connection;
import org.lucci.math.Utilities;


/*
 * Created on Jul 21, 2004
 */

/**
 * @author luc.hogie
 */
public final class NetworkInterface
{

    public NetworkingTechnology networkType;


    private double coverageRadius; 
    private double amplificationRatio = 1;
    private Collection<Connection> connections = new HashSet<Connection>();
    private NetworkingUnit networkingUnit;
    private int macAddress = ++instanceCount;

    // this is used for assigning the MAC address
    private static int instanceCount = 0;

    
    /**
     * @return Returns the macAdress.
     */
    public int getMACAddress()
    {
        return macAddress;
    }

    /**
     * Protected because only the station can set it.
     * @param mac
     */
    protected void setMACAddress(int mac)
    {
        this.macAddress = mac;
    }

    public Collection<Connection> getConnections()
    {
        return connections;
    }


    /**
     * @return Returns the corerageRadius.
     */
    public double getCoverageRadius()
    {
        return coverageRadius;
    }

    /**
     * @param coverageRadius The corerageRadius to set.
     */
    public void setCoverageRadius(double coverageRadius)
    {
        this.coverageRadius = coverageRadius * getAmplificationRatio();
    }

    
    

    /**
     * @return Returns the station.
     */
    public NetworkingUnit getNetworkingUnit()
    {
        return networkingUnit;
    }
    /**
     * @param inut The station to set.
     */
    public void setNetworkingUnit(NetworkingUnit inut)
    {
        this.networkingUnit = inut;
    }


    public Collection<NetworkInterface> getNeighborhood()
    {
        Collection<NetworkInterface> neighborhood = new Vector<NetworkInterface>();
        Iterator connectionIterator = getConnections().iterator();
        
        while (connectionIterator.hasNext())
        {
            Connection c = (Connection) connectionIterator.next();
            neighborhood.add(c.getSibbling(this));
        }

        return neighborhood;
    }
    
    
    /* (non-Javadoc)
     * @see java.lang.Object#hashCode()
     */
    public int hashCode()
    {
        return getMACAddress();
    }

    public double getAmplificationRatio()
    {
        return amplificationRatio;
    }
    public void setAmplificationRatio(double amplificationRatio)
    {
        this.amplificationRatio = amplificationRatio;
    }

    public NetworkingTechnology getNetworkingTechnology()
    {
        return networkType;
    }

    public void setNetworkingTechnology(NetworkingTechnology networkType)
    {
        this.networkType = networkType;
        
        setCoverageRadius(Utilities.getRandomBetween(networkType.getMinimumCoverageRadius(), networkType.getMaximumCoverageRadius(), getNetworkingTechnology().getNetwork().getSimulation().getRandomNumberGenerator().getRandom()));
    }

    public boolean hasNeighborsAtTwoHops()
    {
        
        for (NetworkInterface ni : getNeighborhood())
        {
            // if one if its neighbors has at least connection (including itself),
            // this means that the partition already features 2 hops
            if (ni.getNeighborhood().size() > 1)
            {
                return true;
            }
        }
        
        return false;
    }

    public boolean canSyncTo(NetworkInterface surroundingNetworkAdapter)
    {
        // if the node is not conected to any node, it can freely connect
        if (getNeighborhood().isEmpty())
        {
            // can only connect if the dnode is not involved in a two hops network
            // because in this case the latter wouldn't send any sync token
            return !surroundingNetworkAdapter.hasNeighborsAtTwoHops();
        }
        else
        {
            // it can connect to the node only if it already is the neighbor of one of its neighbors
            for (NetworkInterface nb : getNeighborhood())
            {
                if (nb.getNeighborhood().contains(surroundingNetworkAdapter))
                {
                    return true;
                }
            }

            return false;
        }
    }
}