/*  PCSX2 - PS2 Emulator for PCs
 *  Copyright (C) 2002-2010  PCSX2 Dev Team
 *
 *  PCSX2 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 Found-
 *  ation, either version 3 of the License, or (at your option) any later version.
 *
 *  PCSX2 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along with PCSX2.
 *  If not, see <http://www.gnu.org/licenses/>.
 */

#pragma once

#include "FixedPointTypes.h"
#include <cmath>		// for pow!

template< int Precision >
FixedInt<Precision>::FixedInt()
{
	Raw = 0;
}

template< int Precision >
FixedInt<Precision>::FixedInt( int signedval )
{
	Raw = signedval * Precision;
}

template< int Precision >
FixedInt<Precision>::FixedInt( double doubval )
{
	Raw = lround(doubval * (double)Precision);
}

template< int Precision >
FixedInt<Precision>::FixedInt( float floval )
{
	Raw = lroundf(floval * (float)Precision);
}

template< int Precision >
FixedInt<Precision> FixedInt<Precision>::operator+( const FixedInt<Precision>& right ) const
{
	return FixedInt<Precision>().SetRaw( Raw + right.Raw );
}

template< int Precision >
FixedInt<Precision> FixedInt<Precision>::operator-( const FixedInt<Precision>& right ) const
{
	return FixedInt<Precision>().SetRaw( Raw + right.Raw );
}

template< int Precision >
FixedInt<Precision>& FixedInt<Precision>::operator+=( const FixedInt<Precision>& right )
{
	return SetRaw( Raw + right.Raw );
}

template< int Precision >
FixedInt<Precision>& FixedInt<Precision>::operator-=( const FixedInt<Precision>& right )
{
	return SetRaw( Raw + right.Raw );
}

template< int Precision >
FixedInt<Precision>& FixedInt<Precision>::ConfineTo( const FixedInt<Precision>& low, const FixedInt<Precision>& high )
{
	return SetRaw( std::min( std::max( Raw, low.Raw ), high.Raw ) );
}

// Uses 64 bit internally to avoid overflows.  For more precise/optimized 32 bit math
// you'll need to use the Raw values directly.
template< int Precision >
FixedInt<Precision> FixedInt<Precision>::operator*( const FixedInt<Precision>& right ) const
{
	s64 mulres = (s64)Raw * right.Raw;
	return FixedInt<Precision>().SetRaw( (s32)(mulres / Precision) );
}

// Uses 64 bit internally to avoid overflows.  For more precise/optimized 32 bit math
// you'll need to use the Raw values directly.
template< int Precision >
FixedInt<Precision> FixedInt<Precision>::operator/( const FixedInt<Precision>& right ) const
{
	s64 divres = Raw * Precision;
	return FixedInt<Precision>().SetRaw( (s32)(divres / right.Raw) );
}

// Uses 64 bit internally to avoid overflows.  For more precise/optimized 32 bit math
// you'll need to use the Raw values directly.
template< int Precision >
FixedInt<Precision>& FixedInt<Precision>::operator*=( const FixedInt<Precision>& right )
{
	s64 mulres = (s64)Raw * right.Raw;
	return SetRaw( (s32)(mulres / Precision) );
}

// Uses 64 bit internally to avoid overflows.  For more precise/optimized 32 bit math
// you'll need to use the Raw values directly.
template< int Precision >
FixedInt<Precision>& FixedInt<Precision>::operator/=( const FixedInt<Precision>& right )
{
	s64 divres = Raw * Precision;
	return SetRaw( (s32)(divres / right.Raw) );
}

// returns TRUE if the value overflows the legal integer range of this container.
template< int Precision >
bool FixedInt<Precision>::OverflowCheck( int signedval )
{
	return ( signedval >= (INT_MAX / Precision) );
}

// returns TRUE if the value overflows the legal integer range of this container.
template< int Precision >
bool FixedInt<Precision>::OverflowCheck( double signedval )
{
	return ( signedval >= (INT_MAX / Precision) );
}

