/*$
mlk in apdtool
Copyright (c) 2020 Azel

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
$*/

/*****************************************
 * mLoadImage
 *****************************************/

#include <string.h>

#include "mlk.h"
#include "mlk_loadimage.h"
#include "mlk_io.h"
#include "mlk_util.h"
#include "mlk_imageconv.h"


//==============================
// 内部で使うもの
//==============================


/**@ mIO を読み込み用に開く */

mIO *mLoadImage_openIO(mLoadImageOpen *p)
{
	mIO *io = NULL;

	switch(p->type)
	{
		case MLOADIMAGE_OPEN_FILENAME:
			io = mIO_openReadFile(p->filename);
			break;
		case MLOADIMAGE_OPEN_FP:
			io = mIO_openReadFILEptr(p->fp);
			break;
		case MLOADIMAGE_OPEN_BUF:
			io = mIO_openReadBuf(p->buf, p->size);
			break;
	}

	return io;
}

/**@ 各読み込み用のバッファ作成と mIO を開く
 * 
 * @d:各読み込み用に使うデータバッファを確保して、ファイルを開く。\
 * データの構造体の先頭は mIO * にすること。\
 * pli->handle に確保したポインタがセットされる。
 *
 * @p:size 構造体のデータサイズ
 * @p:endian ファイルのエンディアン。MIO_ENDIAN_*。\
 * 負の値で、mIO を使わない。
 * @r:エラーコード */

mlkerr mLoadImage_createHandle(mLoadImage *pli,int size,int endian)
{
	void *p;
	mIO **pio;

	//確保

	p = mMalloc0(size);
	if(!p)
	{
		pli->handle = NULL;
		return MLKERR_ALLOC;
	}

	pli->handle = p;

	//開く

	if(endian >= 0)
	{
		pio = (mIO **)p;

		*pio = mLoadImage_openIO(&pli->open);
		if(!(*pio)) return MLKERR_OPEN;

		mIO_setEndian(*pio, endian);
	}

	return MLKERR_OK;
}

/**@ 各読み込みの終了処理
 *
 * @d:handle と errmessage を解放する。\
 * また、パレットをコピーさせていない場合は palette_buf を NULL にする。\
 * \
 * handle を独自で確保している場合は、
 * あらかじめ解放の上、handle = NULL にしておくこと。 */

void mLoadImage_closeHandle(mLoadImage *p)
{
	mFree(p->errmessage);
	mFree(p->handle);

	p->handle = NULL;
	p->errmessage = NULL;

	if(!(p->flags & MLOADIMAGE_FLAGS_COPY_PALETTE))
		p->palette_buf = NULL;
}

/**@ パレットデータをセット
 *
 * @d:フラグでパレットをコピーするように指定されている場合は、
 * 確保してコピー。\
 * それ以外はそのままポインタをセット。
 * 
 * @p:buf NULL なら何もしない
 * @p:size バッファのサイズ
 * @p:num  セットするパレット個数
 * @r:エラーコード */

mlkerr mLoadImage_setPalette(mLoadImage *p,uint8_t *buf,int size,int palnum)
{
	if(buf)
	{
		p->palette_num = palnum;

		if(p->flags & MLOADIMAGE_FLAGS_COPY_PALETTE)
		{
			//コピー

			p->palette_buf = (uint8_t *)mMemdup(buf, size);
			if(!p->palette_buf) return MLKERR_ALLOC;
		}
		else
			p->palette_buf = buf;
	}

	return MLKERR_OK;
}

/**@ イメージ変換用の構造体に共通データをセット */

void mLoadImage_setImageConv(mLoadImage *p,mImageConv *dst)
{
	mMemset0(dst, sizeof(mImageConv));

	dst->palbuf = p->palette_buf;
	dst->srcbits = 8;
	dst->dstbits = p->bits_per_sample;
	dst->width = p->width;

	//変換タイプ

	if(p->convert_type == MLOADIMAGE_CONVERT_TYPE_RGB)
		dst->convtype = MIMAGECONV_CONVTYPE_RGB;
	else if(p->convert_type == MLOADIMAGE_CONVERT_TYPE_RGBA)
		dst->convtype = MIMAGECONV_CONVTYPE_RGBA;
	else
		dst->convtype = MIMAGECONV_CONVTYPE_NONE;
}


//==============================


/**@ mLoadImage 構造体を初期化
 *
 * @d:ゼロクリアする。 */

void mLoadImage_init(mLoadImage *p)
{
	memset(p, 0, sizeof(mLoadImage));
}

/**@ ファイルヘッダからフォーマットを判定し、必要な情報を取得
 *
 * @d:TGA の場合、ヘッダに識別子がないため、正確な判定が行えない。\
 * TGA に対応する場合は、判定を最後にすること。
 * 
 * @p:dst いずれかに一致した場合、そのフォーマットの情報が格納される
 * @p:open ヘッダを読み込むための開く情報
 * @p:checks 読み込みに対応する各フォーマットのチェック関数を配列で並べる。\
 * 値が NULL で終了。\
 * 値が MLOADIMAGE_CHECKFORMAT_SKIP で、スキップ。
 * @p:headsize 読み込むヘッダの最大サイズ (0 以下で 17 byte)\
 * PNG: 8byte、WEBP: 12byte、TGA: 17byte
 * @r:エラーコード。\
 * いずれにも一致しなかった場合、MLKERR_UNSUPPORTED が返る。 */

