/**
 * Copyright (C) 2012 - 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.Map;
import java.util.Set;

import com.google.common.collect.ImmutableSet;
import com.opengamma.engine.ComputationTarget;
import com.opengamma.engine.ComputationTargetSpecification;
import com.opengamma.engine.function.AbstractFunction;
import com.opengamma.engine.function.FunctionCompilationContext;
import com.opengamma.engine.function.FunctionExecutionContext;
import com.opengamma.engine.function.FunctionInputs;
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.ValueRequirementNames;
import com.opengamma.engine.value.ValueSpecification;
import com.opengamma.financial.property.UnitProperties;
import com.opengamma.id.UniqueId;

/**
 * 
 */
public abstract class AbstractWeightFunction extends AbstractFunction.NonCompiledInvoker {

  /**
   * The property name that describes the value used to construct the weight. The full set of output properties will include those of the target and its parent node. If any of these are called
   * {@code Value} they will be prefixed to give {@code ValueValue}.
   */
  public static final String VALUE_PROPERTY_NAME = "Value";

  /**
   * The property name exposed by this function for properties on the requirements that would collide with its own {@code Value}.
   */
  public static final String PASSTHROUGH_VALUE_PROPERTY_NAME = "ValueValue";

  /**
   * Default to the generic value, for example FAIR_VALUE or PRESENT_VALUE depending on the asset classes.
   */
  private static final String DEFAULT_VALUE_NAME = ValueRequirementNames.VALUE;

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

  @Override
  public Set<ValueRequirement> getRequirements(final FunctionCompilationContext context, final ComputationTarget target, final ValueRequirement desiredValue) {
    final ValueProperties constraints = desiredValue.getConstraints();
    Set<String> values = constraints.getValues(VALUE_PROPERTY_NAME);
    final String inputValue;
    if ((values == null) || values.isEmpty()) {
      inputValue = DEFAULT_VALUE_NAME;
    } else if (values.size() == 1) {
      inputValue = values.iterator().next();
    } else {
      return null;
    }
    // Propogate the desired value constraints onto the requirements, removing those specific to this function, rewriting any that would be masked
    // and adding a unit homogeneity clause
    final ValueProperties.Builder requirementConstraintsBuilder = constraints.copy().withoutAny(VALUE_PROPERTY_NAME);
    values = constraints.getValues(PASSTHROUGH_VALUE_PROPERTY_NAME);
    if (values != null) {
      if (constraints.isOptional(PASSTHROUGH_VALUE_PROPERTY_NAME)) {
        requirementConstraintsBuilder.withOptional(VALUE_PROPERTY_NAME);
      }
      requirementConstraintsBuilder.withoutAny(PASSTHROUGH_VALUE_PROPERTY_NAME);
      if (values.isEmpty()) {
        requirementConstraintsBuilder.withAny(VALUE_PROPERTY_NAME);
      } else {
        requirementConstraintsBuilder.with(VALUE_PROPERTY_NAME, values);
      }
    }
    for (String unit : UnitProperties.unitPropertyNames()) {
      values = constraints.getValues(unit);
      if (values == null) {
        // Unit was not specified on the output, but we specify it on the inputs so we can check homogeneity to ensure the division is valid
        requirementConstraintsBuilder.withOptional(unit);
      }
    }
    // Request value on the value and parent
    final ValueProperties requirementConstraints = requirementConstraintsBuilder.get();
    return ImmutableSet.of(new ValueRequirement(inputValue, getValueTarget(target), requirementConstraints), new ValueRequirement(inputValue, getParentTarget(target), requirementConstraints));
  }

  protected abstract ComputationTargetSpecification getValueTarget(final ComputationTarget target);

  protected abstract ComputationTargetSpecification getParentTarget(final ComputationTarget target);

