/* 
* PROJECT: NyARToolkitCPP
* --------------------------------------------------------------------------------
*
* The NyARToolkitCPP is C++ version NyARToolkit class library.
* Copyright (C)2008-2009 Ryo Iizuka
*
* 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/>.
* 
* For further information please contact.
*	http://nyatla.jp/nyatoolkit/
*	<airmail(at)ebony.plala.or.jp> or <nyatla(at)nyatla.jp>
* 
*/
#include "NyARRasterThresholdAnalyzer_SlidePTile.h"
#include "NyAR_types.h"
#include "NyARException.h"
#include "nyarcore.h"
namespace NyARToolkitCPP
{

	class ICreateHistgramImpl
	{
	public:
		virtual ~ICreateHistgramImpl(){}
	public:
		virtual int createHistgramImpl(const INyARBufferReader& i_reader,const TNyARIntSize& i_size, int* o_histgram)=0;
	};

	/**
	* Glayscale(MAX256)のヒストグラム計算クラス
	*/
	class CreateHistgramImpl_INT1D_GRAY_8 :public ICreateHistgramImpl
	{
	public:
		int _v_interval;
		CreateHistgramImpl_INT1D_GRAY_8(int i_v_interval)
		{
			this->_v_interval=i_v_interval;
			return;
		}
		int createHistgramImpl(const INyARBufferReader& i_reader,const TNyARIntSize& i_size, int* o_histgram)
		{
			NyAR_ASSERT (i_reader.isEqualBufferType(INyARBufferReader::BUFFERFORMAT_INT1D_GRAY_8));

			int sum=0;
			const int* input=(const int*) i_reader.getBuffer();
			for (int y = i_size.h-1; y >=0 ; y-=this->_v_interval){
				sum+=i_size.w;
				int pt=y*i_size.w;
				for (int x = i_size.w-1; x >=0; x--) {
					o_histgram[input[pt]]++;
					pt++;
				}
			}
			return sum;
		}
	};
	/**
	* RGB24bitのヒストグラム計算クラス
	*
	*/
	class CreateHistgramImpl_BYTE1D_RGB_24 :public ICreateHistgramImpl
	{
	private:
		int _v_interval;
	public:
		CreateHistgramImpl_BYTE1D_RGB_24(int i_v_interval)
		{
			this->_v_interval=i_v_interval;
			return;
		}
		int createHistgramImpl(const INyARBufferReader& i_reader,const TNyARIntSize& i_size, int* o_histgram)
		{
			NyAR_ASSERT (
				i_reader.isEqualBufferType(INyARBufferReader::BUFFERFORMAT_BYTE1D_B8G8R8_24)||
				i_reader.isEqualBufferType(INyARBufferReader::BUFFERFORMAT_BYTE1D_R8G8B8_24));

			const NyAR_BYTE_t* input=(NyAR_BYTE_t*) i_reader.getBuffer();
			const int pix_count=i_size.w;
			const int pix_mod_part=pix_count-(pix_count%8);
			int sum=0;
			for (int y = i_size.h-1; y >=0 ; y-=this->_v_interval) {
				sum+=i_size.w;
				int pt=y*i_size.w*3;
				int x,v;
				for (x = pix_count-1; x >=pix_mod_part; x--) {
					v=((input[pt+0]& 0xff)+(input[pt+1]& 0xff)+(input[pt+2]& 0xff))/3;
					o_histgram[v]++;
					pt+=3;
				}
				//タイリング
				for (;x>=0;x-=8){
					v=((input[pt+ 0]& 0xff)+(input[pt+ 1]& 0xff)+(input[pt+ 2]& 0xff))/3;
					o_histgram[v]++;
					v=((input[pt+ 3]& 0xff)+(input[pt+ 4]& 0xff)+(input[pt+ 5]& 0xff))/3;
					o_histgram[v]++;
					v=((input[pt+ 6]& 0xff)+(input[pt+ 7]& 0xff)+(input[pt+ 8]& 0xff))/3;
					o_histgram[v]++;
					v=((input[pt+ 9]& 0xff)+(input[pt+10]& 0xff)+(input[pt+11]& 0xff))/3;
					o_histgram[v]++;
					v=((input[pt+12]& 0xff)+(input[pt+13]& 0xff)+(input[pt+14]& 0xff))/3;
					o_histgram[v]++;
					v=((input[pt+15]& 0xff)+(input[pt+16]& 0xff)+(input[pt+17]& 0xff))/3;
					o_histgram[v]++;
					v=((input[pt+18]& 0xff)+(input[pt+19]& 0xff)+(input[pt+20]& 0xff))/3;
					o_histgram[v]++;
					v=((input[pt+21]& 0xff)+(input[pt+22]& 0xff)+(input[pt+23]& 0xff))/3;
					o_histgram[v]++;
					pt+=3*8;
				}
			}
			return sum;		
		}
	};



