/**
 * httpdサーバのサンプルです。
 * ROMにある定義済みファイルの入出力と、クロスドメインRESTが確認できます。
 */


#include "sketch_config.h"	//must include this file at first!
#include "native_function.h"
#include "boot/sketch.h"
#include "NyLPC_uipService.h"
#include "NyLPC_httpService.h"

#include "cRemoteMcuRequestparser.h"
#include "cConfiglationStorage.h"

#include "NyLPC_utils.h"
#include <stdio.h>



//イーサネット用の情報
static struct NyLPC_TEthAddr ethaddr;
static struct NyLPC_TIPv4Addr ipaddr;
static struct NyLPC_TIPv4Addr netmask;
static struct NyLPC_TIPv4Addr gateway;
/**
 * アクセスコントロール値
 * 0:サブネット(default)
 * 1:グローバル
 * その他:default
 */
static struct{
	/** mvm.apiのスコープ*/
	NyLPC_TUInt8 mvm_access;
	/**setup.apiのスコープ*/
	NyLPC_TUInt8 setup_access;
}cgi_setting;
static NyLPC_TUInt16 port;
static NyLPC_TcMutex_t _vm_mutex;
/**
 * プラットフォーム名
 */
static const char* _platform_name="Unknown platform";

#define ACCESS_CTRL_SUBNET 0x00 //default
#define ACCESS_CTRL_ALL 0x01


//ソケットの受信バッファサイズ
#define SIZE_OF_RX 512
//同時にオープンするソケットの数
#define NUM_OF_TH 5
//持続性接続状態のソケット最大数
#define LIMIT_OF_PERSISTENT_CONNECTION 2

//MVMのVERSION情報
#define MVM_VERSION "MiMicVM/1.0;Json/1.0"
#define APPLICATION_VERSION "MiMicRemoteMCU/1.3"
//スレッドのスタックサイズ
#define THREAD_STACK_SIZE 256+256



struct TProc{
	NyLPC_TcThread_t th;
	NyLPC_TcTcpSocket_t socket;
	char rbuf[SIZE_OF_RX];
	struct TRemoteMcuRequest reqheader;
}proc[NUM_OF_TH];

//ROMFSの定義
extern struct NyLPC_TRomFileData file_index_html;
extern struct NyLPC_TRomFileData file_mimic_css;
extern struct NyLPC_TRomFileData file_mimiclogo_png;
extern struct NyLPC_TRomFileData file_setup_api;


//private 関数

static NyLPC_TUInt16 parseReqHeader(NyLPC_TcHttpStream_t* i_st,struct TRemoteMcuRequest* o_reqh);
static NyLPC_TBool writeError(NyLPC_TcHttpStream_t* i_st,const struct NyLPC_THttpBasicHeader* i_rqh,NyLPC_TUInt16 i_status);
static NyLPC_TBool writeFile(NyLPC_TcHttpStream_t* i_st,const struct NyLPC_THttpBasicHeader* i_rqh,const struct NyLPC_TRomFileData* i_file);

static NyLPC_TBool writeSetupUpdateJson(NyLPC_TcHttpStream_t* i_st,const struct TRemoteMcuRequest* i_rqh);
static NyLPC_TBool writeSetupCurrentJson(NyLPC_TcHttpStream_t* i_st,const struct TRemoteMcuRequest* i_rqh);


//スケッチ