  @Override
  public Set<ValueSpecification> getResults(final FunctionCompilationContext context, final ComputationTarget target, final Map<ValueSpecification, ValueRequirement> inputs) {
    ValueSpecification inputValue = null;
    ValueSpecification inputParent = null;
    final UniqueId value = target.getUniqueId();
    for (ValueSpecification input : inputs.keySet()) {
      if (value.equals(input.getTargetSpecification().getUniqueId())) {
        assert inputValue == null;
        inputValue = input;
      } else {
        assert inputParent == null;
        inputParent = input;
      }
    }
    final ValueProperties rawResultProperties = inputValue.getProperties().intersect(inputParent.getProperties());
    final ValueProperties.Builder resultPropertiesBuilder = rawResultProperties.copy();
    for (String unit : UnitProperties.unitPropertyNames()) {
      final Set<String> valueUnits = inputValue.getProperties().getValues(unit);
      final Set<String> parentUnits = inputParent.getProperties().getValues(unit);
      if (valueUnits != null) {
        if (parentUnits != null) {
          if (rawResultProperties.getValues(unit) != null) {
            // The operation is a division, so there are no units on the result
            resultPropertiesBuilder.withoutAny(unit);
          } else {
            // No common intersection between parent and value properties for this unit
            return null;
          }
        } else {
          // Parent did not include the same units as the value
          return null;
        }
      } else {
        if (parentUnits != null) {
          // Value did not include the same units as the parent
          return null;
        }
      }
    }
    resultPropertiesBuilder.withoutAny(ValuePropertyNames.FUNCTION).with(ValuePropertyNames.FUNCTION, getUniqueId());
    return Collections.singleton(new ValueSpecification(ValueRequirementNames.WEIGHT, target.toSpecification(), resultPropertiesBuilder.get()));
  }

  @Override
  public Set<ComputedValue> execute(final FunctionExecutionContext executionContext, final FunctionInputs inputs, final ComputationTarget target, final Set<ValueRequirement> desiredValues) {
    double parentValue = 0;
    double targetValue = 0;
    final UniqueId targetIdentifier = target.getUniqueId();
    for (final ComputedValue c : inputs.getAllValues()) {
      if (targetIdentifier.equals(c.getSpecification().getTargetSpecification().getUniqueId())) {
        targetValue = (Double) c.getValue();
      } else {
        parentValue = (Double) c.getValue();
      }
    }
    final ValueRequirement desiredValue = desiredValues.iterator().next();
    return Collections.singleton(new ComputedValue(new ValueSpecification(ValueRequirementNames.WEIGHT, target.toSpecification(), desiredValue.getConstraints()),
        targetValue / parentValue));
  }

}

@@ -1327,383 +1327,67 @@
 ode.
- If any of these are called%0A   * %7B@code Value%7D they will be prefixed to give %7B@code ValueValue%7D.%0A   */%0A  public static final String VALUE_PROPERTY_NAME = %22Value%22;%0A%0A  /**%0A   * The property name exposed by this function for properties on the requirements that would collide with its own %7B@code Value%7D.%0A   */%0A  public static final String PASSTHROUGH_VALUE_PROPERTY_NAME = %22Value
+%0A   */%0A  public static final String VALUE_PROPERTY_NAME = %22
 Valu
@@ -2527,51 +2527,8 @@
 tion
-, rewriting any that would be masked%0A    //
  and
@@ -2683,534 +2683,8 @@
 E);%0A
-    values = constraints.getValues(PASSTHROUGH_VALUE_PROPERTY_NAME);%0A    if (values != null) %7B%0A      if (constraints.isOptional(PASSTHROUGH_VALUE_PROPERTY_NAME)) %7B%0A        requirementConstraintsBuilder.withOptional(VALUE_PROPERTY_NAME);%0A      %7D%0A      requirementConstraintsBuilder.withoutAny(PASSTHROUGH_VALUE_PROPERTY_NAME);%0A      if (values.isEmpty()) %7B%0A        requirementConstraintsBuilder.withAny(VALUE_PROPERTY_NAME);%0A      %7D else %7B%0A        requirementConstraintsBuilder.with(VALUE_PROPERTY_NAME, values);%0A      %7D%0A    %7D%0A
     
@@ -5270,24 +5270,138 @@
     %7D%0A    %7D%0A
+    resultPropertiesBuilder.withoutAny(VALUE_PROPERTY_NAME).with(VALUE_PROPERTY_NAME, inputValue.getValueName());%0A
     resultPr

Fix to the weight function.