/** 
 * @file llunit.h
 * @brief Unit conversion classes
 *
 * $LicenseInfo:firstyear=2001&license=viewerlgpl$
 * Second Life Viewer Source Code
 * Copyright (C) 2012, Linden Research, Inc.
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation;
 * version 2.1 of the License only.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 * 
 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 * $/LicenseInfo$
 */

#ifndef LL_UNITTYPE_H
#define LL_UNITTYPE_H

#include "stdtypes.h"
#include "llpreprocessor.h"
#include "llerror.h"

//lightweight replacement of type traits for simple type equality check
template<typename S, typename T> 
struct LLIsSameType
{
	static const bool value = false;
};

template<typename T>
struct LLIsSameType<T, T>
{
	static const bool value = true;
};

// workaround for decltype() not existing and typeof() not working inline in gcc 4.2
template<typename S, typename T>
struct LLResultTypeAdd
{
	typedef LL_TYPEOF(S() + T()) type_t;
};

template<typename S, typename T>
struct LLResultTypeSubtract
{
	typedef LL_TYPEOF(S() - T()) type_t;
};

template<typename S, typename T>
struct LLResultTypeMultiply
{
	typedef LL_TYPEOF(S() * T()) type_t;
};

template<typename S, typename T>
struct LLResultTypeDivide
{
	typedef LL_TYPEOF(S() / T(1)) type_t;
};

template<typename S, typename T>
struct LLResultTypePromote
{
	typedef LL_TYPEOF((true) ? S() : T()) type_t;
};

template<typename STORAGE_TYPE, typename UNITS>
struct LLUnit
{
	typedef LLUnit<STORAGE_TYPE, UNITS> self_t;
	typedef STORAGE_TYPE storage_t;
	typedef void is_unit_t;

	// value initialization
	LL_FORCE_INLINE explicit LLUnit(storage_t value = storage_t())
	:	mValue(value)
	{}


	LL_FORCE_INLINE static self_t convert(self_t v) 
	{ 
		return v;
	}

	template<typename FROM_STORAGE_TYPE>
	LL_FORCE_INLINE static self_t convert(LLUnit<FROM_STORAGE_TYPE, UNITS> v) 
	{
		self_t result;
		result.mValue = (STORAGE_TYPE)v.value();
		return result;
	}

	template<typename FROM_UNITS>
	LL_FORCE_INLINE static self_t convert(LLUnit<STORAGE_TYPE, FROM_UNITS> v) 
	{
		self_t result;
		STORAGE_TYPE divisor = ll_convert_units(v, result);
		result.mValue /= divisor;
		return result;
	}

	template<typename FROM_STORAGE_TYPE, typename FROM_UNITS>
	LL_FORCE_INLINE static self_t convert(LLUnit<FROM_STORAGE_TYPE, FROM_UNITS> v) 
	{ 
		typedef typename LLResultTypePromote<FROM_STORAGE_TYPE, STORAGE_TYPE>::type_t result_storage_t;
		LLUnit<result_storage_t, UNITS> result;
		result_storage_t divisor = ll_convert_units(v, result);
		result.value(result.value() / divisor);
		return self_t(result.value());
	}


	// unit initialization and conversion
	template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS>
	LL_FORCE_INLINE LLUnit(LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS> other)
	:	mValue(convert(other).mValue)
	{}
	
	LL_FORCE_INLINE storage_t value() const
	{
		return mValue;
	}

	LL_FORCE_INLINE void value(const storage_t& value)
	{
		mValue = value;
	}

	template<typename NEW_UNITS> 
	storage_t valueInUnits() const
	{
		return LLUnit<storage_t, NEW_UNITS>(*this).value();
	}

	template<typename NEW_UNITS> 
	void valueInUnits(const storage_t& value) const
	{
		*this = LLUnit<storage_t, NEW_UNITS>(value);
	}

    LL_FORCE_INLINE operator storage_t() const
    {
        return value();
    }

    /*LL_FORCE_INLINE self_t& operator= (storage_t v)
	{
		value(v);
        return *this;
	}*/

	LL_FORCE_INLINE void operator += (self_t other)
	{
		mValue += convert(other).mValue;
	}

	LL_FORCE_INLINE void operator -= (self_t other)
	{
		mValue -= convert(other).mValue;
	}

	LL_FORCE_INLINE void operator *= (const storage_t& multiplicand)
	{
		mValue *= multiplicand;
	}

	LL_FORCE_INLINE void operator *= (const self_t& multiplicand)
	{
		// spurious use of dependent type to stop gcc from triggering the static assertion before instantiating the template
		LL_BAD_TEMPLATE_INSTANTIATION(STORAGE_TYPE, "Multiplication of unit types not supported.");
	}