void setup(void)
{
	int i;
	const struct TMimicConfigulation* config=cConfiglationStorage_loadMiMicConfigulation();
	//コンフィギュレーションの読み出し
	ethaddr.addr[0]=(NyLPC_TUInt8)((config->mac_00_01_02_03>>24)&0xff);
	ethaddr.addr[1]=(NyLPC_TUInt8)((config->mac_00_01_02_03>>16)&0xff);
	ethaddr.addr[2]=(NyLPC_TUInt8)((config->mac_00_01_02_03>>8)&0xff);
	ethaddr.addr[3]=(NyLPC_TUInt8)((config->mac_00_01_02_03>>0)&0xff);
	ethaddr.addr[4]=(NyLPC_TUInt8)((config->mac_04_05_xx_xx>>24)&0xff);
	ethaddr.addr[5]=(NyLPC_TUInt8)((config->mac_04_05_xx_xx>>16)&0xff);
	ipaddr.v=NyLPC_htonl(config->ipv4_addr_net);
	netmask.v=NyLPC_htonl(config->ipv4_mask_net);
	gateway.v=NyLPC_htonl(config->ipv4_drut_net);
	port=config->ipv4_port;
	//アクセスモード値は正規化するよ。
	cgi_setting.mvm_access=(NyLPC_TUInt8)((config->accessmode>>24)&0xff);
	if(cgi_setting.mvm_access>0x01){
		cgi_setting.mvm_access=0;
	}
	cgi_setting.setup_access=(NyLPC_TUInt8)((config->accessmode>>16)&0xff);
	if(cgi_setting.setup_access>0x01){
		cgi_setting.setup_access=0;
	}
	//VMの排他ロック
	NyLPC_cMutex_initialize(&_vm_mutex);



	//uipサービス初期化。いろいろ利用可能に。
	NyLPC_cUipService_initialize();
	for(i=0;i<NUM_OF_TH;i++){
		NyLPC_cThread_initialize(&(proc[i].th),THREAD_STACK_SIZE);
		NyLPC_cTcpSocket_initialize(&(proc[i].socket),proc[i].rbuf,SIZE_OF_RX);
	}
}

//ステータス用。
static int num_of_reqest=0;
static int num_of_error=0;
static int num_of_connect=0;



//URLハンドラ
static NyLPC_TBool mvm(NyLPC_TcHttpStream_t* i_st,const struct TRemoteMcuRequest* i_rqh);
static NyLPC_TBool writeStatus(NyLPC_TcHttpStream_t* i_st,const struct TRemoteMcuRequest* i_rqh);

/**
 * 接続がサブネットアクセスか調べる。
 */
NyLPC_TBool isSubnetAccess(const NyLPC_TcTcpSocket_t* i_sock)
{
	return NyLPC_TIPv4Addr_isEqualWithMask(&(i_sock->uip_connr.ripaddr), i_sock->uip_connr.lipaddr,&netmask);
}

