/**
 * Copyright (C) 2011 - present by OpenGamma Inc. and the OpenGamma group of companies
 *
 * Please see distribution for license.
 */
package com.opengamma.financial.analytics;

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

import com.opengamma.core.position.Portfolio;
import com.opengamma.core.position.PortfolioNode;
import com.opengamma.core.position.Position;
import com.opengamma.engine.ComputationTarget;
import com.opengamma.engine.function.AbstractFunction;
import com.opengamma.engine.function.CompiledFunctionDefinition;
import com.opengamma.engine.function.FunctionCompilationContext;
import com.opengamma.engine.function.FunctionExecutionContext;
import com.opengamma.engine.function.FunctionInputs;
import com.opengamma.engine.target.ComputationTargetType;
import com.opengamma.engine.value.ComputedValue;
import com.opengamma.engine.value.ValueProperties;
import com.opengamma.engine.value.ValuePropertyNames;
import com.opengamma.engine.value.ValueRequirement;
import com.opengamma.engine.value.ValueSpecification;
import com.opengamma.financial.property.UnitProperties;

/**
 * Able to sum a particular requirement name from a set of underlying positions. If any values are not produced (because of missing market data or computation errors) a partial sum is produced.
 */
public class SummingFunction extends MissingInputsFunction {

  public static final String IGNORE_ROOT_NODE = "SummingFunction.IGNORE_ROOT_NODE";

  /**
   * The number of positions that made up the sum.
   */
  private static final String POSITION_COUNT = "PositionCount";

  /**
   * Main implementation.
   */
  protected static class Impl extends AbstractFunction.NonCompiledInvoker {

    private final String _requirementName;
    private final String[] _homogenousProperties = UnitProperties.unitPropertyNames();

    protected Impl(final String requirementName) {
      _requirementName = requirementName;
    }

    private static CompiledFunctionDefinition of(final String requirementName) {
      return new Impl(requirementName);
    }

    protected String getRequirementName() {
      return _requirementName;
    }

    @Override
    public String getShortName() {
      return "Sum(" + _requirementName + ")";
    }

    @Override
    public ComputationTargetType getTargetType() {
      return ComputationTargetType.PORTFOLIO_NODE;
    }

    @Override
    public boolean canApplyTo(final FunctionCompilationContext context, final ComputationTarget target) {
      // Applies to any portfolio node, except the root if "Don't aggregate root node" is set
      Portfolio portfolio = context.getPortfolio();
      if (portfolio == null || portfolio.getAttributes().get(IGNORE_ROOT_NODE) == null) {
        return true;
      } else {
        return target.getPortfolioNode().getParentNodeId() != null;
      }
    }

    @Override
    public Set<ValueSpecification> getResults(final FunctionCompilationContext context, final ComputationTarget target) {
      return Collections.singleton(new ValueSpecification(getRequirementName(), target.toSpecification(), ValueProperties.all()));
    }

    @Override
    public Set<ValueRequirement> getRequirements(final FunctionCompilationContext context, final ComputationTarget target, final ValueRequirement desiredValue) {
      final PortfolioNode node = target.getPortfolioNode();
      final Set<ValueRequirement> requirements = new HashSet<ValueRequirement>();
      // Requirement has all constraints asked of us
      final ValueProperties.Builder resultConstraintsBuilder = desiredValue.getConstraints().copy();
      for (final String homogenousProperty : _homogenousProperties) {
        // TODO: this should probably only be optional if absent from the desired constraints
        resultConstraintsBuilder.withOptional(homogenousProperty);
      }
      ValueProperties resultConstraints = resultConstraintsBuilder.get();
      for (final Position position : node.getPositions()) {
        requirements.add(new ValueRequirement(getRequirementName(), ComputationTargetType.POSITION, position.getUniqueId().toLatest(), resultConstraints));
      }
      for (final PortfolioNode childNode : node.getChildNodes()) {
        requirements.add(new ValueRequirement(getRequirementName(), ComputationTargetType.PORTFOLIO_NODE, childNode.getUniqueId(), resultConstraints));
      }
      return requirements;
    }