	/**
	* BYTE1D_B8G8R8X8_32のヒストグラム計算クラス
	*
	*/	
	class CreateHistgramImpl_BYTE1D_B8G8R8X8_32 :public ICreateHistgramImpl
	{
	private:
		int _v_interval;
	public:
		CreateHistgramImpl_BYTE1D_B8G8R8X8_32(int i_v_interval)
		{
			this->_v_interval = i_v_interval;
			return;
		}
		int createHistgramImpl(const INyARBufferReader& i_reader,const TNyARIntSize& i_size, int* o_histgram)
		{
			NyAR_ASSERT(i_reader.isEqualBufferType(INyARBufferReader::BUFFERFORMAT_BYTE1D_B8G8R8X8_32));
			const NyAR_BYTE_t* input = (const NyAR_BYTE_t*)i_reader.getBuffer();
			int pix_count = i_size.w;
			int pix_mod_part = pix_count - (pix_count % 8);
			int sum = 0;
			for (int y = i_size.h - 1; y >= 0; y -= this->_v_interval)
			{
				sum += i_size.w;
				int pt = y * i_size.w * 4;
				int x, v;
				for (x = pix_count - 1; x >= pix_mod_part; x--)
				{
					v = ((input[pt + 0] & 0xff) + (input[pt + 1] & 0xff) + (input[pt + 2] & 0xff)) / 3;
					o_histgram[v]++;
					pt += 4;
				}
				//タイリング
				for (; x >= 0; x -= 8)
				{
					v = ((input[pt + 0] & 0xff) + (input[pt + 1] & 0xff) + (input[pt + 2] & 0xff)) / 3;
					o_histgram[v]++;
					v = ((input[pt + 4] & 0xff) + (input[pt + 5] & 0xff) + (input[pt + 6] & 0xff)) / 3;
					o_histgram[v]++;
					v = ((input[pt + 8] & 0xff) + (input[pt + 9] & 0xff) + (input[pt + 10] & 0xff)) / 3;
					o_histgram[v]++;
					v = ((input[pt + 12] & 0xff) + (input[pt + 13] & 0xff) + (input[pt + 14] & 0xff)) / 3;
					o_histgram[v]++;
					v = ((input[pt + 16] & 0xff) + (input[pt + 17] & 0xff) + (input[pt + 18] & 0xff)) / 3;
					o_histgram[v]++;
					v = ((input[pt + 20] & 0xff) + (input[pt + 21] & 0xff) + (input[pt + 22] & 0xff)) / 3;
					o_histgram[v]++;
					v = ((input[pt + 24] & 0xff) + (input[pt + 25] & 0xff) + (input[pt + 26] & 0xff)) / 3;
					o_histgram[v]++;
					v = ((input[pt + 28] & 0xff) + (input[pt + 29] & 0xff) + (input[pt + 30] & 0xff)) / 3;
					o_histgram[v]++;
					pt += 4 * 8;
				}
			}
			return sum;
		}
	};


