/* Distributed under the Apache License, Version 2.0.
   See accompanying NOTICE file for details.*/

//----------------------------------------------------------------------------
/// @file CompoundUnitElement.h
/// @author Chris Volpe
/// 
/// This class contains a representation of a single element in a Compound Unit.
/// A Compound Unit is therefore a product of these.
//----------------------------------------------------------------------------
#pragma once

#include "cdm/utils/unitconversion/SnapValue.h"

class CCompoundUnitElement
{
public:
  // The use of CSnapValue as the exponent type allows for fractional exponents
  // without resulting in "almost integer" exponents. Why do we want fractional
  // exponents? Well, it allows you to take roots of quantities.
  // For example, if I have a square area given in units of acres and I want
  // to know the length of the square, I can compute the square root with the result
  // being expressed in units of acres^[1/2]. This is a perfectly legitimate unit of
  // distance and can be converted to feet, meters, etc by our existing algorithm. The 
  // obvious point that one might make in response is that since any "square-rootable"
  // unit must ultimately have dimensions with even exponents, why not just convert the
  // unit to its expansion into fundamental units and divide those exponents, which must
  // be even, by two? Well, that would either require collusion with client code to do 
  // the value conversion, or would require maintaining a separate fudge-factor within
  // the CompoundUnit structure itself. This seems like a hack. After all, since any
  // unit can be squared and cubed, it should be possible to un-square or un-cube any
  // unit whose dimensions are perfect squares and cubes, respectively. 
  typedef CSnapValue ExponentType;

  // Need a parameterless constructor to be used in a vector
  CCompoundUnitElement()
  {
    // nothing
  }

  CCompoundUnitElement(int unitId, ExponentType exponent = 1.0, int prefixID = -1)
    :m_iUnitID(unitId), m_CExponent(exponent), m_iPrefixID(prefixID)
  {
    // nothing
  };

  CCompoundUnitElement(const CCompoundUnitElement &src)
    :m_iUnitID(src.m_iUnitID), m_CExponent(src.m_CExponent), m_iPrefixID(src.m_iPrefixID)
  {
    // nothing
  };

  CCompoundUnitElement & operator=(const CCompoundUnitElement &rhs)
  {
    if (this != &rhs)
    {
      m_iUnitID = rhs.m_iUnitID;
      m_CExponent = rhs.m_CExponent;
      m_iPrefixID = rhs.m_iPrefixID;
    }
    return *this;
  };

  // Setters/Getters
  void SetUnitID(int unitID)
  {
    m_iUnitID = unitID;
  };

  int GetUnitID() const
  {
    return m_iUnitID;
  }

  void SetExponent(const ExponentType &exponent)
  {
    m_CExponent = exponent;
  };

  void AddExponent(const ExponentType &exponent)
  {
    m_CExponent += exponent;
  }

  void MultExponent(const ExponentType &exppwr)
  {
    m_CExponent *= exppwr;
  }

  void SubtractExponent(const ExponentType &exponent)
  {
    m_CExponent -= exponent;
  }

  const ExponentType & GetExponent() const
  {
    return m_CExponent;
  }

  void SetPrefixID(int prefixID)
  {
    m_iPrefixID = prefixID;
  }
  
  int GetPrefixID() const
  {
    return m_iPrefixID;
  }


  // http://support.microsoft.com/kb/168958 says we need to define operators < and ==
  // for this if we want to export the vector of these contained in CompoundUnit. It
  // even says we can just return true if there's no sensible interpretation of
  // "operator<" for this class.
  bool operator< (const CCompoundUnitElement&) const
  {
    return true; // Dummy implementation
  }

  bool operator== (const CCompoundUnitElement &ref) const
  {
    return ((m_iUnitID == ref.m_iUnitID) &&
        (m_CExponent == ref.m_CExponent) &&
        (m_iPrefixID == ref.m_iPrefixID));
  }

  // Convenience method for moving a CompoundUnitElement from the numerator to 
  // the denominator, or vice versa
  void Invert()
  {
    m_CExponent *= -1;
  }

  double GetBigness() const;
  double GetBias() const;
  bool IsDecibel() const;

private:
  int m_iUnitID;
  ExponentType m_CExponent;
  int m_iPrefixID;
};
