/*
    Scan Tailor - Interactive post-processing tool for scanned pages.
    Copyright (C) 2007-2015  Joseph Artsimovich <joseph.artsimovich@gmail.com>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef IMAGEPROC_BITOPS_H_
#define IMAGEPROC_BITOPS_H_

#include "imageproc_config.h"

namespace imageproc
{

namespace detail
{

IMAGEPROC_EXPORT extern unsigned char const bitCounts[256];
IMAGEPROC_EXPORT extern unsigned char const reversedBits[256];

template<typename T, int BytesRemaining>
class NonZeroBits
{
public:
	static unsigned char count(T val) {
		return bitCounts[static_cast<unsigned char>(val)]
			+ NonZeroBits<T, BytesRemaining - 1>::count(val >> 8);
	}
};

template<typename T>
class NonZeroBits<T, 1>
{
public:
	static unsigned char count(T val) {
		return bitCounts[static_cast<unsigned char>(val)];
	}
};


template<typename T, int TotalBytes, int Offset, bool Done = false>
struct ReverseBytes
{
	static T result(T const val) {
		int const left_shift = (TotalBytes - Offset - 1) * 8;
		int const right_shift = Offset * 8;
		
		typedef unsigned char Byte;
		Byte const left_byte = static_cast<Byte>(val >> left_shift);
		Byte const right_byte = static_cast<Byte>(val >> right_shift);
		
		T res(
			ReverseBytes<
				T, TotalBytes, Offset + 1,
				Offset == TotalBytes / 2
			>::result(val)
		);
		res |= T(reversedBits[left_byte]) << right_shift;
		res |= T(reversedBits[right_byte]) << left_shift;
		
		return res;
	}
};

template<typename T, int TotalBytes, int Offset>
struct ReverseBytes<T, TotalBytes, Offset, true>
{
	static T result(T) {
		return T();
	}
};

template<typename T>
struct ReverseBytes<T, 1, 0, false>
{
	static T result(T const val) {
		typedef unsigned char Byte;
		return T(reversedBits[static_cast<Byte>(val)]);
	}
};


template<typename T, int STRIPE_LEN, int BITS_DONE = 0, bool HAVE_MORE_BITS = true>
struct StripedMaskMSB1
{
	static T const value =
		(((T(1) << STRIPE_LEN) - 1) << (BITS_DONE + STRIPE_LEN)) |
		StripedMaskMSB1<
			T, STRIPE_LEN, BITS_DONE + STRIPE_LEN * 2,
			BITS_DONE + STRIPE_LEN * 2 < sizeof(T) * 8
		>::value;
};

template<typename T, int STRIPE_LEN, int BITS_DONE>
struct StripedMaskMSB1<T, STRIPE_LEN, BITS_DONE, false>
{
	static T const value = 0;
};


template<typename T, int STRIPE_LEN, int BITS_DONE = 0, bool HAVE_MORE_BITS = true>
struct StripedMaskLSB1
{
	static T const value =
		(((T(1) << STRIPE_LEN) - 1) << BITS_DONE) |
		StripedMaskLSB1<
			T, STRIPE_LEN, BITS_DONE + STRIPE_LEN * 2,
			BITS_DONE + STRIPE_LEN * 2 < sizeof(T) * 8
		>::value;
};

template<typename T, int STRIPE_LEN, int BITS_DONE>
struct StripedMaskLSB1<T, STRIPE_LEN, BITS_DONE, false>
{
	static T const value = 0;
};


template<typename T, int STRIPE_LEN>
struct MostSignificantZeroes
{
	static int reduce(T val, int count) {
		if (T tmp = val & StripedMaskMSB1<T, STRIPE_LEN>::value) {
			val = tmp;
			count -= STRIPE_LEN;
		}
		return MostSignificantZeroes<T, STRIPE_LEN / 2>::reduce(val, count);
	}
};

template<typename T>
struct MostSignificantZeroes<T, 0>
{
	static int reduce(T val, int count) {
		return count - 1;
	}
};


template<typename T, int STRIPE_LEN>
struct LeastSignificantZeroes
{
	static int reduce(T val, int count) {
		if (T tmp = val & StripedMaskLSB1<T, STRIPE_LEN>::value) {
			val = tmp;
			count -= STRIPE_LEN;
		}
		return LeastSignificantZeroes<T, STRIPE_LEN / 2>::reduce(val, count);
	}
};

template<typename T>
struct LeastSignificantZeroes<T, 0>
{
	static int reduce(T val, int count) {
		return count - 1;
	}
};

} // namespace detail


template<typename T>
int countNonZeroBits(T const val)
{
	return detail::NonZeroBits<T, sizeof(T)>::count(val);
}

template<typename T>
T reverseBits(T const val)
{
	return detail::ReverseBytes<T, sizeof(T), 0>::result(val);
}

template<typename T>
int countMostSignificantZeroes(T const val)
{
	static int const total_bits = sizeof(T) * 8;
	int zeroes = total_bits;
	
	if (val) {
		zeroes = detail::MostSignificantZeroes<T, total_bits / 2>::reduce(
			val, zeroes
		);
	}
	
	return zeroes;
}

template<typename T>
int countLeastSignificantZeroes(T const val)
{
	static int const total_bits = sizeof(T) * 8;
	int zeroes = total_bits;
	
	if (val) {
		zeroes = detail::LeastSignificantZeroes<T, total_bits / 2>::reduce(
			val, zeroes
		);
	}
	
	return zeroes;
}

} // namespace imageproc

#endif