	/**
	* BYTE1D_X8R8G8B8_32のヒストグラム計算クラス
	*
	*/
	class CreateHistgramImpl_BYTE1D_X8R8G8B8_32 :public ICreateHistgramImpl
	{
	private:
		int _v_interval;
	public:
		CreateHistgramImpl_BYTE1D_X8R8G8B8_32(int i_v_interval)
		{
			this->_v_interval = i_v_interval;
			return;
		}
		int createHistgramImpl(const INyARBufferReader& i_reader,const TNyARIntSize& i_size, int* o_histgram)
		{
			NyAR_ASSERT(i_reader.isEqualBufferType(INyARBufferReader::BUFFERFORMAT_BYTE1D_X8R8G8B8_32));
			const NyAR_BYTE_t* input = (const NyAR_BYTE_t*)i_reader.getBuffer();
			int pix_count = i_size.w;
			int pix_mod_part = pix_count - (pix_count % 8);
			int sum = 0;
			for (int y = i_size.h - 1; y >= 0; y -= this->_v_interval)
			{
				sum += i_size.w;
				int pt = y * i_size.w * 4;
				int x, v;
				for (x = pix_count - 1; x >= pix_mod_part; x--)
				{
					v = ((input[pt + 1] & 0xff) + (input[pt + 2] & 0xff) + (input[pt + 3] & 0xff)) / 3;
					o_histgram[v]++;
					pt += 4;
				}
				//タイリング
				for (; x >= 0; x -= 8)
				{
					v = ((input[pt + 1] & 0xff) + (input[pt + 2] & 0xff) + (input[pt + 3] & 0xff)) / 3;
					o_histgram[v]++;
					v = ((input[pt + 5] & 0xff) + (input[pt + 6] & 0xff) + (input[pt + 7] & 0xff)) / 3;
					o_histgram[v]++;
					v = ((input[pt + 9] & 0xff) + (input[pt + 10] & 0xff) + (input[pt + 11] & 0xff)) / 3;
					o_histgram[v]++;
					v = ((input[pt + 13] & 0xff) + (input[pt + 14] & 0xff) + (input[pt + 15] & 0xff)) / 3;
					o_histgram[v]++;
					v = ((input[pt + 17] & 0xff) + (input[pt + 18] & 0xff) + (input[pt + 19] & 0xff)) / 3;
					o_histgram[v]++;
					v = ((input[pt + 21] & 0xff) + (input[pt + 22] & 0xff) + (input[pt + 23] & 0xff)) / 3;
					o_histgram[v]++;
					v = ((input[pt + 25] & 0xff) + (input[pt + 26] & 0xff) + (input[pt + 27] & 0xff)) / 3;
					o_histgram[v]++;
					v = ((input[pt + 29] & 0xff) + (input[pt + 30] & 0xff) + (input[pt + 31] & 0xff)) / 3;
					o_histgram[v]++;
					pt += 4 * 8;
				}
			}
			return sum;
		}
	};

