/*
 * Copyright (c) 2012 Mateusz Parzonka, Eric Bodden
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 * Mateusz Parzonka - initial API and implementation
 */
package prm4j.indexing.staticdata;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

import prm4j.api.Parameter;
import prm4j.indexing.realtime.DefaultNode;
import prm4j.indexing.realtime.Node;
import prm4j.indexing.realtime.NodeMap;

/**
 * Every {@link Node} is equipped with a MetaNode, containing factory methods and providing statically computed
 * algorithm logic.
 *
 */
public class MetaNode {

    private final MetaNode[] successors;
    private ChainData[] chainData;
    private Set<Parameter<?>> nodeParameterSet;
    private Set<Parameter<?>> fullParameterSet;
    private int monitorSetCount;

    public MetaNode(Set<Parameter<?>> nodeParameterSet, Set<Parameter<?>> fullParameterSet) {
	super();
	this.nodeParameterSet = nodeParameterSet;
	this.fullParameterSet = fullParameterSet;
	assert parameterIndexIsValid(fullParameterSet) : "Full parameter set must be valid.";
	successors = new MetaNode[fullParameterSet.size()];
    }

    private static boolean parameterIndexIsValid(Set<Parameter<?>> fullParameterSet) {
	for (Parameter<?> parameter : fullParameterSet) {
	    if (parameter.getIndex() >= fullParameterSet.size() || parameter.getIndex() < 0) {
		return false;
	    }
	}
	return true;
    }

    public ChainData[] getChainData() {
	return chainData;
    }

    public Node createNode() {
	return new DefaultNode(this);
    }

    public Node createNode(int parameterId) {
	return successors[parameterId].createNode();
    }

    public NodeMap createNodeMap() {
	// TODO Auto-generated method stub
	return null;
    }

    void setChainData(ChainData[] chainData) {
	this.chainData = chainData;
    }

    public MetaNode getMetaNode(Parameter<?> parameter) {
	MetaNode node = successors[parameter.getIndex()];
	if (node == null) {
	    Set<Parameter<?>> parameterSet = new HashSet<Parameter<?>>();
	    parameterSet.addAll(fullParameterSet);
	    assert !parameterSet.contains(parameter) : "Parameter set could not have had contained new parameter.";
	    parameterSet.add(parameter);
	    successors[parameter.getIndex()] = node;
	}
	return node;
    }

    public Set<Parameter<?>> getParameterSet() {
	return fullParameterSet;
    }

    public int getMonitorSetCount() {
	return monitorSetCount;
    }

    public void setMonitorSetCount(int monitorSetCount) {
	this.monitorSetCount = monitorSetCount;
    }

    @Override
    public int hashCode() {
	final int prime = 31;
	int result = 1;
	result = prime * result + Arrays.hashCode(chainData);
	result = prime * result + ((fullParameterSet == null) ? 0 : fullParameterSet.hashCode());
	result = prime * result + monitorSetCount;
	result = prime * result + ((nodeParameterSet == null) ? 0 : nodeParameterSet.hashCode());
	result = prime * result + Arrays.hashCode(successors);
	return result;
    }

    @Override
    public boolean equals(Object obj) {
	if (this == obj)
	    return true;
	if (obj == null)
	    return false;
	if (getClass() != obj.getClass())
	    return false;
	MetaNode other = (MetaNode) obj;
	if (!Arrays.equals(chainData, other.chainData))
	    return false;
	if (fullParameterSet == null) {
	    if (other.fullParameterSet != null)
		return false;
	} else if (!fullParameterSet.equals(other.fullParameterSet))
	    return false;
	if (monitorSetCount != other.monitorSetCount)
	    return false;
	if (nodeParameterSet == null) {
	    if (other.nodeParameterSet != null)
		return false;
	} else if (!nodeParameterSet.equals(other.nodeParameterSet))
	    return false;
	if (!Arrays.equals(successors, other.successors))
	    return false;
	return true;
    }

    @Override
    public String toString() {
	return "MetaNode [successors=" + Arrays.toString(successors) + ", chainData=" + Arrays.toString(chainData)
		+ ", nodeParameterSet=" + nodeParameterSet + ", fullParameterSet=" + fullParameterSet
		+ ", monitorSetCount=" + monitorSetCount + "]";
    }

}

@@ -2084,16 +2084,19 @@
 eter) %7B%0A
+   
 %09MetaNod
@@ -2138,16 +2138,19 @@
 dex()%5D;%0A
+   
 %09if (nod
@@ -2158,24 +2158,27 @@
  == null) %7B%0A
+   
 %09    Set%3CPar
@@ -2188,17 +2188,21 @@
 ter%3C?%3E%3E 
-p
+nodeP
 arameter
@@ -2236,22 +2236,29 @@
 %3C?%3E%3E();%0A
+   
 %09    
-p
+nodeP
 arameter
@@ -2268,20 +2268,25 @@
 .addAll(
-full
+this.node
 Paramete
@@ -2292,16 +2292,19 @@
 erSet);%0A
+   
 %09    ass
@@ -2308,17 +2308,21 @@
 assert !
-p
+nodeP
 arameter
@@ -2412,14 +2412,21 @@
 .%22;%0A
+   
 %09    
-p
+nodeP
 aram
@@ -2449,16 +2449,84 @@
 meter);%0A
+   %09    node = new MetaNode(nodeParameterSet, fullParameterSet);%0A   
 %09    suc
@@ -2563,19 +2563,25 @@
 = node;%0A
-%09%7D%0A
+   %09%7D%0A   
 %09return 
@@ -2586,16 +2586,19 @@
 n node;%0A
+   
     %7D%0A%0A 

Fix bug in MetaNode