/*
 * 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.realtime;

import java.lang.ref.WeakReference;

import prm4j.Util;
import prm4j.api.Parameter;
import prm4j.indexing.BaseMonitor;
import prm4j.indexing.staticdata.MetaNode;

public class DefaultNode extends AbstractNode {

    private final MetaNode metaNode;
    private final MonitorSet[] monitorSets;
    private BaseMonitor monitor;
    private final WeakReference<Node> nodeRef;

    private WeakReference<Node> cachedNodeRef = null;
    private LowLevelBinding cachedBinding = null;
    private int cachedParameterIndex = -1;

    /**
     * @param metaNode
     * @param key
     *            may be null, if node is root node
     * @param hashCode
     *            hash code of the key
     */
    public DefaultNode(MetaNode metaNode, int parameterIndex, LowLevelBinding key) {
	super(key);
	this.metaNode = metaNode;
	monitorSets = new MonitorSet[metaNode.getMonitorSetCount()];
	nodeRef = new WeakReference<Node>(this);
    }

    @Override
    public MetaNode getMetaNode() {
	return metaNode;
    }

    @Override
    public BaseMonitor getMonitor() {
	return monitor;
    }

    @Override
    @Deprecated
    public Node getOrCreateNode(LowLevelBinding binding) {
	binding.registerNode(nodeRef);
	if (cachedBinding != binding) {
	    cachedBinding = binding;
	    cachedNodeRef = getOrCreate(binding.getParameterIndex(), binding).getNodeRef();
	}
	return cachedNodeRef.get();
    }

    @Override
    @Deprecated
    public Node getNode(LowLevelBinding binding) {
	if (cachedBinding != binding) {
	    final Node node = get(binding.getParameterIndex(), binding);
	    if (node != null) {
		cachedBinding = binding;
		cachedNodeRef = node.getNodeRef();
		return node;
	    } else {
		return null;
	    }
	}
	return cachedNodeRef.get();
    }

    @Override
    public Node getOrCreateNode(int parameterIndex, LowLevelBinding binding) {
	binding.registerNode(nodeRef);
	return getOrCreate(parameterIndex, binding);
    }

    @Override
    public Node getNode(int parameterIndex, LowLevelBinding binding) {
	return get(parameterIndex, binding);
    }

    @Override
    public void setMonitor(BaseMonitor monitor) {
	this.monitor = monitor;
    }

    @Override
    public MonitorSet getMonitorSet(int monitorSetId) {
	// lazy creation
	MonitorSet monitorSet = monitorSets[monitorSetId];
	if (monitorSet == null) {
	    monitorSet = new MonitorSet();
	    monitorSets[monitorSetId] = monitorSet;
	}
	return monitorSet;
    }

    @Override
    public MonitorSet[] getMonitorSets() {
	return monitorSets;
    }

    @Override
    public String toString() {
	if (monitor != null) {
	    // output e.g.: (p2=b, p3=c, p5=e)
	    return Util.bindingsToString(monitor.getLowLevelBindings());
	} else {
	    // output e.g.: (p2=?, p3=?, p5=e) because we only now the key and the node parameter set
	    final StringBuilder sb = new StringBuilder();
	    sb.append("(");
	    int i = 0;
	    final int max = metaNode.getNodeParameterList().size();
	    for (Parameter<?> parameter : metaNode.getNodeParameterList()) {
		if (++i < max) {
		    sb.append(parameter).append("=?, ");
		} else {
		    break;
		}
	    }
	    final String key = getKey() == null ? "" : getKey().toString();
	    sb.append(key).append(")");
	    return sb.toString();
	}
    }

    @Override
    public WeakReference<Node> getNodeRef() {
	return nodeRef;
    }

    @Override
    public int parameterIndex() {
	return metaNode.getLastParameterIndex();
    }

}

@@ -2282,180 +2282,690 @@
 );%0A%09
-return getOrCreate(parameterIndex, binding);%0A    %7D%0A%0A    @Override%0A    public Node getNode(int parameterIndex, LowLevelBinding binding) %7B%0A%09return get(parameterIndex, binding
+if (cachedParameterIndex != parameterIndex %7C%7C cachedBinding != binding) %7B%0A%09    cachedParameterIndex = parameterIndex;%0A%09    cachedBinding = binding;%0A%09    cachedNodeRef = getOrCreate(parameterIndex, binding).getNodeRef();%0A%09%7D%0A%09return cachedNodeRef.get();%0A    %7D%0A%0A    @Override%0A    public Node getNode(int parameterIndex, LowLevelBinding binding) %7B%0A%09if (cachedParameterIndex != parameterIndex %7C%7C cachedBinding != binding) %7B%0A%09    final Node node = get(parameterIndex, binding);%0A%09    if (node != null) %7B%0A%09%09cachedParameterIndex = parameterIndex;%0A%09%09cachedBinding = binding;%0A%09%09cachedNodeRef = node.getNodeRef();%0A%09%09return node;%0A%09    %7D else %7B%0A%09%09return null;%0A%09    %7D%0A%09%7D%0A%09return cachedNodeRef.get(
 );%0A 

Add caching to DefaultNode