	/**
	 * WORD1D_R5G6B5_16LEのヒストグラム計算クラス
	 *
	 */	
	class CreateHistgramImpl_WORD1D_R5G6B5_16LE :public ICreateHistgramImpl
    {
	private:
		int _v_interval;
	public:
		CreateHistgramImpl_WORD1D_R5G6B5_16LE(int i_v_interval)
        {
            this->_v_interval = i_v_interval;
            return;
        }
        int createHistgramImpl(const INyARBufferReader& i_reader,const TNyARIntSize& i_size, int* o_histgram)
        {
			NyAR_ASSERT(i_reader.isEqualBufferType(INyARBufferReader::BUFFERFORMAT_WORD1D_R5G6B5_16LE));
            unsigned short* input = (unsigned short*)i_reader.getBuffer();
            int pix_count = i_size.w;
            int pix_mod_part = pix_count - (pix_count % 8);
            int sum = 0;
            for (int y = i_size.h - 1; y >= 0; y -= this->_v_interval)
            {
                sum += i_size.w;
                int pt = y * i_size.w;
                int x;
				unsigned short v;
                for (x = pix_count - 1; x >= pix_mod_part; x--)
                {
                    v =input[pt];
                    v = (((v & 0xf800) >> 8) + ((v & 0x07e0) >> 3) + ((v & 0x001f) << 3))/3;
                    o_histgram[v]++;
                    pt++;
                }
                //タイリング
                for (; x >= 0; x -= 8)
                {
                    v =input[pt];pt++;
                    v = (((v & 0xf800) >> 8) + ((v & 0x07e0) >> 3) + ((v & 0x001f) << 3))/3;
                    o_histgram[v]++;
                    v =input[pt];pt++;
                    v = (((v & 0xf800) >> 8) + ((v & 0x07e0) >> 3) + ((v & 0x001f) << 3))/3;
                    o_histgram[v]++;
                    v =input[pt];pt++;
                    v = (((v & 0xf800) >> 8) + ((v & 0x07e0) >> 3) + ((v & 0x001f) << 3))/3;
                    o_histgram[v]++;
                    v =input[pt];pt++;
                    v = (((v & 0xf800) >> 8) + ((v & 0x07e0) >> 3) + ((v & 0x001f) << 3))/3;
                    o_histgram[v]++;
                    v =input[pt];pt++;
                    v = (((v & 0xf800) >> 8) + ((v & 0x07e0) >> 3) + ((v & 0x001f) << 3))/3;
                    o_histgram[v]++;
                    v =input[pt];pt++;
                    v = (((v & 0xf800) >> 8) + ((v & 0x07e0) >> 3) + ((v & 0x001f) << 3))/3;
                    o_histgram[v]++;
                    v =input[pt];pt++;
                    v = (((v & 0xf800) >> 8) + ((v & 0x07e0) >> 3) + ((v & 0x001f) << 3))/3;
                    o_histgram[v]++;
                    v =input[pt];pt++;
                    v = (((v & 0xf800) >> 8) + ((v & 0x07e0) >> 3) + ((v & 0x001f) << 3))/3;
                    o_histgram[v]++;
                }
            }
            return sum;
        }
    };




	/**
	* INT1D_X8R8G8B8_32のヒストグラム計算クラス
	*
	*/
	/*
	class CreateHistgramImpl_INT1D_X8R8G8B8_32 :public ICreateHistgramImpl
	{
	private:
	int _v_interval;
	public:
	CreateHistgramImpl_INT1D_X8R8G8B8_32(int i_v_interval)
	{
	this->_v_interval = i_v_interval;
	return;
	}
	int createHistgramImpl(const INyARBufferReader& i_reader,const TNyARIntSize& i_size, int* o_histgram)
	{
	NyAR_ASSERT(i_reader.isEqualBufferType(INyARBufferReader::BUFFERFORMAT_INT1D_X8R8G8B8_32));
	const int* input = (const int*)i_reader.getBuffer();
	int pix_count = i_size.w;
	int pix_mod_part = pix_count - (pix_count % 8);
	int sum = 0;
	for (int y = i_size.h - 1; y >= 0; y -= this->_v_interval)
	{
	sum += i_size.w;
	int pt = y * i_size.w;
	int x, v;
	int w;
	for (x = pix_count - 1; x >= pix_mod_part; x--)
	{
	w=input[pt];
	v = (((w>>16)&0xff)+((w>>8)&0xff)+(w&0xff));
	o_histgram[v]++;
	pt++;
	}
	//タイリング
	for (; x >= 0; x -= 8)
	{
	w=input[pt+0];
	v = (((w>>16)&0xff)+((w>>8)&0xff)+(w&0xff));
	o_histgram[v]++;
	w=input[pt+1];
	v = (((w>>16)&0xff)+((w>>8)&0xff)+(w&0xff));
	o_histgram[v]++;
	w=input[pt+2];
	v = (((w>>16)&0xff)+((w>>8)&0xff)+(w&0xff));
	o_histgram[v]++;
	w=input[pt+3];
	v = (((w>>16)&0xff)+((w>>8)&0xff)+(w&0xff));
	o_histgram[v]++;
	w=input[pt+4];
	v = (((w>>16)&0xff)+((w>>8)&0xff)+(w&0xff));
	o_histgram[v]++;
	w=input[pt+5];
	v = (((w>>16)&0xff)+((w>>8)&0xff)+(w&0xff));
	o_histgram[v]++;
	w=input[pt+6];
	v = (((w>>16)&0xff)+((w>>8)&0xff)+(w&0xff));
	o_histgram[v]++;
	w=input[pt+7];
	v = (((w>>16)&0xff)+((w>>8)&0xff)+(w&0xff));
	o_histgram[v]++;
	pt += 8;
	}
	}
	return sum;
	}
	};
	*/



