/*
 *	clipdurc.c
 *
 *	存続カウンタ
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2016 Naoyuki Sawa
 *
 *	* Mon Mar 07 21:56:21 JST 2016 Naoyuki Sawa
 *	- 1st リリース。
 */
#include "clip.h"
/****************************************************************************
 *	アプリケーション用関数
 ****************************************************************************/
ST_DurCnt* DurCnt_New() {
	//構造体のメモリを確保する。
	ST_DurCnt* pDurCnt = malloc(sizeof(ST_DurCnt));
	if(!pDurCnt) { DIE(); }	//メモリ不足
	//ハッシュテーブルを作成する。
	pDurCnt->ht = ght_create(0);	//初期サイズを無指定とする。
	ght_set_rehash(pDurCnt->ht, 1);	//自動リサイズを有効にする。
	return pDurCnt;
}
/*--------------------------------------------------------------------------*/
void DurCnt_Free(ST_DurCnt* pDurCnt) {
	//DurCnt_Exec()を二回呼び出す事で、確実に全てのチャネルが削除される。
	DurCnt_Exec(pDurCnt);	//一回目
	DurCnt_Exec(pDurCnt);	//二回目
	if(ght_size(pDurCnt->ht)) { DIE(); }	//バグ
	//ハッシュテーブルを削除する。
	ght_finalize(pDurCnt->ht);
	//構造体のメモリを開放する。
	free(pDurCnt);
}
/*--------------------------------------------------------------------------*/
void DurCnt_Exec(ST_DurCnt* pDurCnt) {
	ght_iterator_t iterator;
	ST_DurCntCh* pDurCntCh;
	const void* p_key_data;
	int i_key_size;
	//全てのチャネルを走査して…
	for(pDurCntCh = ght_first_keysize(pDurCnt->ht, &iterator, &p_key_data, &i_key_size);
	    pDurCntCh;
	    pDurCntCh = ght_next_keysize( pDurCnt->ht, &iterator, &p_key_data, &i_key_size)) {
		//タッチフラグがセットされていたら…
		// - 即ち、前のフレーム中にこのチャネルに対して、一度でもDurCnt_GetVal()が呼び出されていたら…
		if(pDurCntCh->bTouch) {
			//次のフレームのために、タッチフラグをクリアする。
			// - 次のフレームでこのチャネルに対して、一度もDurCnt_GetVal()が呼び出されなかったら、次回のDurCnt_Exec()の処理で、このチャネルは削除される事になる。
			pDurCntCh->bTouch = 0;
			//チャネル値を進める。
			// - 次のフレームでこのチャネルに対してDurCnt_GetVal()が呼ばれた場合、このチャネル値が返される。
		    #if 0
			if(pDurCntCh->iVal < INT_MAX) { pDurCntCh->iVal++; }	//飽和カウンタとする場合は、こちらを有効にしてください。
		    #else
			                              { pDurCntCh->iVal++; }	//ラップアラウンドカウンタとする場合は、こちらを有効にしてください。	2016/03/07現在は、ラップアラウンドカウンタとしています。
		    #endif
		//タッチフラグがクリアされていたら…
		// - 即ち、前のフレーム中にこのチャネルに対して、一度もDurCnt_GetVal()が呼び出されなかったら…
		} else {
			//このチャネルを登録解除する。
			ght_remove(pDurCnt->ht, i_key_size, p_key_data);	//イテレータは次のノードを指しているので、イテレーションの中で当該ノードをght_remove()しても安全です。ght_hash_table.cのght_finalize()で動作実績が有ります。
			//チャネルのメモリを開放する。
			free(pDurCntCh);
		}
	}
}
/*--------------------------------------------------------------------------*/
int DurCntCh_GetVal_f(ST_DurCnt* pDurCnt, const char* sFmt, ...) {
	int iVal;
	va_list ap;
	va_start(ap, sFmt);
	iVal = DurCntCh_GetVal_g(pDurCnt, sFmt, ap);
	va_end(ap);
	return iVal;
}
int DurCntCh_GetVal_g(ST_DurCnt* pDurCnt, const char* sFmt, va_list ap) {
	char sName[256/*調整可*/];
	vsnprintf(sName, ARRAY_SIZE(sName), sFmt, ap);
	return DurCntCh_GetVal(pDurCnt, sName);
}
int DurCntCh_GetVal(ST_DurCnt* pDurCnt, const char* sName) {
	//キーサイズ(=名前の長さ(終端のnul込み))を求めておく。
	const int i_key_size = strlen(sName) + 1/*nul*/;
	//この名前のチャネルが既に存在しているか?
	ST_DurCntCh* pDurCntCh = ght_get(pDurCnt->ht, i_key_size, sName);
	//この名前のチャネルがまだ存在していなければ…
	if(!pDurCntCh) {
		//新しいチャネルを作成し、登録する。
		pDurCntCh = calloc(1, sizeof(ST_DurCntCh));	//この時点ではbTouch=0,iVal=0。この下の処理でbTouch=1,iVal=0になる。
		if(!pDurCntCh) { DIE(); }	//メモリ不足
		if(ght_insert(pDurCnt->ht, pDurCntCh, i_key_size, sName)) { DIE(); }	//バグ
	}
	//タッチフラグをセットする。
	// - 既存のチャネルでも新規のチャネルでも、次のフレームに存続する事が確定する。
	pDurCntCh->bTouch = 1;
	return pDurCntCh->iVal;
}
/*--------------------------------------------------------------------------*/
void DurCntCh_Delete_f(ST_DurCnt* pDurCnt, const char* sFmt, ...) {
	va_list ap;
	va_start(ap, sFmt);
	DurCntCh_Delete_g(pDurCnt, sFmt, ap);
	va_end(ap);
}
void DurCntCh_Delete_g(ST_DurCnt* pDurCnt, const char* sFmt, va_list ap) {
	char sName[256/*調整可*/];
	vsnprintf(sName, ARRAY_SIZE(sName), sFmt, ap);
	DurCntCh_Delete(pDurCnt, sName);
}
void DurCntCh_Delete(ST_DurCnt* pDurCnt, const char* sName) {
	//キーサイズ(=名前の長さ(終端のnul込み))を求めておく。
	const int i_key_size = strlen(sName) + 1/*nul*/;
	//この名前のチャネルが存在していたら、登録を解除する。
	// - この名前のチャネルが存在していなければ、'pDurCnt=NULL'となる。
	ST_DurCntCh* pDurCntCh/*may be NULL*/ = ght_remove(pDurCnt->ht, i_key_size, sName);
	//この名前のチャネルが存在していたら、チャネルのメモリを開放する。
	// - この名前のチャネルが存在していなければ、'free(NULL)'となり、安全なダミー操作となります。
	free(pDurCntCh/*may be NULL*/);
}
/****************************************************************************
 *	使用例
 ****************************************************************************/
//※TODO:使用例を書け。
