#ifndef __CompositeValue_h__
#define __CompositeValue_h__

/////////////////////////////////////////////////////////////////////////////
// CompositeValue.h | Declaration of the TCCompositeValue template class.


/////////////////////////////////////////////////////////////////////////////
// TCCompositeValue provides a mechanism to evaluate a series of values and
// to determine a composite value to represent the series. This process is
// useful in a property page when multiple objects' properties are being
// displayed.
//
// Take the case of a check box that displays the state of a booean property.
// When only one object is being edited by the property page, the check box
// can be set to "checked" if the property is true, or "unchecked" if false.
// However, if the same boolean propery exists in multiple objects, the check
// box should only be set to "checked" if the property is true in *all* of
// the objects. If the property differs in one or more of the objects, the
// check box would be set to "indeterminate". If, when applying changes, the
// check box is still "indeterminate", the boolean property would not be
// applied to any of the objects. But, if the check box had been changed to
// either "checked" or "unchecked", the *same* boolean value would be applied
// to *all* of the objects.
//
// To use this template, declare a local variable in the scope where the
// series is being evaluated:
//
//      int DetermineCheckState(int cBooleans, bool rgBooleans[])
//      {
//        TCCompositeValue<int> bResult(2, 0);
//
// In this example, the int template parameter indicates that the resulting
// value will be an int, suitable as this function's return value, which can
// be passed to the BM_SETCHECK message. The first parameter to the
// constructor is 2, which specifies the resulting value if any value in the
// evaluated series is not equal to the others. The second parameters, 0, is
// the initial resulting value. This can be thought of as the default value
// of the object until, and if, the first value of the series is evaluated.
// This value is *not* compared against when evaluating the series.
//
// Next, you would iterate through the series of values, passing each to the
// Evaluate method:
//
//        for (int i = 0; i < cBooleans; ++i)
//        {
//          if (!bResult.Evaluate(rgBooleans[i]))
//            break;
//        }
//
// This example stops iterating through the values upon finding a value that
// is not equal to the others, indicated by the result of the Evaluate
// method. This is to avoid unnecessary processing, since continuing to
// evaluate the series would not affect the resulting value.
//
// Finally, you would make use of the resulting value:
//
//        return bResult;
//      }
//
// Notice that the object itself is returned. This is made possible by the
// cast operator, which allows the object to be used in place of a type
// /T/, int in this example. Alternately, the GetValue method could be used:
//
//        return bResult.GetValue();
//      }
//
// Note: This template can also be used with other types of values, including
// strings. However, since the class works with values, the values and the
// result must support being passed, copied, and compared by value. The MFC
// CString and the STL std::basic_string both support these semantics.
//
// TODO: Perhaps a second template parameter should be added to supply the
// binary operation to use for comparison, defaulting to std::less. This
// would allow a string comparison, for example, to be case insensitive.
//
// TODO: Perhaps this example function should be added to MMACRtl. If so, it
// should perhaps also coerce the result to the symbolic check box states. If
// the values are the same, the switch statement will probably optimize away:
//
//         switch (bResult)
//         {
//           case false: return BST_UNCHECKED;
//           case true:  return BST_CHECKED;
//           default:    return BST_BST_INDETERMINATE;
//         }
//       }
//
// Parameters:
//   T - The type of the resulting value and each value of the input series.
//
// See Also: TCPropertyPageImpl
template <class T>
class TCCompositeValue
{
// Construction
public:
  TCCompositeValue(const T& tInequalResult = T(), const T& tInitValue = T());

// Attributes
public:
  bool IsEqual() const;
  const T& GetValue() const;

// Operations
public:
  bool Evaluate(const T& tNextValue);
  void Reset(const T& tInitValue = T());

// Operators
public:
  operator const T&() const;

// Data Members
protected:
  bool m_bInit : 1;   // Flag to indicate if any values have been evaluated.
  bool m_bEqual : 1;  // Flag to indicate that all evaluated values are equal.
  T m_tInequalResult; // Resulting value if any evaluated value is unequal.
  T m_tCurrentValue;  // The current value of the object.
};


/////////////////////////////////////////////////////////////////////////////
// Group=Construction