//Httpのセッション関数
static int server(void* p)
{
	struct TProc* proc=(struct TProc*)p;
	NyLPC_TUInt16 ret;
	NyLPC_TcHttpStream_t st;


	if(!NyLPC_cTcpSocket_accept(&(proc->socket),3000)){
		return -1;
	}
	num_of_connect++;

	//TCPのオープン
	if(NyLPC_cHttpStream_initialize(&st,&(proc->socket))){
		//サブネットアクセスの確認
		for(;;){
			ret=parseReqHeader(&st,&(proc->reqheader));
			num_of_reqest++;
			//コネクションが増えすぎたら持続性接続を停止するためにCLOSEDにする。
			if(num_of_connect>LIMIT_OF_PERSISTENT_CONNECTION){
				proc->reqheader.super.connection=NyLPC_THttpMessgeHeader_Connection_CLOSE;
			}
			if(ret!=200){
				//200以外なら、エラーコード返却
				writeError(&st,&(proc->reqheader.super),ret);
				num_of_error++;
				break;
			}
			switch(proc->reqheader._content_id){
			case TRemoteMcuRequest_CONTENT_ID_MIMICBC:
				//アクセスコントロールがSUBNETで、サブネット外からの場合
				if(cgi_setting.mvm_access==ACCESS_CTRL_SUBNET && (!isSubnetAccess(&(proc->socket)))){
					//アクセス不可
					writeError(&st,&(proc->reqheader.super),403);
				}else{
					if(mvm(&st,&(proc->reqheader))){
						continue;
					}
				}
				break;
			case TRemoteMcuRequest_CONTENT_ID_INDEX:
				if(writeFile(&st,&(proc->reqheader.super),&file_index_html)){
					continue;
				}
				break;
			case TRemoteMcuRequest_CONTENT_ID_SETUP:
				//アクセスコントロールがSUBNETで、サブネット外からの場合
				if(cgi_setting.setup_access==ACCESS_CTRL_SUBNET && (!isSubnetAccess(&(proc->socket)))){
					//アクセス不可
					writeError(&st,&(proc->reqheader.super),403);
				}else{
					switch(proc->reqheader.content.setup.cval){
					case TRemoteMcuRequest_QVAL_C_GET:
						if(writeSetupCurrentJson(&st,&(proc->reqheader))){
							continue;
						}
						break;
					case TRemoteMcuRequest_QVAL_C_UPDATE:
						if(writeSetupUpdateJson(&st,&(proc->reqheader))){
							continue;
						}
						break;
					default:
						if(writeFile(&st,&(proc->reqheader.super),&file_setup_api)){
							continue;
						}
					}
				}
				break;
			case TRemoteMcuRequest_CONTENT_ID_CSS:
				if(writeFile(&st,&(proc->reqheader.super),&file_mimic_css)){
					continue;
				}
				break;
			case TRemoteMcuRequest_CONTENT_ID_LOGO:
				if(writeFile(&st,&(proc->reqheader.super),&file_mimiclogo_png)){
					continue;
				}
				break;

			case TRemoteMcuRequest_CONTENT_ID_UNKNOWN:
				writeError(&st,&(proc->reqheader.super),404);
				break;
			case TRemoteMcuRequest_CONTENT_ID_STATUS:
				if(writeStatus(&st,&(proc->reqheader))){
					continue;
				}
			}
			break;
		}
		NyLPC_cHttpStream_finalize(&st);
	}
	//5秒以内に切断
	NyLPC_cTcpSocket_close(&(proc->socket),5000);
	num_of_connect--;
	return 0;
}


void loop(void)
{
	NyLPC_TcIPv4Config_t config;
	NyLPC_TcTcpListener_t listener;
	int i;


	NyLPC_cIPv4Config_initialzeForEthernet(&config,&ethaddr,1480);
	NyLPC_cIPv4Config_setDefaultRoute(&config,&gateway);
	NyLPC_cIPv4Config_setIp(&config,&ipaddr,&netmask);


	NyLPC_cTcpListener_initialize(&listener,port);
	NyLPC_cUipService_start(&config);
	//プラットフォーム名を推測(デバイス名の初めの1文字だけ見る。)
	switch(*(NyLPC_cUipService_refDeviceName())){
	case 'L':
		_platform_name="LPCXPresso1769";
		break;
	case 'D':
		_platform_name="mbed";
		break;
	default:
		break;
	}
	for(;;){
		//ターミネイト状態のタスクを検索
		for(i=0;i<NUM_OF_TH;i++){
			if(NyLPC_cThread_isTerminated(&(proc[i].th))){
				//リスニング
				if(!NyLPC_cTcpListener_listen(&listener,&(proc[i].socket),5000)){
					continue;
				}
				//タスク起動
				NyLPC_cThread_start(&(proc[i].th),server,&(proc[i]));
			}
		}
	}
	for(;;){}
}

////////////////////////////////////////////////////////////////////////////////
// private functions
////////////////////////////////////////////////////////////////////////////////
/**
 * イベントハンドラを継承
 */
struct NyLPC_TRemoteMCU_EventHandler
{
	struct NyLPC_TcMiMicVM_TEvent super;
	const struct TRemoteMcuRequest* req;
	NyLPC_TcHttpStream_t* st;
	NyLPC_TUInt16 db_pos;
	/** ストリームへ出力したデータの数*/
	NyLPC_TUInt16 st_len;
	/** Bodyのライター(参照している)*/
	NyLPC_TcHttpBodyWriter_t bw;

};

