/*$
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.
$*/

/*****************************
 * テクスチャ
 *****************************/

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

#include "mlk.h"
#include "mlk_list.h"
#include "mlk_loadimage.h"
#include "mlk_imagebuf.h"
#include "mlk_charset.h"
#include "mlk_stdio.h"
#include "mlk_file.h"
#include "mlk_util.h"


//----------------

typedef struct _TexItem
{
	mListItem i;
	char *path;
	uint8_t *buf;
	int w,h;
}TexItem;

typedef struct
{
	mListItem i;
	char *path;
	uint32_t hash;
	int32_t col;
}TexPathItem;

//----------------


//=========================
// テクスチャ画像リスト
//=========================


/** 画像読み込み */

static mlkbool _load_image(TexItem *pi)
{
	mImageBuf *img;
	mLoadImageOpen open;
	mLoadImageType type;
	mFuncLoadImageCheck checks[] = {
		mLoadImage_checkPNG, mLoadImage_checkBMP, mLoadImage_checkGIF, 0
	};
	int w,h,ix,iy,size,r,g,b,a;
	uint8_t *ps,*pd;
	
	//フォーマット判定

	open.type = MLOADIMAGE_OPEN_FILENAME;
	open.filename = pi->path;

	if(mLoadImage_checkFormat(&type, &open, checks, 8))
		return FALSE;

	//読み込み

	img = mImageBuf_loadImage(&open, &type, 32, 0);
	if(!img) return FALSE;

	w = img->width;
	h = img->height;

	pi->w = w;
	pi->h = h;

	//バッファ確保

	size = (w * h + 3) & (~3);

	pi->buf = (uint8_t *)mMalloc(size);
	if(!pi->buf)
	{
		mImageBuf_free(img);
		return FALSE;
	}

	//変換

	ps = img->buf;
	pd = pi->buf;

	for(iy = h; iy; iy--)
	{
		for(ix = w; ix; ix--, ps += 4)
		{
			//背景白とアルファ合成
			
			a = ps[3];

			if(a == 0)
				r = g = b = 255;
			else if(a == 255)
			{
				r = ps[0];
				g = ps[1];
				b = ps[2];
			}
			else
			{
				r = (ps[0] - 255) * a / 255 + 255;
				g = (ps[1] - 255) * a / 255 + 255;
				b = (ps[2] - 255) * a / 255 + 255;
			}
			
			*(pd++) = 255 - ((r * 77 + g * 150 + b * 29) >> 8);
		}
	}

	//

	mImageBuf_free(img);

	return TRUE;
}

/** パスから検索 */

static TexItem *_search_path(mList *list,const char *path)
{
	TexItem *pi;

	MLIST_FOR(*list, pi, TexItem, i)
	{
		if(strcmp(path, pi->path) == 0)
			return pi;
	}

	return NULL;
}

/* アイテム削除ハンドラ */

static void _item_destroy(mList *list,mListItem *item)
{
	mFree(((TexItem *)item)->buf);
	mFree(((TexItem *)item)->path);
}


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


/** リスト初期化 */

void TextureList_init(mList *list)
{
	list->item_destroy = _item_destroy;
}

/** テクスチャ読み込み */

TexItem *TextureList_load(mList *list,const char *path)
{
	TexItem *pi;

	//同じパスが存在するか

	pi = _search_path(list, path);
	if(pi) return pi;

	//追加

	pi = (TexItem *)mListAppendNew(list, sizeof(TexItem));
	if(!pi) return NULL;

	pi->path = mStrdup(path);

	if(!_load_image(pi))
	{
		mPutUTF8_format_stdout(" ! load error '%s'\n", path);
		mListDelete(list, MLISTITEM(pi));
		return NULL;
	}

	return pi;
}

/** 濃度を取得 */

uint8_t TextureItem_getOpacity(TexItem *pi,int x,int y)
{
	int w,h;

	w = pi->w;
	h = pi->h;

/*
	if(x < 0)
		x += (-x / w + 1) * w;

	if(y < 0)
		y += (-y / h + 1) * h;
*/

	return *(pi->buf + (y % h) * w + (x % w));
}


//=========================
// テクスチャパスリスト
//=========================


/* パスから検索 */

static TexPathItem *_texpath_search(mList *list,const char *path)
{
	TexPathItem *pi;
	uint32_t hash;

	hash = mCalcStringHash(path);

	MLIST_FOR(*list, pi, TexPathItem, i)
	{
		if(pi->hash == hash && strcmp(path, pi->path) == 0)
			return pi;
	}

	return NULL;
}

/* アイテム削除ハンドラ */

static void _pathitem_destroy(mList *list,mListItem *item)
{
	mFree(((TexPathItem *)item)->path);
}


/** リスト初期化 */

void TexturePathList_init(mList *list)
{
	list->item_destroy = _pathitem_destroy;
}

/** パス追加 */

void TexturePathList_add(mList *list,const char *path,int col)
{
	TexPathItem *pi;

	//同じパスが存在するか

	pi = _texpath_search(list, path);
	if(pi) return;

	//追加

	pi = (TexPathItem *)mListAppendNew(list, sizeof(TexPathItem));
	if(!pi) return;

	pi->path = mStrdup(path);
	pi->hash = mCalcStringHash(path);
	pi->col = MLK_RGB(col,col,col);
}

/** パスから色取得
 *
 * return: -1 で変換なし */

int TexturePathList_getColor(mList *list,const char *path)
{
	TexPathItem *pi;

	pi = _texpath_search(list, path);
	if(!pi)
		return -1;
	else
		return pi->col;
}

/** テキストに出力 */

void TexturePathList_output(mList *list,const char *filename)
{
	FILE *fp;
	TexPathItem *pi;

	fp = mFILEopen(filename, "wt");
	if(!fp)
	{
		mPutUTF8_format_stdout("! open error '%s'\n", filename);
		return;
	}

	fputs(
"; path@[0-100] : (density:%) 0=white,100=black\n"
"; path#[0-255] : (8bit col)  0=black,255=white\n"
"; ! do not convert paths without a suffix.\n"
"; {ex} tone/60_20.png@20\n\n"
	,fp);

	MLIST_FOR(*list, pi, TexPathItem, i)
	{
		fputs(pi->path, fp);
		fputc('\n', fp);
	}

	fclose(fp);

	mPutUTF8_format_stdout("=> %s\n", filename);
}

/** テキストから読み込み */

void TexturePathList_readFile(mList *list,const char *filename)
{
	mFileText *file;
	char *top,*pc;
	int len,cnt,col;

	file = mFileText_readFile(filename);
	if(!file)
	{
		mPutUTF8_format_stdout("! open error '%s'\n", filename);
		return;
	}

	while(1)
	{
		top = mFileText_nextLine_skip(file);
		if(!top) break;

		//コメント

		if(*top == ';') continue;

		//色値取得

		len = strlen(top);
		cnt = 0;
		col = -1;

		for(pc = top + len - 1; *pc >= '0' && *pc <= '9' && cnt < 3 && cnt < len; pc--, cnt++);

		if(cnt > 0 && (*pc == '@' || *pc == '#'))
		{
			col = atoi(pc + 1);

			if(*pc == '#')
			{
				if(col > 255) col = 255;
			}
			else if(*pc == '@')
			{
				if(col > 100) col = 100;
				col = 255 * (100 - col) / 100;
			}

			*pc = 0;
		}

		//変換なしなら追加しない

		if(col != -1)
			TexturePathList_add(list, top, col);
	}

	mFileText_end(file);
}