    protected Object addValue(final Object previousSum, final Object currentValue) {
      return SumUtils.addValue(previousSum, currentValue, getRequirementName());
    }

    protected ValueProperties.Builder createValueProperties(final ValueProperties inputProperties, final int componentCount) {
      return inputProperties.copy().withoutAny(ValuePropertyNames.FUNCTION).with(ValuePropertyNames.FUNCTION, getUniqueId()).with(POSITION_COUNT, Integer.toString(componentCount));
    }

    @Override
    public Set<ValueSpecification> getResults(final FunctionCompilationContext context, final ComputationTarget target, final Map<ValueSpecification, ValueRequirement> inputs) {
      int positionCount = 0;
      // Result properties are anything that was common to the input requirements
      ValueProperties common = null;
      final boolean[] homogenousProperties = new boolean[_homogenousProperties.length];
      for (final ValueSpecification input : inputs.keySet()) {
        final ValueProperties properties = input.getProperties();
        final Set<String> inputPositionCountValues = properties.getValues(POSITION_COUNT);
        if (inputPositionCountValues == null) {
          positionCount++;
        } else {
          if (inputPositionCountValues.size() == 1) {
            final int inputPositionCount = Integer.parseInt(inputPositionCountValues.iterator().next());
            if (inputPositionCount == 0) {
              // Ignore this one
              continue;
            }
            positionCount += inputPositionCount;
          }
        }
        if (common == null) {
          common = properties;
          for (int i = 0; i < homogenousProperties.length; i++) {
            homogenousProperties[i] = properties.getValues(_homogenousProperties[i]) != null;
          }
        } else {
          for (int i = 0; i < homogenousProperties.length; i++) {
            if ((properties.getValues(_homogenousProperties[i]) != null) != homogenousProperties[i]) {
              // Either defines one of the properties that something else doesn't, or doesn't define
              // one that something else does
              return null;
            }
          }
          common = SumUtils.addProperties(common, properties);
        }
      }
      if (common == null) {
        // Can't have been any inputs - the sum will be zero with any properties the caller wants
        return Collections.singleton(new ValueSpecification(getRequirementName(), target.toSpecification(), createValueProperties().with(POSITION_COUNT, "0").get()));
      }
      for (int i = 0; i < homogenousProperties.length; i++) {
        if (homogenousProperties[i] == Boolean.TRUE) {
          if (common.getValues(_homogenousProperties[i]) == null) {
            // No common intersection of values for homogenous property
            return null;
          }
        }
      }
      return Collections.singleton(new ValueSpecification(getRequirementName(), target.toSpecification(), createValueProperties(common, positionCount).get()));
    }

    @Override
    public Set<ComputedValue> execute(final FunctionExecutionContext executionContext, final FunctionInputs inputs, final ComputationTarget target, final Set<ValueRequirement> desiredValues) {
      final ValueRequirement desiredValue = desiredValues.iterator().next();
      Object value = null;
      for (final ComputedValue input : inputs.getAllValues()) {
        final Object inputValue = input.getValue();
        if (inputValue instanceof String) {
          // Threat the empty string as a special case - it's used in place of 0 when there are no valid inputs
          if (((String) inputValue).length() == 0) {
            continue;
          }
        }
        value = addValue(value, input.getValue());
      }
      if (value == null) {
        // Can't have been any non-zero inputs - the sum is logical zero
        value = "";
      }
      return Collections.singleton(new ComputedValue(new ValueSpecification(getRequirementName(), target.toSpecification(), desiredValue.getConstraints()), value));
    }

  }

  public SummingFunction(final String requirementName) {
    super(Impl.of(requirementName));
  }

}

@@ -7954,17 +7954,16 @@
     // T
-h
 reat the

Correct typo.