/////////////////////////////////////////////////////////////////////////////
// Initializes the object by calling the Reset method.
//
// Internally, the m_bEqual member is set to true, indicating that all values
// evaluated up to this point (none) are equal. The m_bInit member is set to
// false, indicating that Evaluate has not yet been called.
//
// Parameters:
//   tInequalResult - The resulting value if any value in the evaluated
// series is not equal to the others.
//   tInitValue - The default value of the object until the first call to the
// Evaluate method. This value is *never* compared against when evaluating
// the series.
//
// See Also: TCCompositeValue::Evaluate, TCCompositeValue::GetValue,
// TCCompositeValue::IsEqual, TCCompositeValue::Reset
template <class T>
inline TCCompositeValue<T>::TCCompositeValue(const T& tInequalResult,
  const T& tInitValue) : m_tInequalResult(tInequalResult)
{
  Reset(tInitValue);
}


/////////////////////////////////////////////////////////////////////////////
// Group=Attributes

/////////////////////////////////////////////////////////////////////////////
// Description: Tests the equality state of the currently evaluated series.
//
// Tests the equality state of the currently evaluated series.
//
// Return Value: true if all values evaluated since construction (or the most
// recent call to Reset) have been equal. Otherwise false, indicating that at
// least one value in the series is not equal to the rest.
//
// See Also: TCCompositeValue::constructor, TCCompositeValue::Evaluate,
// TCCompositeValue::Reset, TCCompositeValue::m_bEqual
template <class T>
inline bool TCCompositeValue<T>::IsEqual() const
{
  return m_bEqual;
}

/////////////////////////////////////////////////////////////////////////////
// Description: Gets the current value of the object.
//
// Gets the current value of the object.
//
// Return Value: The current value of the object.
//
// See Also: TCCompositeValue::constructor, TCCompositeValue::cast operator,
// TCCompositeValue::m_tCurrentValue
template <class T>
inline const T& TCCompositeValue<T>::GetValue() const
{
  return m_tCurrentValue;
}


/////////////////////////////////////////////////////////////////////////////
// Group=Operations

/////////////////////////////////////////////////////////////////////////////
// Description: Evaluates the specified value against the current composite
// value, if any.
//
// Evaluates the specified value against the current composite value, if any.
//
// If this is the first time that the method has been called since
// construction (or the most recent call to Reset), the specified value is
// simply stored and the m_bInit flag is set to indicate that at least one
// value has been evaluated.
//
// Parameters:
//   tNextValue - The value to be compared to the current composite value, if
// any.
//
// Return Value: true if no unequal value has been introduced to the
// composite, otherwise false. This can also be determined using the IsEqual
// method.
//
// See Also: TCCompositeValue::constructor, TCCompositeValue::IsEqual,
// TCCompositeValue::Reset, TCCompositeValue::m_bInit
template <class T>
bool TCCompositeValue<T>::Evaluate(const T& tNextValue)
{
  if (m_bEqual)
  {
    if (m_bInit)
    {
      if (m_tCurrentValue != tNextValue)
      {
        m_tCurrentValue = m_tInequalResult;
        m_bEqual = false;
      }
    }
    else
    {
      m_bInit = true;
      m_tCurrentValue = tNextValue;
    }
  }
  return m_bEqual;
}


/////////////////////////////////////////////////////////////////////////////
// Group=Operations

/////////////////////////////////////////////////////////////////////////////
// Description: Resets the object to an initialized state.
//
// Resets the object to an initialized state. Internally, the m_bEqual member
// is set to true, indicating that all values evaluated up to this point
// (none) are equal. The m_bInit member is set to false, indicating that
// Evaluate has not yet been called.
//
// Parameters:
//   tInitValue - The default value of the object until the first call to the
// Evaluate method. This value is *never* compared against when evaluating
// the series.
//
// See Also: TCCompositeValue::constructor, TCCompositeValue::Evaluate,
// TCCompositeValue::GetValue, TCCompositeValue::IsEqual
template <class T>
inline void TCCompositeValue<T>::Reset(const T& tInitValue)
{
  m_bInit = false;
  m_bEqual = true;
  m_tCurrentValue = tInitValue;
}


/////////////////////////////////////////////////////////////////////////////
// Group=Operators

/////////////////////////////////////////////////////////////////////////////
// See Also: TCCompositeValue::GetValue
template <class T>
inline TCCompositeValue<T>::operator const T&() const
{
  return GetValue();
}


/////////////////////////////////////////////////////////////////////////////

#endif // !__CompositeValue_h__