
#ifndef __CDBL_H__
#define __CDBL_H__

#include "exception.h"
#include "cstring.h"

// definition of arithmetic operator ( + - * )
#define DBL_ARITH_OP(op) \
	CDbl &operator op##=(const CDbl &p_Obj) { \
		m_dVal op##= p_Obj.m_dVal; \
		m_bSet = true; \
		return *this; \
	} \
	CDbl &operator op##=(const double p_dVal) { \
		m_dVal op##= p_dVal; \
		m_bSet = true; \
		return *this; \
	} \
	CDbl operator op(const CDbl &p_Obj) { \
		return CDbl(m_dVal op p_Obj.m_dVal); \
	} \
	CDbl operator op(const double p_dVal) { \
		return CDbl(m_dVal op p_dVal); \
	} \
	friend CDbl operator op(const double p_dVal, const CDbl &p_Obj) { \
		return CDbl(p_dVal op p_Obj.m_dVal); \
	}


// definition of arithmetic operator 2(/ *)
#define DBL_ARITH_OP2(op) \
	CDbl &operator op##=(const CDbl &p_Obj) \
	{ \
		if (p_Obj.m_dVal == 0.) { \
			throw CException("operator" #op " : Divided by zero Exception"); \
		} \
		m_dVal op##= p_Obj.m_dVal; \
		m_bSet = true; \
		return *this; \
	} \
	CDbl &operator op##=(const double p_dVal) \
	{ \
		if (p_dVal == 0.) { \
			throw CException("operator" #op " : Divided by zero Exception"); \
		} \
		m_dVal op##= p_dVal; \
		m_bSet = true; \
		return *this; \
	} \
	CDbl operator op(const CDbl &p_Obj) \
	{ \
		if (p_Obj.m_dVal == 0.) { \
			throw CException("operator" #op " : Divided by zero Exception"); \
		} \
		return CDbl(m_dVal op p_Obj.m_dVal); \
	} \
	CDbl operator op(const double p_dVal) \
	{ \
		if (p_dVal == 0.) { \
			throw CException("operator" #op " : Divided by zero Exception"); \
		} \
		return CDbl(m_dVal op p_dVal); \
	} \
	friend CDbl operator op(double p_dVal, const CDbl &p_Obj) \
	{ \
		if (p_Obj.m_dVal == 0.) { \
			throw CException("operator" #op " : Divided by zero Exception"); \
		} \
		return CDbl(p_dVal op p_Obj.m_dVal); \
	}


// definition of comparison operator
#define DBL_COMP_OPERATOR(op) \
	bool operator op(const CDbl &p_Obj) const { \
		return (m_dVal op p_Obj.m_dVal) ? true : false; \
	} \
	bool operator op(const double p_dVal) const { \
		return (m_dVal op p_dVal) ? true : false; \
	} \
	friend bool operator op(const double p_dVal, const CDbl &p_Obj) { \
		return (p_dVal op p_Obj.m_dVal) ? true : false; \
	}


class V2_CLASS_API CDbl
{
private:
	double m_dVal;
	bool m_bSet;

public:
	CDbl() {
		m_dVal = 0.;
		m_bSet = false;
	}

	CDbl(const double p_dVal) {
		m_dVal = p_dVal;
		m_bSet = true;
	}
	
	CDbl(const int p_iVal) {
		m_dVal = (double) p_iVal;
		m_bSet = true;
	}

	CDbl(const CDbl &p_Obj) {
		m_dVal = p_Obj.m_dVal;
		m_bSet = p_Obj.m_bSet;
	}

	~CDbl() {
		m_dVal = 0.;
		m_bSet = false;
	}

	void Init() {
		m_dVal = 0.;
		m_bSet = false;
	}

	void SetNull() {
		Init();
	}

	bool IsValid() const {
		return m_bSet;
	}

	int ToInt() const {
		return (int)m_dVal;
	}
	
	double ToDbl() const {
		return m_dVal;
	}

	CString ToFmtStr(int p_iDecPlace=0, bool p_bComma=true) const;
	
	operator double() const {
		return m_dVal;
	}

	CString Repr(const char p_cFmt='f', const int p_iPrec=-1) const {
		if (m_bSet) {
			if (p_cFmt == 'e') {
				if (p_iPrec >= 0) {
					return CFormat("<CDbl: %.*e>", p_iPrec, m_dVal);
				} else {
					return CFormat("<CDbl: %e>", m_dVal);
				}
			} else {
				if (p_iPrec >= 0) {
					return CFormat("<CDbl: %.*f>", p_iPrec, m_dVal);
				} else {
					return CFormat("<CDbl: %f>", m_dVal);
				}
			}
		}
		return CString("<CDbl: unset>");
	}
	
	// assign
	CDbl &operator=(const CDbl &p_Obj) {
		m_dVal = p_Obj.m_dVal;
		m_bSet = p_Obj.m_bSet;
		return *this;
	}

	CDbl &operator=(const double p_dVal) {
		m_dVal = p_dVal;
		m_bSet = true;
		return *this;
	}
	// unary
	CDbl operator+(void) {
		return *this;
	}
	CDbl operator-(void) {
		m_dVal *= -1.;
		return *this;
	}

	// arithmetic operators
	DBL_ARITH_OP(+);	// plus
	DBL_ARITH_OP(-);	// minus
	DBL_ARITH_OP(*);	// multiply
	DBL_ARITH_OP2(/);	// divide
	// no modulus in double

	// compare
	DBL_COMP_OPERATOR(==);
	DBL_COMP_OPERATOR(!=);
	DBL_COMP_OPERATOR(>);
	DBL_COMP_OPERATOR(>=);
	DBL_COMP_OPERATOR(<);
	DBL_COMP_OPERATOR(<=);
};

#endif	// __CDBL_H__

