
package kr.ac.kaist.ds.groupd.managing;

import java.util.Collection;
import java.util.Date;
import java.util.Random;

import kr.ac.kaist.ds.groupd.interest.InterestProtocol;
import kr.ac.kaist.ds.groupd.interest.impl.InterestProtocolImpl;
import kr.ac.kaist.ds.groupd.search.SearchProtocol;
import kr.ac.kaist.ds.groupd.search.SearchQuery;
import kr.ac.kaist.ds.groupd.topology.InterestInitializer;
import peersim.config.Configuration;
import peersim.core.CommonState;
import peersim.core.Control;
import peersim.core.Network;
import peersim.core.Node;
import peersim.dynamics.NodeInitializer;

public class Manager implements Control {

    private static final String PAR_TIME_RANGE_START_MS = "start";

    private static final String PAR_TIME_RANGE_END_MS = "end";

    private static final String PAR_INTEREST_GROUP_PROTOCOL = "interestgroup";

    private static final String PAR_SEARCH_PROTOCOL = "search";

    private static final String PAR_INIT = "init";
    
    private static final String PAR_MESSAGE_CYCLE = "messagecycle";

    private long startRangeMs;

    private long endRangeMs;

    private int interestGroupPid;

    private Random rand;

    private Date startTime;

    private Long interval;

    private NodeInitializer[] inits;

    private int searchProtocolPid;

    /**
     * Determines in which cycles a new search query will be started.
     */
    private int messageCycle;
    
    /**
     * The most simple way to give every search query an idea. Of course not threadsafe...
     */
    private int messageId = 0;

    public Manager(String prefix) {
        this.startRangeMs = Configuration.getLong(prefix + "." + PAR_TIME_RANGE_START_MS);
        this.endRangeMs = Configuration.getLong(prefix + "." + PAR_TIME_RANGE_END_MS);
        this.interestGroupPid = Configuration.getPid(prefix + "." + PAR_INTEREST_GROUP_PROTOCOL);
        this.searchProtocolPid = Configuration.getPid(prefix + "." + PAR_SEARCH_PROTOCOL);
        this.messageCycle = Configuration.getInt(prefix + "." + PAR_MESSAGE_CYCLE);
        this.rand = new Random();
        Object[] tmp = Configuration.getInstanceArray(prefix + "." + PAR_INIT);
        inits = new NodeInitializer[tmp.length];
        for (int i = 0; i < tmp.length; ++i) {
            inits[i] = (NodeInitializer)tmp[i];
        }
    }

    /**
     * Performs a change in the network, which means a node will change its
     * location and connect with new nodes. This will cause a new election to
     * take place.
     */
    private void changeNetwork() {
        Node node = insertNewNodeIntoNetwork();
        InterestProtocol interestGroupProtocol = (InterestProtocol)node
                .getProtocol(interestGroupPid);
        interestGroupProtocol.startCommunityFormation(node, interestGroupPid);
        InterestProtocol protocol = (InterestProtocol)interestGroupProtocol.getRepresentative().getProtocol(interestGroupPid);
        protocol.performGroupNameSetting(interestGroupProtocol.getRepresentative(), interestGroupPid);
    }

    /**
     * This method removes an existing node, creates a new one, copies the
     * interst vector of the old one to the new one and inserts the new node
     * into the network again. It is necessary to create a new node because the
     * state of the removed one is set to FAILED after removal.
     * 
     * @return the newly created and inserted node
     */
    private Node insertNewNodeIntoNetwork() {
        Node removedNode = (Node)Network.remove(CommonState.r.nextInt(Network.size()));
        InterestProtocolImpl oldNodeInterestGroupProtocol = (InterestProtocolImpl)removedNode
                .getProtocol(interestGroupPid);
        removeNodeFromAllOldNeigbours(removedNode, oldNodeInterestGroupProtocol);
        Node newnode = (Node)Network.prototype.clone();
        InterestProtocolImpl newNodeInterestGroupProtocol = (InterestProtocolImpl)newnode
                .getProtocol(interestGroupPid);
        newNodeInterestGroupProtocol.setInterestVector(oldNodeInterestGroupProtocol.getInterest());
        for (int j = 0; j < inits.length; ++j) {
            NodeInitializer initializer = inits[j];
            if (initializer instanceof InterestInitializer) {
                continue;
            }
            initializer.initialize(newnode);
        }
        Network.add(newnode);
        return newnode;
    }

    /**
     * Since the similarity is commutative, we do not need to check all nodes in
     * the network
     */
    private void removeNodeFromAllOldNeigbours(Node removedNode,
            InterestProtocol oldNodeInterestGroupProtocol) {
        Collection<Node> neighbours = oldNodeInterestGroupProtocol.getNeighbours();
        neighbours.stream().map(n -> (InterestProtocol)n.getProtocol(interestGroupPid))
                .forEach(p -> p.removeNeighbour(removedNode));
    }

    /**
     * @see peersim.core.Control#execute()
     */
    public boolean execute() {
        if (interval == null) {
            startTime = new Date();
            long range = endRangeMs - startRangeMs + 1;
            // compute a fraction of the range, 0 <= frac < range
            long fraction = (long)(range * rand.nextDouble());
            interval = fraction + startRangeMs;
        }
        long timeDifference = new Date().getTime() - startTime.getTime();
        if (timeDifference >= interval) {
            double nrOfNodesToChange = Math.ceil(timeDifference / interval);
            interval = null;
            //because PeerSims round model does not fit our aim to use a time interval
            //it is possible that we have to change several nodes
            for (int i = 0; i < nrOfNodesToChange; i++) {
                changeNetwork();
            }
        }
        if (CommonState.getIntTime() % messageCycle == 0) {
            Random random = new Random();
            int destination = random.nextInt(6040);
            int source = random.nextInt(6040);
            Node node = Network.get(source);
            SearchProtocol protocol = (SearchProtocol)node.getProtocol(searchProtocolPid);
            protocol.addSearchQuery(new SearchQuery(source, destination, CommonState.getIntTime(), messageId));
            messageId++;
        }
        return false;
    }

}

@@ -5940,24 +5940,93 @@
 le == 0) %7B%0D%0A
+            int networkSize = Configuration.getInt(%22network.size%22);%0D%0A
             
@@ -6093,36 +6093,43 @@
  random.nextInt(
-6040
+networkSize
 );%0D%0A            
@@ -6160,12 +6160,19 @@
 Int(
-6040
+networkSize
 );%0D%0A

fix bug in Manager, who used magic number instead of config param