	LL_FORCE_INLINE void operator /= (const storage_t& divisor)
	{
		mValue /= divisor;
	}

	void operator /= (const self_t& divisor)
	{
		// spurious use of dependent type to stop gcc from triggering the static assertion before instantiating the template
		LL_BAD_TEMPLATE_INSTANTIATION(STORAGE_TYPE, "Illegal in-place division of unit types.");
	}

	template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS>
	LL_FORCE_INLINE bool operator == (const LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS>& other) const
	{
		return mValue == convert(other).value();
	}

	template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS>
	LL_FORCE_INLINE bool operator != (const LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS>& other) const
	{
		return mValue != convert(other).value();
	}

	template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS>
	LL_FORCE_INLINE bool operator < (const LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS>& other) const
	{
		return mValue < convert(other).value();
	}

	template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS>
	LL_FORCE_INLINE bool operator <= (const LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS>& other) const
	{
		return mValue <= convert(other).value();
	}

	template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS>
	LL_FORCE_INLINE bool operator > (const LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS>& other) const
	{
		return mValue > convert(other).value();
	}

	template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS>
	LL_FORCE_INLINE bool operator >= (const LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS>& other) const
	{
		return mValue >= convert(other).value();
	}

protected:
	storage_t mValue;
};

template<typename STORAGE_TYPE, typename UNITS>
std::ostream& operator <<(std::ostream& s, const LLUnit<STORAGE_TYPE, UNITS>& unit)
{
        s << unit.value() << UNITS::getUnitLabel();
        return s;
}

template<typename STORAGE_TYPE, typename UNITS>
std::istream& operator >>(std::istream& s, LLUnit<STORAGE_TYPE, UNITS>& unit)
{
        STORAGE_TYPE val;
        s >> val;
        unit.value(val);
        return s;
}

template<typename STORAGE_TYPE, typename UNITS>
struct LLUnitImplicit : public LLUnit<STORAGE_TYPE, UNITS>
{
	typedef LLUnitImplicit<STORAGE_TYPE, UNITS> self_t;
	typedef typename LLUnit<STORAGE_TYPE, UNITS>::storage_t storage_t;
	typedef LLUnit<STORAGE_TYPE, UNITS> base_t;

	LL_FORCE_INLINE LLUnitImplicit(storage_t value = storage_t())
	:	base_t(value)
	{}

	template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS>
	LL_FORCE_INLINE LLUnitImplicit(LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS> other)
	:	base_t(other)
	{}

	// unlike LLUnit, LLUnitImplicit is *implicitly* convertable to a POD value (F32, S32, etc)
	// this allows for interoperability with legacy code
	LL_FORCE_INLINE operator storage_t() const
	{
		return base_t::value();
	}

	using base_t::operator +=;
	LL_FORCE_INLINE void operator += (storage_t value)
	{
        base_t::mValue += value;
	}

	// this overload exists to explicitly catch use of another implicit unit
	// without ambiguity between conversion to storage_t vs conversion to base_t
	template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS>
	LL_FORCE_INLINE void operator += (LLUnitImplicit<OTHER_STORAGE_TYPE, OTHER_UNITS> other)
	{
        base_t::mValue += base_t::convert(other).value();
	}

	using base_t::operator -=;
	LL_FORCE_INLINE void operator -= (storage_t value)
	{
        base_t::mValue -= value;
	}

	// this overload exists to explicitly catch use of another implicit unit
	// without ambiguity between conversion to storage_t vs conversion to base_t
	template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS>
	LL_FORCE_INLINE void operator -= (LLUnitImplicit<OTHER_STORAGE_TYPE, OTHER_UNITS> other)
	{
        base_t::mValue -= base_t::convert(other).value();
	}