/**
 * ストリームハンドラ(put)
 */
static NyLPC_TBool mvmputs_json(struct NyLPC_TcMiMicVM_TEvent* i_eh,NyLPC_TUInt32 i_val)
{
	struct NyLPC_TRemoteMCU_EventHandler* eh=(struct NyLPC_TRemoteMCU_EventHandler*)i_eh;
	if(eh->st_len>0){
		eh->st_len++;
		return NyLPC_cHttpBodyWriter_format(&(eh->bw),",%u",i_val);
	}else{
		eh->st_len++;
		return NyLPC_cHttpBodyWriter_format(&(eh->bw),"%u",i_val);
	}
}

/**
 * ストリームハンドラ(get)
 */
static NyLPC_TBool mvmgets(struct NyLPC_TcMiMicVM_TEvent* i_eh,NyLPC_TUInt32* o_val)
{
	struct NyLPC_TRemoteMCU_EventHandler* eh=(struct NyLPC_TRemoteMCU_EventHandler*)i_eh;
	//読み出し済みDBサイズの確認
	if(eh->req->content.mvm.vm_instruction.db_len<=eh->db_pos){
		//読めない
		return NyLPC_TBool_FALSE;
	}
	*o_val=eh->req->content.mvm.vm_instruction.db_part[eh->db_pos];
	eh->db_pos++;
	return NyLPC_TBool_TRUE;
}
/**
 * ネイティブCALLハンドラ
 */
static NyLPC_TUInt32 nativeCall(struct NyLPC_TcMiMicVM_TEvent* i_evh,NyLPC_TUInt32 i_id,NyLPC_TcMiMicVM_t* i_vm)
{
	(void)i_evh;
	NyLPC_TNativeFunction f=getNativeFunctionById(i_id);
	if(f==NULL){
		return NyLPC_cMiMicVM_RESULT_RUNTIME_NG_UNKNOWN_CALL;
	}
	return f(i_vm)?NyLPC_cMiMicVM_RESULT_OK:NyLPC_cMiMicVM_RESULT_RUNTIME_NG_CALL;
}


static void mvmsleep(struct NyLPC_TcMiMicVM_TEvent* i_eh,NyLPC_TUInt32 i_sleep_in_msec)
{
	(void)i_eh;
	NyLPC_cThread_sleep(i_sleep_in_msec);
}

static NyLPC_TBool writeJsonHeader(NyLPC_TcHttpStream_t* i_st,NyLPC_TcHttpHeaderWriter_t* i_work,const struct NyLPC_THttpBasicHeader* i_rqh)
{
	//ヘッダライタの生成
	if(!NyLPC_cHttpHeaderWriter_initialize(i_work,i_st,i_rqh)){
		return NyLPC_TBool_FALSE;
	}
	//ヘッダ書込み
	if(!NyLPC_THttpBasicHeader_isPersistent(i_rqh)){
		NyLPC_cHttpHeaderWriter_setClose(i_work);
	}
	//@bug HTTP/1.1未満のクライアントを考慮していない。
	NyLPC_cHttpHeaderWriter_setChunked(i_work);
	//ヘッダの基本情報出力
	NyLPC_cHttpHeaderWriter_writeHeader(i_work,200);
	//拡張メッセージヘッダの出力
	NyLPC_cHttpHeaderWriter_writeMessage(i_work,"Content-type","application/json");
	NyLPC_cHttpHeaderWriter_writeMessage(i_work,"Access-Control-Allow-Origin","*");

	//ヘッダ書込み終わり。(最後だけチェック)
	if(!NyLPC_cHttpHeaderWriter_close(i_work)){
		NyLPC_cHttpHeaderWriter_finalize(i_work);
		return NyLPC_TBool_FALSE;
	}
	NyLPC_cHttpHeaderWriter_finalize(&(work.hw));
	return NyLPC_TBool_TRUE;
}