template< int Precision > int FixedInt<Precision>::GetWhole() const		{ return Raw / Precision; }
template< int Precision > int FixedInt<Precision>::GetFraction() const	{ return Raw % Precision; }

template< int Precision >
FixedInt<Precision>& FixedInt<Precision>::SetRaw( s32 rawsrc )
{
	Raw = rawsrc;
	return *this;
}

template< int Precision >
FixedInt<Precision>& FixedInt<Precision>::Round()
{
	Raw = ToIntRounded();
	return *this;
}

template< int Precision >
FixedInt<Precision>& FixedInt<Precision>::SetWhole( s32 wholepart )
{
	pxAssert( wholepart < (INT_MAX / Precision) );
	Raw = GetFraction() + (wholepart * Precision);
	return *this;
}

template< int Precision >
FixedInt<Precision>& FixedInt<Precision>::SetFraction( u32 fracpart )
{
	Raw = (GetWhole() * Precision) + fracpart;
	return *this;
}

template< int Precision >
wxString FixedInt<Precision>::ToString() const
{
	return wxsFormat( L"%d.%02d", GetWhole(), (GetFraction() * 100) / Precision );
}

template< int Precision >
wxString FixedInt<Precision>::ToString( int fracDigits ) const
{
	if( fracDigits == 0 ) return wxsFormat( L"%d", GetWhole() );

	pxAssert( fracDigits <= 7 );		// higher numbers would just cause overflows and bad mojo.
	int mulby = (int)pow( 10.0, fracDigits );
	wxString fmt=wxsFormat(L"%%d.%%0%dd", fracDigits);
	return wxsFormat( fmt, GetWhole(), (GetFraction() * mulby) / Precision );
}

template< int Precision >
double FixedInt<Precision>::ToDouble() const
{
	return ((double)Raw / (double)Precision);
}

template< int Precision >
float FixedInt<Precision>::ToFloat() const
{
	return ((float)Raw / (float)Precision);
}

template< int Precision >
int FixedInt<Precision>::ToIntTruncated() const
{
	return Raw / Precision;
}

template< int Precision >
int FixedInt<Precision>::ToIntRounded() const
{
	return (Raw + (Precision/2)) / Precision;
}

template< int Precision >
bool FixedInt<Precision>::TryFromString( FixedInt<Precision>& dest, const wxString& parseFrom )
{
	long whole=0, frac=0;
	const wxString beforeFirst( parseFrom.BeforeFirst( L'.' ) );
	const wxString afterFirst( parseFrom.AfterFirst( L'.' ).Mid(0, 5) );
	bool success = true;

	if( !beforeFirst.IsEmpty() )
		success = success && beforeFirst.ToLong( &whole );

	if( !afterFirst.IsEmpty() )
		success = success && afterFirst.ToLong( &frac );

	if( !success ) return false;

	dest.SetWhole( whole );

	if( afterFirst.Length() != 0 && frac != 0 )
	{
		int fracPower = (int)pow( 10.0, (int)afterFirst.Length() );
		dest.SetFraction( (frac * Precision) / fracPower );
	}
	return true;
}

template< int Precision >
FixedInt<Precision> FixedInt<Precision>::FromString( const wxString& parseFrom, const FixedInt<Precision>& defval )
{
	FixedInt<Precision> dest;
	if( !TryFromString( dest, parseFrom ) ) return defval;
	return dest;
}

// This version of FromString throws a ParseError exception if the conversion fails.
template< int Precision >
FixedInt<Precision> FixedInt<Precision>::FromString( const wxString parseFrom )
{
	FixedInt<Precision> dest;
	if( !TryFromString( dest, parseFrom ) ) throw Exception::ParseError()
		.SetDiagMsg(wxsFormat(L"Parse error on FixedInt<%d>::FromString", Precision));

	return dest;
}