	template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS>
	LL_FORCE_INLINE bool operator == (LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const
	{
		return base_t::mValue == base_t::convert(other).value();
	}

	template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS>
	LL_FORCE_INLINE bool operator == (LLUnitImplicit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const
	{
		return base_t::mValue == base_t::convert(other).value();
	}

	template<typename STORAGE_T>
	LL_FORCE_INLINE bool operator == (STORAGE_T other) const
	{
		return base_t::mValue == other;
	}

	template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS>
	LL_FORCE_INLINE bool operator != (LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const
	{
		return base_t::mValue != convert(other).value();
	}

	template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS>
	LL_FORCE_INLINE bool operator != (LLUnitImplicit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const
	{
		return base_t::mValue != base_t::convert(other).value();
	}

	template<typename STORAGE_T>
	LL_FORCE_INLINE bool operator != (STORAGE_T other) const
	{
		return base_t::mValue != other;
	}
	
	template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS>
	LL_FORCE_INLINE bool operator < (LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const
	{
		return base_t::mValue < base_t::convert(other).value();
	}

	template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS>
	LL_FORCE_INLINE bool operator < (LLUnitImplicit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const
	{
		return base_t::mValue < base_t::convert(other).value();
	}

	template<typename STORAGE_T>
	LL_FORCE_INLINE bool operator < (STORAGE_T other) const
	{
		return base_t::mValue < other;
	}

	template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS>
	LL_FORCE_INLINE bool operator <= (LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const
	{
		return base_t::mValue <= base_t::convert(other).value();
	}

	template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS>
	LL_FORCE_INLINE bool operator <= (LLUnitImplicit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const
	{
		return base_t::mValue <= base_t::convert(other).value();
	}

	template<typename STORAGE_T>
	LL_FORCE_INLINE bool operator <= (STORAGE_T other) const
	{
		return base_t::mValue <= other;
	}

	template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS>
	LL_FORCE_INLINE bool operator > (LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const
	{
		return base_t::mValue > base_t::convert(other).value();
	}

	template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS>
	LL_FORCE_INLINE bool operator > (LLUnitImplicit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const
	{
		return base_t::mValue > base_t::convert(other).value();
	}

	template<typename STORAGE_T>
	LL_FORCE_INLINE bool operator > (STORAGE_T other) const
	{
		return base_t::mValue > other;
	}

	template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS>
	LL_FORCE_INLINE bool operator >= (LLUnit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const
	{
		return base_t::mValue >= base_t::convert(other).value();
	}

	template<typename OTHER_STORAGE_TYPE, typename OTHER_UNITS>
	LL_FORCE_INLINE bool operator >= (LLUnitImplicit<OTHER_STORAGE_TYPE, OTHER_UNITS> other) const
	{
		return base_t::mValue >= base_t::convert(other).value();
	}

	template<typename STORAGE_T>
	LL_FORCE_INLINE bool operator >= (STORAGE_T other) const
	{
		return base_t::mValue >= other;
	}
};

template<typename STORAGE_TYPE, typename UNITS>
std::ostream& operator <<(std::ostream& s, const LLUnitImplicit<STORAGE_TYPE, UNITS>& unit)
{
        s << unit.value() << UNITS::getUnitLabel();
        return s;
}

template<typename STORAGE_TYPE, typename UNITS>
std::istream& operator >>(std::istream& s, LLUnitImplicit<STORAGE_TYPE, UNITS>& unit)
{
        STORAGE_TYPE val;
        s >> val;
        unit = val;
        return s;
}

template<typename S1, typename T1, typename S2, typename T2>
LL_FORCE_INLINE S2 ll_convert_units(LLUnit<S1, T1> in, LLUnit<S2, T2>& out)
{
	S2 divisor(1);

	LL_STATIC_ASSERT((LLIsSameType<T1, T2>::value 
						|| !LLIsSameType<T1, typename T1::base_unit_t>::value 
						|| !LLIsSameType<T2, typename T2::base_unit_t>::value), 
						"conversion requires compatible units");

	if (LLIsSameType<T1, T2>::value)
	{
		// T1 and T2 same type, just assign
		out.value((S2)in.value());
	}
	else if (T1::sLevel > T2::sLevel)
	{
		// reduce T1
		LLUnit<S2, typename T1::base_unit_t> new_in;
		divisor *= (S2)ll_convert_units(in, new_in);
		divisor *= (S2)ll_convert_units(new_in, out);
	}
	else
	{
		// reduce T2
		LLUnit<S2, typename T2::base_unit_t> new_out;
		divisor *= (S2)ll_convert_units(in, new_out);
		divisor *= (S2)ll_convert_units(new_out, out);
	}
	return divisor;
}

template<typename T>
struct LLStorageType
{
	typedef T type_t;
};

template<typename STORAGE_TYPE, typename UNITS>
struct LLStorageType<LLUnit<STORAGE_TYPE, UNITS> >
{
	typedef STORAGE_TYPE type_t;
};

// all of these operators need to perform type promotion on the storage type of the units, so they 
// cannot be expressed as operations on identical types with implicit unit conversion 
// e.g. typeof(S32Bytes(x) + F32Megabytes(y)) <==> F32Bytes

//
// operator +
//
template<typename STORAGE_TYPE1, typename UNITS1, typename STORAGE_TYPE2, typename UNITS2>
LL_FORCE_INLINE LLUnit<typename LLResultTypeAdd<STORAGE_TYPE1, STORAGE_TYPE2>::type_t, UNITS1> operator + (LLUnit<STORAGE_TYPE1, UNITS1> first, LLUnit<STORAGE_TYPE2, UNITS2> second)
{
	LLUnit<typename LLResultTypeAdd<STORAGE_TYPE1, STORAGE_TYPE2>::type_t, UNITS1> result(first);
	result += second;
	return result;
}

template<typename STORAGE_TYPE, typename UNITS, typename UNITLESS>
LLUnit<STORAGE_TYPE, UNITS> operator + (LLUnit<STORAGE_TYPE, UNITS> first, UNITLESS second)
{
	LL_BAD_TEMPLATE_INSTANTIATION(STORAGE_TYPE, "operator + requires compatible unit types");
	return LLUnit<STORAGE_TYPE, UNITS>(0);
}

template<typename STORAGE_TYPE, typename UNITS, typename UNITLESS>
LLUnit<STORAGE_TYPE, UNITS> operator + (UNITLESS first, LLUnit<STORAGE_TYPE, UNITS> second)
{
	LL_BAD_TEMPLATE_INSTANTIATION(STORAGE_TYPE, "operator + requires compatible unit types");
	return LLUnit<STORAGE_TYPE, UNITS>(0);
}

template<typename STORAGE_TYPE1, typename UNITS1, typename STORAGE_TYPE2, typename UNITS2>
LL_FORCE_INLINE LLUnitImplicit<typename LLResultTypeAdd<STORAGE_TYPE1, STORAGE_TYPE2>::type_t, UNITS1> operator + (LLUnitImplicit<STORAGE_TYPE1, UNITS1> first, LLUnitImplicit<STORAGE_TYPE2, UNITS2> second)
{
	LLUnitImplicit<typename LLResultTypeAdd<STORAGE_TYPE1, STORAGE_TYPE2>::type_t, UNITS1> result(first);
	result += second;
	return result;
}

template<typename STORAGE_TYPE1, typename UNITS1, typename STORAGE_TYPE2, typename UNITS2>
LL_FORCE_INLINE LLUnitImplicit<typename LLResultTypeAdd<STORAGE_TYPE1, STORAGE_TYPE2>::type_t, UNITS1> operator + (LLUnit<STORAGE_TYPE1, UNITS1> first, LLUnitImplicit<STORAGE_TYPE2, UNITS2> second)
{
	LLUnitImplicit<typename LLResultTypeAdd<STORAGE_TYPE1, STORAGE_TYPE2>::type_t, UNITS1> result(first);
	result += second;
	return result;
}

template<typename STORAGE_TYPE1, typename UNITS1, typename STORAGE_TYPE2, typename UNITS2>
LL_FORCE_INLINE LLUnitImplicit<typename LLResultTypeAdd<STORAGE_TYPE1, STORAGE_TYPE2>::type_t, UNITS1> operator + (LLUnitImplicit<STORAGE_TYPE1, UNITS1> first, LLUnit<STORAGE_TYPE2, UNITS2> second)
{
	LLUnitImplicit<typename LLResultTypeAdd<STORAGE_TYPE1, STORAGE_TYPE2>::type_t, UNITS1> result(first);
	result += LLUnitImplicit<STORAGE_TYPE1, UNITS1>(second);
	return result;
}

template<typename STORAGE_TYPE, typename UNITS, typename UNITLESS_TYPE>
LL_FORCE_INLINE LLUnitImplicit<typename LLResultTypeAdd<STORAGE_TYPE, typename LLStorageType<UNITLESS_TYPE>::type_t>::type_t, UNITS> operator + (LLUnitImplicit<STORAGE_TYPE, UNITS> first, UNITLESS_TYPE second)
{
	LLUnitImplicit<typename LLResultTypeAdd<STORAGE_TYPE, typename LLStorageType<UNITLESS_TYPE>::type_t>::type_t, UNITS> result(first);
	result += second;
	return result;
}

template<typename STORAGE_TYPE, typename UNITS, typename UNITLESS_TYPE>
LL_FORCE_INLINE LLUnitImplicit<typename LLResultTypeAdd<typename LLStorageType<UNITLESS_TYPE>::type_t, STORAGE_TYPE>::
	type_t, UNITS> operator + (UNITLESS_TYPE first, LLUnitImplicit<STORAGE_TYPE, UNITS> second)
{
	LLUnitImplicit<typename LLResultTypeAdd<typename LLStorageType<UNITLESS_TYPE>::type_t, STORAGE_TYPE>::type_t, UNITS> result(first);
	result += second;
	return result;
}

//
// operator -
//
template<typename STORAGE_TYPE1, typename UNITS1, typename STORAGE_TYPE2, typename UNITS2>
LL_FORCE_INLINE LLUnit<typename LLResultTypeSubtract<STORAGE_TYPE1, STORAGE_TYPE2>::type_t, UNITS1> operator - (LLUnit<STORAGE_TYPE1, UNITS1> first, LLUnit<STORAGE_TYPE2, UNITS2> second)
{
	LLUnit<typename LLResultTypeSubtract<STORAGE_TYPE1, STORAGE_TYPE2>::type_t, UNITS1> result(first);
	result -= second;
	return result;
}

template<typename STORAGE_TYPE, typename UNITS, typename UNITLESS>
LLUnit<STORAGE_TYPE, UNITS> operator - (LLUnit<STORAGE_TYPE, UNITS> first, UNITLESS second)
{
	LL_BAD_TEMPLATE_INSTANTIATION(STORAGE_TYPE, "operator - requires compatible unit types");
	return LLUnit<STORAGE_TYPE, UNITS>(0);
}

template<typename STORAGE_TYPE, typename UNITS, typename UNITLESS>
LLUnit<STORAGE_TYPE, UNITS> operator - (UNITLESS first, LLUnit<STORAGE_TYPE, UNITS> second)
{
	LL_BAD_TEMPLATE_INSTANTIATION(STORAGE_TYPE, "operator - requires compatible unit types");
	return LLUnit<STORAGE_TYPE, UNITS>(0);
}

template<typename STORAGE_TYPE1, typename UNITS1, typename STORAGE_TYPE2, typename UNITS2>
LL_FORCE_INLINE LLUnitImplicit<typename LLResultTypeSubtract<STORAGE_TYPE1, STORAGE_TYPE2>::type_t, UNITS1> operator - (LLUnitImplicit<STORAGE_TYPE1, UNITS1> first, LLUnitImplicit<STORAGE_TYPE2, UNITS2> second)
{
	LLUnitImplicit<typename LLResultTypeSubtract<STORAGE_TYPE1, STORAGE_TYPE2>::type_t, UNITS1> result(first);
	result -= second;
	return result;
}

template<typename STORAGE_TYPE1, typename UNITS1, typename STORAGE_TYPE2, typename UNITS2>
LL_FORCE_INLINE LLUnitImplicit<typename LLResultTypeSubtract<STORAGE_TYPE1, STORAGE_TYPE2>::type_t, UNITS1> operator - (LLUnit<STORAGE_TYPE1, UNITS1> first, LLUnitImplicit<STORAGE_TYPE2, UNITS2> second)
{
	LLUnitImplicit<typename LLResultTypeSubtract<STORAGE_TYPE1, STORAGE_TYPE2>::type_t, UNITS1> result(first);
	result -= second;
	return result;
}

template<typename STORAGE_TYPE1, typename UNITS1, typename STORAGE_TYPE2, typename UNITS2>
LL_FORCE_INLINE LLUnitImplicit<typename LLResultTypeSubtract<STORAGE_TYPE1, STORAGE_TYPE2>::type_t, UNITS1> operator - (LLUnitImplicit<STORAGE_TYPE1, UNITS1> first, LLUnit<STORAGE_TYPE2, UNITS2> second)
{
	LLUnitImplicit<typename LLResultTypeSubtract<STORAGE_TYPE1, STORAGE_TYPE2>::type_t, UNITS1> result(first);
	result -= LLUnitImplicit<STORAGE_TYPE1, UNITS1>(second);
	return result;
}

template<typename STORAGE_TYPE, typename UNITS, typename UNITLESS_TYPE>
LL_FORCE_INLINE LLUnitImplicit<typename LLResultTypeSubtract<STORAGE_TYPE, typename LLStorageType<UNITLESS_TYPE>::type_t>::type_t, UNITS> operator - (LLUnitImplicit<STORAGE_TYPE, UNITS> first, UNITLESS_TYPE second)
{
	LLUnitImplicit<typename LLResultTypeSubtract<STORAGE_TYPE, typename LLStorageType<UNITLESS_TYPE>::type_t>::type_t, UNITS> result(first);
	result -= second;
	return result;
}

template<typename STORAGE_TYPE, typename UNITS, typename UNITLESS_TYPE>
LL_FORCE_INLINE LLUnitImplicit<typename LLResultTypeSubtract<typename LLStorageType<UNITLESS_TYPE>::type_t, STORAGE_TYPE>::type_t, UNITS> operator - (UNITLESS_TYPE first, LLUnitImplicit<STORAGE_TYPE, UNITS> second)
{
	LLUnitImplicit<typename LLResultTypeSubtract<typename LLStorageType<UNITLESS_TYPE>::type_t, STORAGE_TYPE>::type_t, UNITS> result(first);
	result -= second;
	return result;
}

//
// operator *
//
template<typename STORAGE_TYPE1, typename UNITS1, typename STORAGE_TYPE2, typename UNITS2>
LLUnit<STORAGE_TYPE1, UNITS1> operator * (LLUnit<STORAGE_TYPE1, UNITS1>, LLUnit<STORAGE_TYPE2, UNITS2>)
{
	// spurious use of dependent type to stop gcc from triggering the static assertion before instantiating the template
	LL_BAD_TEMPLATE_INSTANTIATION(STORAGE_TYPE1, "multiplication of unit types results in new unit type - not supported.");
	return LLUnit<STORAGE_TYPE1, UNITS1>();
}

template<typename STORAGE_TYPE, typename UNITS, typename UNITLESS_TYPE>
LL_FORCE_INLINE LLUnit<typename LLResultTypeMultiply<STORAGE_TYPE, typename LLStorageType<UNITLESS_TYPE>::type_t>::type_t, UNITS> operator * (LLUnit<STORAGE_TYPE, UNITS> first, UNITLESS_TYPE second)
{
	return LLUnit<typename LLResultTypeMultiply<STORAGE_TYPE, typename LLStorageType<UNITLESS_TYPE>::type_t>::type_t, UNITS>(first.value() * second);
}

template<typename STORAGE_TYPE, typename UNITS, typename UNITLESS_TYPE>
LL_FORCE_INLINE LLUnit<typename LLResultTypeMultiply<typename LLStorageType<UNITLESS_TYPE>::type_t, STORAGE_TYPE>::type_t, UNITS> operator * (UNITLESS_TYPE first, LLUnit<STORAGE_TYPE, UNITS> second)
{
	return LLUnit<typename LLResultTypeMultiply<typename LLStorageType<UNITLESS_TYPE>::type_t, STORAGE_TYPE>::type_t, UNITS>(first * second.value());
}

template<typename STORAGE_TYPE1, typename UNITS1, typename STORAGE_TYPE2, typename UNITS2>
LLUnitImplicit<STORAGE_TYPE1, UNITS1> operator * (LLUnitImplicit<STORAGE_TYPE1, UNITS1>, LLUnitImplicit<STORAGE_TYPE2, UNITS2>)
{
	// spurious use of dependent type to stop gcc from triggering the static assertion before instantiating the template
	LL_BAD_TEMPLATE_INSTANTIATION(STORAGE_TYPE1, "multiplication of unit types results in new unit type - not supported.");
	return LLUnitImplicit<STORAGE_TYPE1, UNITS1>();
}

template<typename STORAGE_TYPE, typename UNITS, typename UNITLESS_TYPE>
LL_FORCE_INLINE LLUnitImplicit<typename LLResultTypeMultiply<STORAGE_TYPE, typename LLStorageType<UNITLESS_TYPE>::type_t>::type_t, UNITS> operator * (LLUnitImplicit<STORAGE_TYPE, UNITS> first, UNITLESS_TYPE second)
{
	return LLUnitImplicit<typename LLResultTypeMultiply<STORAGE_TYPE, UNITLESS_TYPE>::type_t, UNITS>(first.value() * second);
}

template<typename STORAGE_TYPE, typename UNITS, typename UNITLESS_TYPE>
LL_FORCE_INLINE LLUnitImplicit<typename LLResultTypeMultiply<typename LLStorageType<UNITLESS_TYPE>::type_t, STORAGE_TYPE>::type_t, UNITS> operator * (UNITLESS_TYPE first, LLUnitImplicit<STORAGE_TYPE, UNITS> second)
{
	return LLUnitImplicit<typename LLResultTypeMultiply<typename LLStorageType<UNITLESS_TYPE>::type_t, STORAGE_TYPE>::type_t, UNITS>(first * second.value());
}


//
// operator /
//

template<typename STORAGE_TYPE, typename UNITS, typename UNITLESS_TYPE>
LL_FORCE_INLINE LLUnit<typename LLResultTypeDivide<STORAGE_TYPE, typename LLStorageType<UNITLESS_TYPE>::type_t>::type_t, UNITS> operator / (LLUnit<STORAGE_TYPE, UNITS> first, UNITLESS_TYPE second)
{
	return LLUnit<typename LLResultTypeDivide<STORAGE_TYPE, typename LLStorageType<UNITLESS_TYPE>::type_t>::type_t, UNITS>(first.value() / second);
}

template<typename STORAGE_TYPE1, typename UNITS1, typename STORAGE_TYPE2, typename UNITS2>
LL_FORCE_INLINE typename LLResultTypeDivide<STORAGE_TYPE1, STORAGE_TYPE2>::type_t operator / (LLUnit<STORAGE_TYPE1, UNITS1> first, LLUnit<STORAGE_TYPE2, UNITS2> second)
{
	return first.value() / first.convert(second).value();
}

template<typename STORAGE_TYPE, typename UNITS, typename UNITLESS_TYPE>
LL_FORCE_INLINE LLUnitImplicit<typename LLResultTypeDivide<STORAGE_TYPE, typename LLStorageType<UNITLESS_TYPE>::type_t>::type_t, UNITS> operator / (LLUnitImplicit<STORAGE_TYPE, UNITS> first, UNITLESS_TYPE second)
{
	return LLUnitImplicit<typename LLResultTypeDivide<STORAGE_TYPE, typename LLStorageType<UNITLESS_TYPE>::type_t>::type_t, UNITS>(first.value() / second);
}

template<typename STORAGE_TYPE1, typename UNITS1, typename STORAGE_TYPE2, typename UNITS2>
LL_FORCE_INLINE typename LLResultTypeDivide<STORAGE_TYPE1, STORAGE_TYPE2>::type_t operator / (LLUnitImplicit<STORAGE_TYPE1, UNITS1> first, LLUnitImplicit<STORAGE_TYPE2, UNITS2> second)
{
	return (typename LLResultTypeDivide<STORAGE_TYPE1, STORAGE_TYPE2>::type_t)(first.value() / first.convert(second).value());
}

template<typename STORAGE_TYPE1, typename UNITS1, typename STORAGE_TYPE2, typename UNITS2>
LL_FORCE_INLINE typename LLResultTypeDivide<STORAGE_TYPE1, STORAGE_TYPE2>::type_t operator / (LLUnit<STORAGE_TYPE1, UNITS1> first, LLUnitImplicit<STORAGE_TYPE2, UNITS2> second)
{
	return (typename LLResultTypeDivide<STORAGE_TYPE1, STORAGE_TYPE2>::type_t)(first.value() / first.convert(second).value());
}

template<typename STORAGE_TYPE1, typename UNITS1, typename STORAGE_TYPE2, typename UNITS2>
LL_FORCE_INLINE typename LLResultTypeDivide<STORAGE_TYPE1, STORAGE_TYPE2>::type_t operator / (LLUnitImplicit<STORAGE_TYPE1, UNITS1> first, LLUnit<STORAGE_TYPE2, UNITS2> second)
{
	return (typename LLResultTypeDivide<STORAGE_TYPE1, STORAGE_TYPE2>::type_t)(first.value() / first.convert(second).value());
}

template<typename T> 
struct LLGetUnitLabel
{
	static const char* getUnitLabel() { return ""; }
};

template<typename T, typename STORAGE_T>
struct LLGetUnitLabel<LLUnit<STORAGE_T, T> >
{
	static const char* getUnitLabel() { return T::getUnitLabel(); }
};

template<typename T>
struct LLUnitLinearOps
{
	typedef LLUnitLinearOps<T> self_t;

	LLUnitLinearOps(T val) 
	:	mValue(val),
		mDivisor(1)
	{}

	template<typename OTHER_T>
	self_t operator * (OTHER_T other)
	{
		return mValue * other;
	}

	template<typename OTHER_T>
	self_t operator / (OTHER_T other)
	{
		mDivisor *= other;
		return *this;
	}

	template<typename OTHER_T>
	self_t operator + (OTHER_T other)
	{
		mValue += other * mDivisor;
		return *this;
	}

	template<typename OTHER_T>
	self_t operator - (OTHER_T other)
	{
		mValue -= other * mDivisor;
		return *this;
	}

	T mValue;
	T mDivisor;
};

template<typename T>
struct LLUnitInverseLinearOps
{
	typedef LLUnitInverseLinearOps<T> self_t;

	LLUnitInverseLinearOps(T val) 
	:	mValue(val),
		mDivisor(1),
		mMultiplicand(1)
	{}

	template<typename OTHER_T>
	self_t operator * (OTHER_T other)
	{
		mDivisor *= other;
		return *this;
	}

	template<typename OTHER_T>
	self_t operator / (OTHER_T other)
	{
		mValue *= other;
		mMultiplicand *= other;
		return *this;
	}

	template<typename OTHER_T>
	self_t operator + (OTHER_T other)
	{
		mValue -= other * mMultiplicand;
		return *this;
	}

	template<typename OTHER_T>
	self_t operator - (OTHER_T other)
	{
		mValue += other * mMultiplicand;
		return *this;
	}

	T mValue;
	T mDivisor;
	T mMultiplicand;
};

#define LL_DECLARE_BASE_UNIT(base_unit_name, unit_label)                                             \
struct base_unit_name                                                                                \
{                                                                                                    \
	static const int sLevel = 0;                                                                     \
	typedef base_unit_name base_unit_t;                                                              \
	static const char* getUnitLabel() { return unit_label; }                                         \
	template<typename T>                                                                             \
	static LLUnit<T, base_unit_name> fromValue(T value) { return LLUnit<T, base_unit_name>(value); } \
	template<typename STORAGE_T, typename UNIT_T>                                                    \
	static LLUnit<STORAGE_T, base_unit_name> fromValue(LLUnit<STORAGE_T, UNIT_T> value)              \
	{ return LLUnit<STORAGE_T, base_unit_name>(value); }                                             \
}


#define LL_DECLARE_DERIVED_UNIT(unit_name, unit_label, base_unit_name, conversion_operation)	 \
struct unit_name                                                                                 \
{                                                                                                \
	static const int sLevel = base_unit_name::sLevel + 1;                                        \
	typedef base_unit_name base_unit_t;                                                          \
	static const char* getUnitLabel() { return unit_label; }									 \
	template<typename T>                                                                         \
	static LLUnit<T, unit_name> fromValue(T value) { return LLUnit<T, unit_name>(value); }		 \
	template<typename STORAGE_T, typename UNIT_T>                                                \
	static LLUnit<STORAGE_T, unit_name> fromValue(LLUnit<STORAGE_T, UNIT_T> value)				 \
	{ return LLUnit<STORAGE_T, unit_name>(value); }												 \
};                                                                                               \
	                                                                                             \
template<typename S1, typename S2>                                                               \
LL_FORCE_INLINE S2 ll_convert_units(LLUnit<S1, unit_name> in, LLUnit<S2, base_unit_name>& out)   \
{                                                                                                \
	typedef typename LLResultTypePromote<S1, S2>::type_t result_storage_t;                       \
	LLUnitInverseLinearOps<result_storage_t> result =                                            \
		LLUnitInverseLinearOps<result_storage_t>(in.value()) conversion_operation;               \
	out = LLUnit<S2, base_unit_name>((S2)result.mValue);	                                     \
	return result.mDivisor;                                                                      \
}                                                                                                \
                                                                                                 \
template<typename S1, typename S2>                                                               \
LL_FORCE_INLINE S2 ll_convert_units(LLUnit<S1, base_unit_name> in, LLUnit<S2, unit_name>& out)   \
{                                                                                                \
	typedef typename LLResultTypePromote<S1, S2>::type_t result_storage_t;                       \
	LLUnitLinearOps<result_storage_t> result =                                                   \
		LLUnitLinearOps<result_storage_t>(in.value()) conversion_operation;				         \
	out = LLUnit<S2, unit_name>((S2)result.mValue);                                              \
	return result.mDivisor;                                                                      \
}                                                                                               

#define LL_DECLARE_UNIT_TYPEDEFS(ns, unit_name)                         \
	typedef LLUnit<F32, ns::unit_name> F32##unit_name;                  \
	typedef LLUnitImplicit<F32, ns::unit_name> F32##unit_name##Implicit;\
	typedef LLUnit<F64, ns::unit_name> F64##unit_name;                  \
	typedef LLUnitImplicit<F64, ns::unit_name> F64##unit_name##Implicit;\
	typedef LLUnit<S32, ns::unit_name> S32##unit_name;                  \
	typedef LLUnitImplicit<S32, ns::unit_name> S32##unit_name##Implicit;\
	typedef LLUnit<S64, ns::unit_name> S64##unit_name;                  \
	typedef LLUnitImplicit<S64, ns::unit_name> S64##unit_name##Implicit;\
	typedef LLUnit<U32, ns::unit_name> U32##unit_name;                  \
	typedef LLUnitImplicit<U32, ns::unit_name> U32##unit_name##Implicit;\
	typedef LLUnit<U64, ns::unit_name> U64##unit_name;                  \
	typedef LLUnitImplicit<U64, ns::unit_name> U64##unit_name##Implicit

#endif //LL_UNITTYPE_H