/**
 * setupコンフィギュレーション値を設定する。
 */
static NyLPC_TBool writeSetupUpdateJson(NyLPC_TcHttpStream_t* i_st,const struct TRemoteMcuRequest* i_rqh)
{
	struct TMimicConfigulation cfg_image;
	NyLPC_TBool ret=NyLPC_TBool_TRUE;
	union{
		NyLPC_TcHttpHeaderWriter_t hw;
		NyLPC_TcHttpBodyWriter_t bw;
	}work;
	//パラメータ→ROMイメージ変換
	cfg_image.fast_boot=0xffffffff;
	cfg_image.mac_00_01_02_03=(i_rqh->content.setup.param_buf[0]);
	cfg_image.mac_04_05_xx_xx=(i_rqh->content.setup.param_buf[1]&0xffff0000);
	cfg_image.ipv4_addr_net  =i_rqh->content.setup.param_buf[2];
	cfg_image.ipv4_mask_net  =i_rqh->content.setup.param_buf[3];
	cfg_image.ipv4_drut_net  =i_rqh->content.setup.param_buf[4];
	cfg_image.ipv4_port =(NyLPC_TUInt16)(i_rqh->content.setup.param_buf[5]>>16);
	cfg_image.accessmode=(i_rqh->content.setup.param_buf[6]);
	//一応確認。
	if((cfg_image.ipv4_port==0)|| (cfg_image.accessmode & 0xFEFE0000)!=0x00000000){
		return NyLPC_TBool_FALSE;
	}

	//FreeRTOSの停止
	vTaskSuspendAll();
	//Flashへの書き込み
	ret=cConfiglationStorage_updateConfigulation(&cfg_image);
	//FreeRTOSの復帰
	xTaskResumeAll();

	//結果返却
	if(!writeJsonHeader(i_st,&(work.hw),&(i_rqh->super))){
		return NyLPC_TBool_FALSE;
	}
	//Bodyの書込み
	NyLPC_cHttpBodyWriter_initialize(&(work.bw),i_st);
	//チャンク転送設定
	NyLPC_cHttpBodyWriter_setChunked(&(work.bw));

	//JSONを書く。
	if(!NyLPC_cHttpBodyWriter_format(&(work.bw),
		"{\"application\":\""APPLICATION_VERSION";%s\",\"result\":%u}",
		_platform_name,ret?0x80000000:0x00000000))
	{
		return NyLPC_TBool_FALSE;
	}
	//エラーチェック
	if(!NyLPC_cHttpBodyWriter_close(&(work.bw))){
		NyLPC_OnErrorGoto(Error_NyLPC_cHttpBodyWriter_close);
	}
	NyLPC_cHttpBodyWriter_finalize(&hw);

	return NyLPC_TBool_TRUE;
Error_NyLPC_cHttpBodyWriter_close:
	return NyLPC_TBool_FALSE;
}


/**
 * setupコンフィギュレーション値を返す。
 */