	/**
	* 明点と暗点をPタイル法で検出して、その中央値を閾値とする。
	* 
	* 
	*/
	NyARRasterThresholdAnalyzer_SlidePTile::NyARRasterThresholdAnalyzer_SlidePTile(int i_persentage,int i_raster_format,int i_vertical_interval)
	{
		NyAR_ASSERT (0 <= i_persentage && i_persentage <= 50);
		this->_persentage = i_persentage;
		switch (i_raster_format)
		{
		case INyARBufferReader::BUFFERFORMAT_BYTE1D_B8G8R8_24:
		case INyARBufferReader::BUFFERFORMAT_BYTE1D_R8G8B8_24:
			this->_histgram = new CreateHistgramImpl_BYTE1D_RGB_24(i_vertical_interval);
			break;
		case INyARBufferReader::BUFFERFORMAT_INT1D_GRAY_8:
			this->_histgram = new CreateHistgramImpl_INT1D_GRAY_8(i_vertical_interval);
			break;
		case INyARBufferReader::BUFFERFORMAT_BYTE1D_B8G8R8X8_32:
			this->_histgram = new CreateHistgramImpl_BYTE1D_B8G8R8X8_32(i_vertical_interval);
			break;
		case INyARBufferReader::BUFFERFORMAT_BYTE1D_X8R8G8B8_32:
			this->_histgram = new CreateHistgramImpl_BYTE1D_X8R8G8B8_32(i_vertical_interval);
			break;
		case INyARBufferReader::BUFFERFORMAT_WORD1D_R5G6B5_16LE:
			this->_histgram=new CreateHistgramImpl_WORD1D_R5G6B5_16LE(i_vertical_interval);
			break;
		default:
			throw NyARException();
		}
	}
	NyARRasterThresholdAnalyzer_SlidePTile::~NyARRasterThresholdAnalyzer_SlidePTile()
	{
		NyAR_SAFE_DELETE(this->_histgram);
		return;
	}

	void NyARRasterThresholdAnalyzer_SlidePTile::setVerticalInterval(int i_step)
	{
		NyARException::notImplement();
		return;//未実装一号
	}
	void NyARRasterThresholdAnalyzer_SlidePTile::analyzeRaster(const INyARRaster& i_input)
	{
		int histgram_buf[256];
		const TNyARIntSize& size=i_input.getSize();

		//最大画像サイズの制限
		NyAR_ASSERT(size.w*size.h<0x40000000);

		//ヒストグラム初期化
		for (int i = 0; i < 256; i++) {
			histgram_buf[i] = 0;
		}
		int sum_of_pixel=this->_histgram->createHistgramImpl(i_input.getBufferReader(), size, histgram_buf);

		// 閾値ピクセル数確定
		const int th_pixcels = sum_of_pixel * this->_persentage / 100;
		int th_wk;
		int th_w, th_b;

		// 黒点基準
		th_wk = th_pixcels;
		for (th_b = 0; th_b < 254; th_b++) {
			th_wk -= histgram_buf[th_b];
			if (th_wk <= 0) {
				break;
			}
		}
		// 白点基準
		th_wk = th_pixcels;
		for (th_w = 255; th_w > 1; th_w--) {
			th_wk -= histgram_buf[th_w];
			if (th_wk <= 0) {
				break;
			}
		}
		// 閾値の保存
		this->_threshold = (th_w + th_b) / 2;
		return;
	}
	int NyARRasterThresholdAnalyzer_SlidePTile::getThreshold()const
	{
		return this->_threshold;
	}

	int NyARRasterThresholdAnalyzer_SlidePTile::getThreshold(int i_x, int i_y)const
	{
		return this->_threshold;
	}


}

