#include "stdafx.h"

#include "HdrImageLoader.h"

#include <math.h>

#include <string>
#include <fstream>
#include <sstream>


namespace lib_graph
{

namespace hdr
{


bool HdrImageLoader::Load( lib_graph::RawImage4b& img , const char* filename )
{
	std::ifstream ifs(filename, std::ios::binary);
	if( !ifs.is_open() )
		return false;

	return Load( img , ifs );
}

// アルファにeを入れた生データ形式
bool HdrImageLoader::Load( lib_graph::RawImage4b& img , std::istream& ifs )
{
	std::string s;

	if (!LoadToHeader(ifs, s))
		return false;

	HdrFileHeader header;
	if (!LoadHeader(ifs, header, s))
		return false;

	int width = header.Width;
	int height = header.Height;
	bool x_lines = header.LinesX;

	if (img.size_x() != width || img.size_y() != height)
		img.resize(width, height);

	double last_e = 0.0;
	double last_e2 = 1.0;
	unsigned char last_ei = 128;

	int num_arrays = ( x_lines ? width : height );
	int array_len  = ( x_lines ? height : width );
	std::vector<unsigned char> buf_r( array_len );
	std::vector<unsigned char> buf_g( array_len );
	std::vector<unsigned char> buf_b( array_len );
	std::vector<unsigned char> buf_e( array_len );
	for (int i = 0; i < num_arrays; ++i)
	{
		unsigned char line_header[4];
		ifs.read((char*)line_header, 4);

		if (line_header[0] != 0x02)
			return false;
		if (line_header[1] != 0x02)
			return false;

		for (int read_seq = 0; read_seq < 4; ++read_seq)
		{
			std::vector<unsigned char>* line_buf = NULL;
			if( read_seq == 0 ) line_buf = &buf_r;
			if( read_seq == 1 ) line_buf = &buf_g;
			if( read_seq == 2 ) line_buf = &buf_b;
			if( read_seq == 3 ) line_buf = &buf_e;

			int readed_count = 0;

			for (;;)
			{
				unsigned char db;
				ifs.read((char*)&db, 1);
				bool is_repeat = (int)db > 128;
				if (is_repeat)
				{
					int num_repeat = (int)db - 128;
					unsigned char repeat_val;
					ifs.read( (char*)&repeat_val , 1 );
					for (int j = 0; j < num_repeat; ++j)
					{
						(*line_buf)[readed_count++] = repeat_val;
					}
				}
				else
				{
					int num_repeat = (int)db;
					ifs.read((char*)&(*line_buf)[readed_count], num_repeat);

					readed_count += num_repeat;
				}

				if (readed_count > array_len)
					return false;

				if (readed_count == array_len)
					break;
			}
		}

		for (int j = 0; j < array_len; ++j)
		{
			unsigned char ei = buf_e[j];
			unsigned char r = buf_r[j];
			unsigned char g = buf_g[j];
			unsigned char b = buf_b[j];

			if (x_lines)
				img(i, j).set(r, g, b, ei);
			else
				img(j, i).set(r, g, b, ei);
		}
	}

	return true;
}

bool HdrImageLoader::Load( lib_graph::RawImage3f& img , const char* filename )
{
	std::ifstream ifs(filename, std::ios::binary);
	if( !ifs.is_open() )
		return false;

	return Load( img , ifs );
}

// 実数補正済みRGB形式
bool HdrImageLoader::Load( lib_graph::RawImage3f& img , std::istream& ifs )
{
	std::string s;

	if (!LoadToHeader(ifs, s))
		return false;

	HdrFileHeader header;
	if (!LoadHeader(ifs, header, s))
		return false;

	int width = header.Width;
	int height = header.Height;
	bool x_lines = header.LinesX;

	if (img.size_x() != width || img.size_y() != height)
		img.resize(width, height);

	double last_e = 0.0;
	double last_e2 = 1.0;
	unsigned char last_ei = 128;

	int num_arrays = ( x_lines ? width : height );
	int array_len  = ( x_lines ? height : width );
	std::vector<unsigned char> buf_r( array_len );
	std::vector<unsigned char> buf_g( array_len );
	std::vector<unsigned char> buf_b( array_len );
	std::vector<unsigned char> buf_e( array_len );
	for (int i = 0; i < num_arrays; ++i)
	{
		unsigned char line_header[4];
		ifs.read((char*)line_header, 4);

		if (line_header[0] != 0x02)
			return false;
		if (line_header[1] != 0x02)
			return false;

		for (int read_seq = 0; read_seq < 4; ++read_seq)
		{
			std::vector<unsigned char>* line_buf = NULL;
			if( read_seq == 0 ) line_buf = &buf_r;
			if( read_seq == 1 ) line_buf = &buf_g;
			if( read_seq == 2 ) line_buf = &buf_b;
			if( read_seq == 3 ) line_buf = &buf_e;

			int readed_count = 0;

			for (;;)
			{
				unsigned char db;
				ifs.read((char*)&db, 1);
				bool is_repeat = (int)db > 128;
				if (is_repeat)
				{
					int num_repeat = (int)db - 128;
					unsigned char repeat_val;
					ifs.read( (char*)&repeat_val , 1 );
					for (int j = 0; j < num_repeat; ++j)
					{
						(*line_buf)[readed_count++] = repeat_val;
					}
				}
				else
				{
					int num_repeat = (int)db;
					ifs.read((char*)&(*line_buf)[readed_count], num_repeat);

					readed_count += num_repeat;
				}

				if (readed_count > array_len)
					return false;

				if (readed_count == array_len)
					break;
			}
		}

		for (int j = 0; j < array_len; ++j)
		{
			unsigned char ei = buf_e[j];
			float e2;
			if (last_ei == ei)
			{
				e2 = last_e2;
			}
			else
			{
				float e = (float)((int)ei - 128);
				e2 = pow(2.0, e);
				last_e2 = e2;
				last_ei = ei;
			}

			float r = (float)buf_r[j] * e2 / 255.0f;
			float g = (float)buf_g[j] * e2 / 255.0f;
			float b = (float)buf_b[j] * e2 / 255.0f;

			if (x_lines)
				img(i, j).set(r, g, b);
			else
				img(j, i).set(r, g, b);
		}
	}

	return true;
}

// コメント行をスキップしてヘッダ行まで進む
bool HdrImageLoader::LoadToHeader( std::istream& ifs , std::string& s )
{
	for(;;)
	{
		s = "";
		std::getline( ifs , s );

		if( ifs.eof() )
			return false;

		if( s.empty() )
			continue;
		if( s[0] == '#' )
			continue;

		break;
	}

	return true;
}

// ヘッダを読み込む
bool HdrImageLoader::LoadHeader( std::istream& ifs , HdrFileHeader& header , std::string& s )
{
	std::string format_str = s;

	float exposure = 1.0f;
	{
		std::getline( ifs , s );
		std::istringstream iss( s );
		std::string dummy_str;
		iss >> dummy_str >> exposure;
	}

	// find textbuffer

	for(;;)
	{
		std::getline( ifs , s );
		if( ifs.eof() )
			return false;

		if( s.empty() )
			continue;

		break;
	}

	header.LinesX = true;  // x方向へのアレイ列の場合はtrue
	header.AngleFlagX = 1;
	header.AngleFlagY = -1;
	{
		std::istringstream iss( s );
		std::string ax0 , ax1;
		int len0 , len1;
		iss >> ax0 >> len0 >> ax1 >> len1;

		if( ax0[1] != 'X' )
		{
			header.LinesX = false;
			std::swap( ax0 , ax1 );
			std::swap( len0 , len1 );
		}

		header.AngleFlagX = ( ax0[0] == '-' ? -1 : 1 );
		header.AngleFlagY = ( ax1[0] == '-' ? -1 : 1 );
		header.Width = len0;
		header.Height = len1;
	}

	return true;
}


}

}
