#pragma once
#include <float.h>
#include <math.h>
#include <crtdbg.h>
#include "SIUnitInternal.h"

// Used to prevent divide by zero problems 
// Rather than using DBL_MIN (2.2250738585072014e-308), use a value that if the min is then scaled by the worst multiplier, we are still OK
const double verySmallNumber = DBL_MIN * 1e30;
const double veryBigNumber = DBL_MAX * 1e-30;

namespace NDTS
{
	namespace SCO
	{
		static const char *pppName[SI::NumberOfBaseUnits][SI::NumberOfPrefixes] =  {
			{	"Ym", "Zm", "Em", "Pm", "Tm", "Gm", "Mm", "km", "hm", "dam", "m", "dm", "cm", "mm", "um", "nm", "pm", "fm", "am", "zm", "ym"}, 
			{	"Ys", "Zs", "Es", "Ps", "Ts", "Gs", "Ms", "ks", "hs", "das", "s", "ds", "cs", "ms", "us", "ns", "ps", "fs", "as", "zs", "ys"}, 
			{	"YHz", "ZHz", "EHz", "PHz", "THz", "GHz", "MHz", "kHz", "hHz", "daHz", "Hz", "dHz", "cHz", "mHz", "uHz", "nHz", "pHz", "fHz", "aHz", "zHz", "yHz"},
			{	"***", "***", "***", "***", "***", "***", "***", "***", "***", "***", "dB",	 "***", "***", "***", "***", "***", "***", "***", "***", "***", "***"},  // dB should never be convered - see note in PropertiesInterface.h
		};
		static const double pScaler[SI::NumberOfPrefixes] = {1e24, 1e21, 1e18, 1e15, 1e12, 1e9, 1e6, 1e3, 1e2, 1e1, 1e0,  
			1e-1, 1e-2, 1e-3, 1e-6, 1e-9, 1e-12, 1e-15, 1e-18, 1e-21, 1e-24};

		SIUnitInternal::SIUnitInternal(SI::BaseUnit base, SI::Prefix prefix)
		{
			_ASSERTE(base < SI::NumberOfBaseUnits);
			_ASSERTE(prefix < SI::NumberOfPrefixes);
			this->baseUnit = base;
			this->prefix = prefix;
			if (base == SI::Hertz)
				isInverse = true;
			else
				isInverse = false;
		}
		SIUnitInternal::~SIUnitInternal()
		{
		}

		SI::BaseUnit SIUnitInternal::getBaseUnit()
		{
			return baseUnit;
		}

		SI::Prefix SIUnitInternal::getPrefix()
		{
			return prefix;
		}

		double SIUnitInternal::baseToPrefix(double value)
		{
			if (prefix == SI::NoPrefix)
				return value;
			else
			{
				if (!isInverse)
					return value / pScaler[prefix];
				else
				{
					if (abs(value) < verySmallNumber)
						return veryBigNumber;
					// An inverse unit such as frequency is stored internally as seconds
					// The scaler is scaling the inverted unit, i.e. 0.0001S SI unit -> 10000Hz -> 10Khz prefixed unit
					return (1 / value) / pScaler[prefix];
				}
			}
		}

		double SIUnitInternal::prefixToBase(double value)
		{
			if (prefix == SI::NoPrefix)
				return value;
			else
			{
				if (!isInverse)
					return value * pScaler[prefix];
				else
				{
					if (abs(value) < verySmallNumber)
						return veryBigNumber;
					//10 Khz -> 0.0001 S
					return 1 / (value * pScaler[prefix]);
				}
			}
		}
		bool SIUnitInternal::getIsInverse()
		{
			return isInverse;
		}

		const char *SIUnitInternal::getName()
		{
			return pppName[baseUnit][prefix];
		}
	}}