static NyLPC_TBool writeSetupCurrentJson(NyLPC_TcHttpStream_t* i_st,const struct TRemoteMcuRequest* i_rqh)
{
	union{
		NyLPC_TcHttpHeaderWriter_t hw;
		NyLPC_TcHttpBodyWriter_t bw;
	}work;
	const struct TMimicConfigulation* config;
	vTaskSuspendAll();
	config=cConfiglationStorage_loadMiMicConfigulation();
	xTaskResumeAll();

	//結果返却
	if(!writeJsonHeader(i_st,&(work.hw),&(i_rqh->super))){
		return NyLPC_TBool_FALSE;
	}

	//Bodyの書込み
	NyLPC_cHttpBodyWriter_initialize(&(work.bw),i_st);
	//チャンク転送設定
	NyLPC_cHttpBodyWriter_setChunked(&(work.bw));

	//JSONを書く。
	if(!NyLPC_cHttpBodyWriter_format(&(work.bw),
		"{\"application\":\""APPLICATION_VERSION";%s\",\"mac00010203\":%u,\"mac0405xxxx\":%u,\"ip\":%u,\"mask\":%u,\"droute\":%u,\"port\":%u,\"access\":%u}",
		_platform_name,
		config->mac_00_01_02_03,
		config->mac_04_05_xx_xx,
		config->ipv4_addr_net,
		config->ipv4_mask_net,
		config->ipv4_drut_net,
		config->ipv4_port,
		config->accessmode
		)){
		return NyLPC_TBool_FALSE;
	}
	//エラーチェック
	if(!NyLPC_cHttpBodyWriter_close(&(work.bw))){
		NyLPC_OnErrorGoto(Error_NyLPC_cHttpBodyWriter_close);
	}
	NyLPC_cHttpBodyWriter_finalize(&hw);

	return NyLPC_TBool_TRUE;
Error_NyLPC_cHttpBodyWriter_close:
	return NyLPC_TBool_FALSE;
}



/**
 * RemoteMCUのステータスを返す。基本的にjson
 * {
 * 	application:"[VERSION]"
 * }
 */
static NyLPC_TBool writeStatus(NyLPC_TcHttpStream_t* i_st,const struct TRemoteMcuRequest* i_rqh)
{
	union{
		NyLPC_TcHttpHeaderWriter_t hw;
		NyLPC_TcHttpBodyWriter_t bw;
	}work;
	if(!writeJsonHeader(i_st,&(work.hw),&(i_rqh->super))){
		return NyLPC_TBool_FALSE;
	}

	//Bodyの書込み
	NyLPC_cHttpBodyWriter_initialize(&(work.bw),i_st);

	//チャンク転送設定
	NyLPC_cHttpBodyWriter_setChunked(&(work.bw));

	//JSONを書く。
	if(!NyLPC_cHttpBodyWriter_format(&(work.bw),"{\"application\":\""APPLICATION_VERSION";%s\"}",_platform_name)){
		return NyLPC_TBool_FALSE;
	}
	//エラーチェック
	if(!NyLPC_cHttpBodyWriter_close(&(work.bw))){
		NyLPC_OnErrorGoto(Error_NyLPC_cHttpBodyWriter_close);
	}
	NyLPC_cHttpBodyWriter_finalize(&hw);

	return NyLPC_TBool_TRUE;
Error_NyLPC_cHttpBodyWriter_close:
	return NyLPC_TBool_FALSE;
}

/**
 * MimicVMの起動と,ResponseJSONの起動
 * @return
 * 持続性接続を継続するかの真偽値
 */
