#include "Fixed.hpp"

/*
 * functions to print numbers in binary
 * for the float, found help from stackoverflow :
 * https://stackoverflow.com/questions/474007/floating-point-to-binary-valuec
 */

std::string printBitsInt(int num)
{
	int i = 0;

	for (unsigned int mask = 1U << (sizeof(int) *8 -1); mask; mask >>= 1)
	{
		std::cout << ((num & mask) != 0);
		i++;
		if (i == 1 || i == 9 || i == 24)
			std::cout << ' ';
	}
	return "";
}

std::string	printBitsFloat(float num)
{
	int	*p = (int *)&num;
	int i = 0;

	for (unsigned int mask = 1U << (sizeof(float) *8 -1); mask; mask >>= 1)
	{
		std::cout << ((*p & mask) != 0);
		i++;
		if (i == 1 || i == 9 || i == 24)
			std::cout << ' ';
	}
	return "";
}


/*
 * statics variables initialisation
 *
 * for MAX integer :
 * 00000000 01111111 11111111 11111111 ( 8388607) (-1U >> (this->_frac +1))
 *              <= ... >=
 * 11111111 10000000 00000000 00000000 (-8388608)
 *
 */

int const Fixed::_frac = 8;
int const Fixed::_max = -1U >> (_frac +1);
int const Fixed::_min = ~_max;


/*
 * default constructor / copy constructor / destructor
 */

Fixed::Fixed() : _value(0) {
	return;
}

Fixed::Fixed(Fixed const & src) {
	*this = src;
	return;
}

Fixed::~Fixed( void ) {
	return;
}


/*
 * int and float constructors
 */

Fixed::Fixed(int integer) {
	if (integer < Fixed::_min || integer > Fixed::_max)
		std::cout << "error: integer out of range" << '\n';
	else
		this->_value = integer << Fixed::_frac;
}

Fixed::Fixed(float const floater) {
	if (floater < Fixed::_min || floater > Fixed::_max)
		std::cout << "error: float out of range" << '\n';
	else
		this->_value = roundf(floater * (1 << Fixed::_frac));
}


/*
 * assignement operator
 */

Fixed &	Fixed::operator=( Fixed const & rhs ) {
	if ( this != &rhs )
		this->_value = rhs.getRawBits();
	return *this;
}


/*
 * operators < ; > ; <= ; == ; != ; + ; - ; * ; / ; ++ ; --
 * ref : https://en.cppreference.com/w/cpp/language/operators
 * for division, if you want to avoid floats (legitimate) :
 * https://stackoverflow.com/questions/8506317/fixed-point-unsigned-division-in-c
 */

bool	Fixed::operator< (Fixed const & rhs) const {
	return this->_value < rhs._value;
}
bool	Fixed::operator> (Fixed const & rhs) const {
	return rhs < *this;
}
bool	Fixed::operator<=(Fixed const & rhs) const {
	return !(*this > rhs);
}
bool	Fixed::operator>=(Fixed const & rhs) const {
	return !(*this < rhs);
}
bool	Fixed::operator==(Fixed const & rhs) const {
	return this->_value == rhs._value;
}
bool	Fixed::operator!=(Fixed const & rhs) const {
	return !(*this == rhs);
}
Fixed	Fixed::operator+ ( Fixed const & rhs ) const {
	Fixed	result(*this);
	result._value += rhs._value;
	return (result);
}
Fixed	Fixed::operator- ( Fixed const & rhs ) const {
	Fixed	result(*this);
	result._value -= rhs._value;
	return (result);
}
Fixed	Fixed::operator* ( Fixed const & rhs ) const {
	Fixed	result(*this);
	result._value = ((long)result._value * (long)rhs._value) >> Fixed::_frac;
	return result;
}
Fixed	Fixed::operator/ ( Fixed const & rhs ) const {
	Fixed	result(*this);
	if (rhs._value == 0)
		std::cout << "!impossible division by 0¡";
	else
		result._value = (long)(result._value << Fixed::_frac) / rhs._value;
	return result;
}
Fixed &	Fixed::operator++() {
	this->_value++;
	return *this;
}
Fixed &	Fixed::operator--() {
	this->_value--;
	return *this;
}
Fixed	Fixed::operator++( int ) {
	Fixed old = *this;
	Fixed::operator++();
	return old;
}
Fixed	Fixed::operator--( int ) {
	Fixed old = *this;
	Fixed::operator--();
	return old;
}


/*
 * returns min and max
 */

Fixed const &	Fixed::min(Fixed const & lhs, Fixed const & rhs) {
	if (lhs < rhs)
		return lhs;
	return rhs;
}
Fixed const &	Fixed::max(Fixed const & lhs, Fixed const & rhs) {
	if (lhs > rhs)
		return lhs;
	return rhs;
}
Fixed &	Fixed::min(Fixed & lhs, Fixed & rhs) {
	if (lhs < rhs)
		return lhs;
	return rhs;
}
Fixed &	Fixed::max(Fixed & lhs, Fixed & rhs) {
	if (lhs > rhs)
		return lhs;
	return rhs;
}


/*
 * functions that returns _value
 */

int		Fixed::getRawBits( void ) const {
	return this->_value;
}

void	Fixed::setRawBits( int const raw ) {
	this->_value = raw;
}

int		Fixed::toInt( void ) const {
	return (this->_value >> Fixed::_frac);
}
float	Fixed::toFloat( void ) const {
	return ((float)this->_value / (float)(1 << Fixed::_frac));
}


/*
 * overload "<<" -> output fixed point in float representation
 * found here : https://github.com/pgomez-a/42_CPP_Piscine/blob/master/cpp02/ex01/Fixed.cpp
 */

std::ostream &	operator<<(std::ostream & o, Fixed const & rhs)
{
	o << rhs.toFloat();
	return (o);
}
