#include "stdafx.h"

#include <stdio.h>
#include <stdlib.h>

#include "jpeglib.h"
#include "jconfig.h"
#include "jerror.h"
#include "jmorecfg.h"

static Mix::Plugin::INFORMATION g_Info = { Mix::Plugin::TEXTURE_LOADER, L"JPEG", L"1.0.0.0" };

////////////////////////////////////////////////////////////////////////////////////////////////////
// プラグイン情報の取得
////////////////////////////////////////////////////////////////////////////////////////////////////

const Mix::Plugin::INFORMATION& __stdcall GetInformation( void )
{
	return g_Info;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// ロード
////////////////////////////////////////////////////////////////////////////////////////////////////

struct JPEG_BUFFER
{
	JSAMPARRAY rows;
	unsigned int rowPitch;
	unsigned int rowCount;
};

bool JPEG_AllocBuff( JPEG_BUFFER& buff, unsigned int rowPitch, unsigned int rowCount )
{
	unsigned int rowsPtrSize = sizeof( JSAMPROW ) * rowCount;
	unsigned int i;

	buff.rows = reinterpret_cast<JSAMPARRAY>( ::malloc( rowsPtrSize ) );
	if( buff.rows == NULL )
	{
		return false;
	}

	::ZeroMemory( buff.rows, rowsPtrSize );

	for( i = 0; i < rowCount; ++i )
	{
        buff.rows[i] = reinterpret_cast<JSAMPROW>( ::calloc( sizeof( JSAMPLE ), rowPitch ) );
		if( buff.rows[i] == NULL )
		{
			break;
		}
    }

	if( i < rowCount )
	{
		for( i = 0; i < rowCount; ++i )
		{
			if( buff.rows[i] != NULL )
			{
				::free( buff.rows[i] );
				buff.rows[i] = NULL;
			}
		}

		::free( buff.rows );
		buff.rows = NULL;
	}

	buff.rowPitch = rowPitch;
	buff.rowCount = rowCount;

	return true;
}

void JPEG_FreeBuff( JPEG_BUFFER& buff )
{
	for( unsigned int i = 0; i < buff.rowCount; ++i )
	{
		::free( buff.rows[i] );
		buff.rows[i] = NULL;
	}

	::free( buff.rows );
	buff.rows = NULL;

	buff.rowPitch = 0;
	buff.rowCount = 0;
}

int __stdcall Load( Mix::Plugin::IReader* pSrc, Mix::Plugin::Graphics::ITextureKit* pDst )
{
	////////////////////////////////////////////////////////////////////////////////////////////////////
	// シグネチャの確認
	////////////////////////////////////////////////////////////////////////////////////////////////////

	unsigned char header[10];

	if( pSrc->Read( header, sizeof( unsigned char[10] ) ) != sizeof( unsigned char[10] ) )
	{
		return Mix::Plugin::Graphics::TP_NOT_SUPPORTED;
	}

	if( ( header[0] != 0xFF ) || // FLAG
		( header[1] != 0xD8 ) || // MARKER(SOI)
		( header[2] != 0xFF ) || // FLAG
		( header[3] != 0xE0 ) || // MARKER
		( header[4] != 0x00 ) || // SIZE(H)
		( header[5] != 0x10 ) || // SIZE(L)
		( header[6] != 0x4A ) || // 'J'
		( header[7] != 0x46 ) || // 'F'
		( header[8] != 0x49 ) || // 'I'
		( header[9] != 0x46 ) )  // 'F'
	{
		return Mix::Plugin::Graphics::TP_NOT_SUPPORTED;
	}

	pSrc->Seek( Mix::Plugin::IReader::BEGIN, 0 );

	////////////////////////////////////////////////////////////////////////////////////////////////////

	JPEG_BUFFER buff;

	unsigned int srcPitch;
	unsigned int i;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// イメージを展開
	////////////////////////////////////////////////////////////////////////////////////////////////////

	unsigned int readResult;

	unsigned long long ui64SrcSize;
	unsigned int srcSize;
	const void* pTexSrc;

	struct jpeg_decompress_struct jpeg_info;
	struct jpeg_error_mgr jpeg_error;

	int ret;

	// 全て読み取る //

	ui64SrcSize = pSrc->GetSize();
	if( ui64SrcSize <= 4294967295 )
	{
		srcSize = static_cast<unsigned int>( ui64SrcSize );
	}
	else
	{
		return Mix::Plugin::Graphics::TP_FATAL_ERROR;
	}

	pTexSrc = pSrc->Read( srcSize, readResult );
	if( srcSize != readResult )
	{
		return Mix::Plugin::Graphics::TP_FATAL_ERROR;
	}

	// 展開の初期化 //

	jpeg_info.err = jpeg_std_error( &jpeg_error );
	jpeg_create_decompress( &jpeg_info );
	jpeg_mem_src( &jpeg_info, reinterpret_cast<unsigned char*>( const_cast<void*>( pTexSrc ) ), srcSize );
	
	ret = jpeg_read_header( &jpeg_info, true );
	if( ret != JPEG_HEADER_OK )
	{
		jpeg_finish_decompress( &jpeg_info );
		jpeg_destroy_decompress( &jpeg_info );
		return Mix::Plugin::Graphics::TP_ILLEGAL_FORMAT;
	}

	if( jpeg_start_decompress( &jpeg_info ) == false )
	{
		jpeg_finish_decompress( &jpeg_info );
		jpeg_destroy_decompress( &jpeg_info );
		return Mix::Plugin::Graphics::TP_ILLEGAL_FORMAT;
	}

	if( jpeg_info.output_components != 3 )
	{
		//RGBのみ対応
		jpeg_finish_decompress( &jpeg_info );
		jpeg_destroy_decompress( &jpeg_info );
		return Mix::Plugin::Graphics::TP_NOT_SUPPORTED;
	}

	// 展開先のバッファのメモリを確保 //

	srcPitch = jpeg_info.output_width * jpeg_info.output_components;

	if( JPEG_AllocBuff( buff, srcPitch, jpeg_info.output_height ) == false )
	{
		jpeg_finish_decompress( &jpeg_info );
		jpeg_destroy_decompress( &jpeg_info );
		return Mix::Plugin::Graphics::TP_ILLEGAL_FORMAT;
	}

	// 展開を実行 //

	while( jpeg_info.output_scanline < jpeg_info.output_height )
	{
		jpeg_read_scanlines(	&jpeg_info,
								buff.rows + jpeg_info.output_scanline,
								jpeg_info.output_height - jpeg_info.output_scanline );
	}

	// 展開の終了 //

	jpeg_finish_decompress( &jpeg_info );
	jpeg_destroy_decompress( &jpeg_info );

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 実際のイメージを作成する
	////////////////////////////////////////////////////////////////////////////////////////////////////

	void* pMem;

	unsigned int dstPitch;
	unsigned int dstSize;
	unsigned int* pTexDst;

	unsigned int r;
	unsigned int g;
	unsigned int b;

	pDst->SetType( Mix::Plugin::Graphics::ITextureKit::PLANE );
	pDst->SetFormat( Mix::Plugin::Graphics::ITextureKit::FORMAT::R8G8B8A8 );
	pDst->SetSize( jpeg_info.output_width, jpeg_info.output_height );
	pDst->SetDepth( 1 );
	pDst->SetMipLevels( 1 );
	pDst->SetFlags( 0 );

	dstPitch = jpeg_info.output_width * 4;
	dstSize = dstPitch * jpeg_info.output_height;

	pMem = pDst->AddSubResourceData( dstSize, dstPitch );
	if( pMem == NULL )
	{
		JPEG_FreeBuff( buff );
		return Mix::Plugin::Graphics::TP_OUT_OF_MEMORY;
	}

	pTexDst = reinterpret_cast<unsigned int*>( pMem );

	for( i = 0; i < jpeg_info.output_height; ++i )
	{
		JSAMPROW pSmp = buff.rows[i];
		JSAMPROW pSmpEnd = pSmp + srcPitch;

		while( pSmp != pSmpEnd )
		{
			r = *pSmp++;
			g = *pSmp++;
			b = *pSmp++;

			*pTexDst++ = 0xFF000000 | ( r << 16 ) | ( g << 8 ) | b;
		}
    }

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 後処理
	////////////////////////////////////////////////////////////////////////////////////////////////////

	JPEG_FreeBuff( buff );

	return Mix::Plugin::Graphics::TP_OK;
}