static NyLPC_TBool mvm(NyLPC_TcHttpStream_t* i_st,const struct TRemoteMcuRequest* i_rqh)
{
	//メモリ節約の為だよ
	union{
		NyLPC_TcHttpHeaderWriter_t hw;
		struct{
			NyLPC_TUInt32 vmret;
			NyLPC_TcMiMicVM_t vm;
			struct NyLPC_TRemoteMCU_EventHandler eh;
		}bw;
	}work;
	NyLPC_Assert(i_rqh->content.mvm.v==1);
	NyLPC_Assert(i_rqh->content.mvm.o==TRemoteMcuRequest_QVAL_O_JSON);

	//ヘッダを書く
	if(!writeJsonHeader(i_st,&(work.hw),&(i_rqh->super))){
		return NyLPC_TBool_FALSE;
	}

	//Bodyを書く
//ここでリクエストパーサのエラー原因を調べて、詳細JSONをかくのもあり
	//ハンドラインスタンスの設定
	work.bw.eh.super.get_stream=mvmgets;
	work.bw.eh.super.put_stream=mvmputs_json;
	work.bw.eh.super.native_call=nativeCall;
	work.bw.eh.super.sleep=mvmsleep;
	work.bw.eh.db_pos=0;
	work.bw.eh.st_len=0;
	work.bw.eh.st=i_st;
	work.bw.eh.req=i_rqh;

	//VM起動の為の排他ロック
	NyLPC_Assert(NyLPC_cMutex_lock(&_vm_mutex));

	//Bodyの書込み
	NyLPC_cHttpBodyWriter_initialize(&(work.bw.eh.bw),i_st);
	//起動VMの初期化
	NyLPC_cMiMicVM_initialize(&(work.bw.vm),(struct NyLPC_TcMiMicVM_TEvent*)&(work.bw.eh));

	//チャンク転送設定
	NyLPC_cHttpBodyWriter_setChunked(&(work.bw.eh.bw));

	//JSONを書く。
	if(!NyLPC_cHttpBodyWriter_format(&(work.bw.eh.bw),"{\"version\":\""MVM_VERSION"\",\"stream\":[")){
		NyLPC_OnErrorGoto(Error_NyLPC_cHttpBodyWriter_format);
	}

	//VMの実行
	work.bw.vmret=NyLPC_cMiMicVM_run(&(work.bw.vm),i_rqh->content.mvm.vm_instruction.bc_buf,i_rqh->content.mvm.vm_instruction.txt_len);
	if(!NyLPC_cHttpBodyWriter_format(&(work.bw.eh.bw),"],\"result\":%u}",work.bw.vmret)){
		NyLPC_OnErrorGoto(Error_NyLPC_cHttpBodyWriter_format);
	}
	//エラーチェック
	if(!NyLPC_cHttpBodyWriter_close(&(work.bw.eh.bw))){
		NyLPC_OnErrorGoto(Error_NyLPC_cHttpBodyWriter_close);
	}
	NyLPC_cMiMicVM_finalize(&vm);
	NyLPC_cHttpBodyWriter_finalize(&hw);

	//VM排他ロックの解除
	NyLPC_Assert(NyLPC_cMutex_unlock(&_vm_mutex));


	//持続性はフラグから判断する。
	return NyLPC_THttpBasicHeader_isPersistent(&(i_rqh->super));
Error_NyLPC_cHttpBodyWriter_format:
Error_NyLPC_cHttpBodyWriter_close:
	//VM排他ロックの解除
	NyLPC_Assert(NyLPC_cMutex_unlock(&_vm_mutex));
	NyLPC_cMiMicVM_finalize(&vm);
	NyLPC_cHttpBodyWriter_finalize(&hw);
	return NyLPC_TBool_FALSE;
}

/**
 * リクエストヘッダのパーサ
 */
static NyLPC_TUInt16 parseReqHeader(NyLPC_TcHttpStream_t* i_st,struct TRemoteMcuRequest* o_out)
{
	NyLPC_TUInt16 ret=200;
	TcRemoteMcuRequestParser_t parser;
	cRemoteMcuRequestParser_initialize(&parser);
	ret=cRemoteMcuRequestParser_parse(&parser,i_st,o_out);
	cRemoteMcuRequestParser_finalize(&parser);
	return ret;
}
/**
 * エラーレスポンスのライタ。
 * 戻り値はpersistentConnectionが有効かどうか。
 */
