/**
 * Copyright (C) 2013 - present by OpenGamma Inc. and the OpenGamma group of companies
 *
 * Please see distribution for license.
 */
package com.opengamma.engine.exec.plan;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.collect.Sets;
import com.opengamma.engine.cache.CacheSelectHint;
import com.opengamma.engine.calcnode.CalculationJobItem;
import com.opengamma.engine.depgraph.DependencyGraph;
import com.opengamma.engine.depgraph.DependencyNode;
import com.opengamma.engine.depgraph.impl.DependencyNodeImpl;
import com.opengamma.engine.function.FunctionParameters;
import com.opengamma.engine.value.ValueSpecification;
import com.opengamma.engine.view.ExecutionLogMode;
import com.opengamma.engine.view.impl.ExecutionLogModeSource;

/**
 * Produces an execution plan for a graph that will execute on a single calculation node in a single thread.
 */
public class SingleNodeExecutionPlanner implements GraphExecutionPlanner {

  private static final Logger s_logger = LoggerFactory.getLogger(SingleNodeExecutionPlanner.class);

  private static final class JobBuilder {

    private final List<CalculationJobItem> _items;
    private final String _calcConfigName;
    private final ExecutionLogModeSource _logModeSource;
    private final Set<ValueSpecification> _sharedValues;
    private final Set<ValueSpecification> _privateValues;
    private final Map<ValueSpecification, FunctionParameters> _parameters;
    private final Set<DependencyNode> _executed;

    public JobBuilder(final String calcConfigName, final int size, final ExecutionLogModeSource logModeSource, final Set<ValueSpecification> sharedValues,
        final Map<ValueSpecification, FunctionParameters> parameters) {
      _items = new ArrayList<CalculationJobItem>(size);
      _calcConfigName = calcConfigName;
      _logModeSource = logModeSource;
      _sharedValues = sharedValues;
      _privateValues = new HashSet<ValueSpecification>();
      _parameters = parameters;
      _executed = Sets.newHashSetWithExpectedSize(size);
    }

    public void addNodes(final DependencyNode root) {
      if (!_executed.add(root)) {
        return;
      }
      final ValueSpecification[] inputs = DependencyNodeImpl.getInputValueArray(root);
      for (int i = 0; i < inputs.length; i++) {
        final ValueSpecification input = inputs[i];
        if (!_sharedValues.contains(input)) {
          addNodes(root.getInputNode(i));
        }
      }
      final ExecutionLogMode logMode = _logModeSource.getLogMode(_calcConfigName, root.getOutputValue(0));
      final ValueSpecification[] outputs = DependencyNodeImpl.getOutputValueArray(root);
      FunctionParameters functionParameters = root.getFunction().getParameters();
      for (ValueSpecification output : outputs) {
        _privateValues.add(output);
        FunctionParameters newParameters = _parameters.get(output);
        if (newParameters != null) {
          functionParameters = newParameters;
        }
      }
      _items.add(new CalculationJobItem(root.getFunction().getFunctionId(), functionParameters, root.getTarget(), inputs, outputs, logMode));
    }

    public List<CalculationJobItem> getJobItems() {
      return _items;
    }

    public CacheSelectHint getCacheHint() {
      s_logger.debug("{} private values, {} shared values in graph", _privateValues.size(), _sharedValues.size());
      if (_privateValues.size() < _sharedValues.size()) {
        return CacheSelectHint.privateValues(_privateValues);
      } else {
        return CacheSelectHint.sharedValues(_sharedValues);
      }
    }

  }

  protected static PlannedJob createJob(final DependencyGraph graph, final ExecutionLogModeSource logModeSource, final Set<ValueSpecification> sharedValues,
      final Map<ValueSpecification, FunctionParameters> parameters) {
    final JobBuilder builder = new JobBuilder(graph.getCalculationConfigurationName(), graph.getSize(), logModeSource, sharedValues, parameters);
    final int roots = graph.getRootCount();
    iLoop: for (int i = 0; i < roots; i++) { //CSIGNORE
      final DependencyNode root = graph.getRootNode(i);
      final int outputs = root.getOutputCount();
      for (int j = 0; j < outputs; j++) {
        if (sharedValues.contains(root.getOutputValue(j))) {
          continue iLoop;
        }
      }
      builder.addNodes(root);
    }
    return new PlannedJob(0, builder.getJobItems(), builder.getCacheHint(), null, null);
  }

  // GraphExecutionPlanner

  @Override
  public GraphExecutionPlan createPlan(final DependencyGraph graph, final ExecutionLogModeSource logModeSource, final long functionInitialisationId, final Set<ValueSpecification> sharedValues,
      final Map<ValueSpecification, FunctionParameters> parameters) {
    final PlannedJob job = createJob(graph, logModeSource, sharedValues, parameters);
    return new GraphExecutionPlan(graph.getCalculationConfigurationName(), functionInitialisationId, Collections.singleton(job), 1, job.getItems().size(), Double.NaN, Double.NaN);
  }

}

@@ -1517,32 +1517,89 @@
 _privateValues;%0A
+    private final Map%3CValueSpecification, ?%3E _terminals;%0A
     private fina
@@ -1739,45 +1739,29 @@
 nal 
-String calcConfigName, final int size
+DependencyGraph graph
 , fi
@@ -1845,24 +1845,16 @@
 dValues,
-%0A       
  final M
@@ -1906,24 +1906,64 @@
 rameters) %7B%0A
+      final int size = graph.getSize();%0A
       _items
@@ -2030,30 +2030,55 @@
 gName = 
-calcConfig
+graph.getCalculationConfiguration
 Name
+()
 ;%0A      
@@ -2199,24 +2199,71 @@
 ication%3E();%0A
+      _terminals = graph.getTerminalOutputs();%0A
       _param
@@ -3075,24 +3075,126 @@
  outputs) %7B%0A
+        if (_terminals.containsKey(output)) %7B%0A          _sharedValues.add(output);%0A        %7D else %7B%0A  
         _pri
@@ -3209,32 +3209,42 @@
 es.add(output);%0A
+        %7D%0A
         Function
@@ -4281,59 +4281,8 @@
 raph
-.getCalculationConfigurationName(), graph.getSize()
 , lo

Mark terminal output values for the shared cache