mlkerr mLoadImage_checkFormat(mLoadImageType *dst,
	mLoadImageOpen *open,mFuncLoadImageCheck *checks,int headsize)
{
	mIO *io;
	uint8_t *buf;
	mlkbool ret;

	if(headsize <= 0) headsize = 17;

	//ヘッダバッファ

	buf = (uint8_t *)mMalloc(headsize);
	if(!buf) return MLKERR_ALLOC;

	//先頭バイト読み込み

	io = mLoadImage_openIO(open);
	if(!io)
	{
		mFree(buf);
		return MLKERR_OPEN;
	}

	headsize = mIO_read(io, buf, headsize);

	mIO_close(io);

	//各判定

	for(ret = FALSE; *checks; checks++)
	{
		if(*checks == MLOADIMAGE_CHECKFORMAT_SKIP)
			continue;
	
		ret = (checks[0])(dst, buf, headsize);
		if(ret) break;
	}

	//

	mFree(buf);

	return (ret)? MLKERR_OK: MLKERR_UNSUPPORTED;
}

/**@ イメージのバッファを解放
 *
 * @d:imgbuf と、各ラインのバッファを解放する。 */

void mLoadImage_freeImage(mLoadImage *p)
{
	uint8_t **ppbuf;
	int i;

	ppbuf = p->imgbuf;

	if(ppbuf)
	{
		for(i = p->height; i > 0; i--, ppbuf++)
			mFree(*ppbuf);

		mFree(p->imgbuf);
	}
}

/**@ 読み込み用のイメージバッファを確保
 *
 * @d:imgbuf, line_bytes の値が上書きされる。
 * 
 * @p:line_bytes Y1行のバイト数。\
 * 0 以下で、カラータイプから計算 (4byte単位)。
 * @r:すべて確保できたか */

mlkbool mLoadImage_allocImage(mLoadImage *p,int line_bytes)
{
	uint8_t **ppbuf;
	int i;

	//Y1行バイト数

	if(line_bytes <= 0)
	{
		line_bytes = mLoadImage_getLineBytes(p);
		line_bytes = (line_bytes + 3) & (~3);
	}

	p->line_bytes = line_bytes;

	//イメージバッファ

	ppbuf = p->imgbuf = (uint8_t **)mMalloc0(p->height * sizeof(void*));
	if(!ppbuf) return FALSE;

	for(i = p->height; i > 0; i--, ppbuf++)
	{
		*ppbuf = (uint8_t *)mMalloc(line_bytes);
		if(!(*ppbuf))
		{
			mLoadImage_freeImage(p);
			return FALSE;
		}
	}

	return TRUE;
}

/**@ 読み込み用のイメージバッファを確保
 *
 * @d:全体が一つのバッファのイメージからポインタをセットする。\
 * imgbuf を確保し、各ラインには buf 内のポインタをセットする。
 * 
 * @p:line_bytes buf の Y1行のバイト数
 * @r:imgbuf が確保できたか */

mlkbool mLoadImage_allocImageFromBuf(mLoadImage *p,void *buf,int line_bytes)
{
	uint8_t **ppbuf,*ps;
	int i;

	ppbuf = (uint8_t **)mMalloc(sizeof(void *) * p->height);
	if(!ppbuf) return FALSE;

	p->imgbuf = ppbuf;
	p->line_bytes = line_bytes;

	//ポインタセット

	ps = (uint8_t *)buf;

	for(i = p->height; i > 0; i--)
	{
		*(ppbuf++) = ps;
	
		ps += line_bytes;
	}

	return TRUE;
}

/**@ Y1行のバイト数を計算
 *
 * @r:必要な最小限のバイト数 */

int mLoadImage_getLineBytes(mLoadImage *p)
{
	int ret;

	ret = p->width;

	if(p->bits_per_sample == 16)
		ret <<= 1;

	switch(p->coltype)
	{
		case MLOADIMAGE_COLTYPE_RGB:
			ret *= 3;
			break;
		case MLOADIMAGE_COLTYPE_RGBA:
		case MLOADIMAGE_COLTYPE_CMYK:
			ret <<= 2;
			break;
		case MLOADIMAGE_COLTYPE_GRAY_A:
			ret <<= 1;
			break;
	}

	return ret;
}

/**@ 解像度を DPI 単位で取得
 *
 * @r:DPI で取得できない場合 FALSE */

mlkbool mLoadImage_getDPI(mLoadImage *p,int *horz,int *vert)
{
	int h,v;

	h = p->reso_horz;
	v = p->reso_vert;

	if(h <= 0 || v <= 0) return FALSE;

	switch(p->reso_unit)
	{
		//dot/inch
		case MLOADIMAGE_RESOUNIT_DPI:
			*horz = h;
			*vert = v;
			return TRUE;
		//dot/meter
		case MLOADIMAGE_RESOUNIT_DPM:
			*horz = mDPMtoDPI(h);
			*vert = mDPMtoDPI(v);
			return TRUE;
		//dot/centimeter
		case MLOADIMAGE_RESOUNIT_DPCM:
			*horz = mDPMtoDPI(h * 100);
			*vert = mDPMtoDPI(v * 100);
			return TRUE;
	}

	return FALSE;
}