static NyLPC_TBool writeError(NyLPC_TcHttpStream_t* i_st,const struct NyLPC_THttpBasicHeader* i_rqh,NyLPC_TUInt16 i_status)
{
	static const char* HTML_FORMAT="<html><h1>STATUS %d</h1><hr/>"NyLPC_cHttpdConfig_SERVER"</html>";
	NyLPC_TcHttpHeaderWriter_t hw;
	NyLPC_TcHttpBodyWriter_t bw;
	//ヘッダライタの生成
	if(!NyLPC_cHttpHeaderWriter_initialize(&hw,i_st,i_rqh)){
		return NyLPC_TBool_FALSE;
	}
	//ヘッダ書込み
	if(!NyLPC_THttpBasicHeader_isPersistent(i_rqh)){
		NyLPC_cHttpHeaderWriter_setClose(&hw);
	}
	//@bug HTTP/1.1未満のクライアントを考慮していない。
	NyLPC_cHttpHeaderWriter_setChunked(&hw);
	//ヘッダの基本情報出力
	NyLPC_cHttpHeaderWriter_writeHeader(&hw,i_status);
	//拡張メッセージヘッダの出力
	NyLPC_cHttpHeaderWriter_writeMessage(&hw,"Content-type","text/html");

	//ヘッダ書込み終わり。(最後だけチェック)
	if(!NyLPC_cHttpHeaderWriter_close(&hw)){
		NyLPC_cHttpHeaderWriter_finalize(&hw);
		return NyLPC_TBool_FALSE;
	}
	NyLPC_cHttpHeaderWriter_finalize(&hw);

	//Bodyの書込み
	NyLPC_cHttpBodyWriter_initialize(&bw,i_st);
	//チャンク転送設定
	NyLPC_cHttpBodyWriter_setChunked(&bw);
	NyLPC_cHttpBodyWriter_format(&bw,HTML_FORMAT,(NyLPC_TInt32)i_status);
	//エラーチェック
	if(!NyLPC_cHttpBodyWriter_close(&bw)){
		NyLPC_cHttpBodyWriter_finalize(&hw);
		return NyLPC_TBool_FALSE;
	}
	NyLPC_cHttpBodyWriter_finalize(&hw);
	return NyLPC_THttpBasicHeader_isPersistent(i_rqh);
}


/**
 * ROMファイルのレスポンスライタ。
 * 戻り値はpersistentConnectionが有効かどうか。
 */
static NyLPC_TBool writeFile(NyLPC_TcHttpStream_t* i_st,const struct NyLPC_THttpBasicHeader* i_rqh,const struct NyLPC_TRomFileData* i_file)
{
	NyLPC_TcHttpHeaderWriter_t hw;
	NyLPC_TcHttpBodyWriter_t bw;
	//ヘッダライタの生成
	if(!NyLPC_cHttpHeaderWriter_initialize(&hw,i_st,i_rqh)){
		return NyLPC_TBool_FALSE;
	}
	//ヘッダ書込み
	if(!NyLPC_THttpBasicHeader_isPersistent(i_rqh)){
		NyLPC_cHttpHeaderWriter_setClose(&hw);
	}
	NyLPC_cHttpHeaderWriter_setContentLength(&hw,i_file->size);
	//ヘッダの基本情報出力
	NyLPC_cHttpHeaderWriter_writeHeader(&hw,200);
	//拡張メッセージヘッダの出力
	NyLPC_cHttpHeaderWriter_writeMessage(&hw,"Content-type",i_file->content_type);

	//ヘッダ書込み終わり。(最後だけチェック)
	if(!NyLPC_cHttpHeaderWriter_close(&hw)){
		NyLPC_cHttpHeaderWriter_finalize(&hw);
		return NyLPC_TBool_FALSE;
	}
	NyLPC_cHttpHeaderWriter_finalize(&hw);

	//Bodyの書込み
	NyLPC_cHttpBodyWriter_initialize(&bw,i_st);
	NyLPC_cHttpBodyWriter_write(&bw,i_file->data,i_file->size);
	//エラーチェック
	if(!NyLPC_cHttpBodyWriter_close(&bw)){
		NyLPC_cHttpBodyWriter_finalize(&hw);
		return NyLPC_TBool_FALSE;
	}
	NyLPC_cHttpBodyWriter_finalize(&hw);
	return NyLPC_THttpBasicHeader_isPersistent(i_rqh);
}


