#include "StdAfx.h"
#include "nlib_include.h"



typedef enum {

	NicoMyList_Root = 0,
	NicoMyList_mylist,
	NicoMyList_mylistitem,
	NicoMyList_item,
	NicoMyList_item_data,

}NicoMyListHierarchy;

typedef enum {
	NicoMyListGroup_Root = 0,
	NicoMyListGroup_mylistgrouphead,
	NicoMyListGroup_mylistgroup,
	NicoMyLIstGroup_mylist


}NicoMyListGroupHierarchy;

typedef struct{
	UINT64 nord;
	NLIB_RESULT errcode;
	LPVOID option;
	

}NMContainer,*NMContainer_P;

typedef struct {
	NMContainer container;
	NicoMyListCallBack callBack;
	NicoMyListItem_P pitem;
}NicoMyListItemContainer,*NicoMyListItemContainer_P;


typedef struct {
	NMContainer container;
	NicoMyListGroupCallBack callBack;
	NicoMyList_P pMyList;

}NicoMyListContainer,*NicoMyListContainer_P;


///
///マイリスト一覧取得共通処理
///
static NLIB_RESULT NicoMyList_getMyListInternal(NicoVideoAuth_P pAuth,LPCTSTR objectName,NicoMyListCallBack callback,LPVOID option);

static NLIB_RESULT NicoMyList_common(NicoVideoAuth_P pAuth,LPCTSTR objectName,JSONStructEventHandler structHandler,JSONDataEventHandler dataHandler,JSONStructEndEventHandler endHandler,LPVOID item);

static NicoMyListItem_P NicoMyListItem_new();

static VOID NicoMyListItem_delete(NicoMyListItem_P *pitem);

static NicoMyList_P NicoMyList_new();

static VOID NicoMyList_delete(NicoMyList_P *pMyList);

static JSONVoid myListStructEventHandler(JSONVoid *option,JSONUInt thisHierarchy,JSONUInt valueType,JSONChar_P name);

static JSONVoid myListDataEventHandler(JSONVoid *option,JSONUInt thisHierarchy,JSONUInt valueType,JSONChar_P name,JSONParameter param);

static JSONVoid myListStructEndEventHandler(JSONVoid *option,JSONUInt thisHierarchy,JSONUInt valueType,JSONChar_P param);

static JSONVoid myListItemStructEventHandler(JSONVoid *option,JSONUInt thisHierarchy,JSONUInt valueType,JSONChar_P name);

static JSONVoid myListItemDataEventHandler(JSONVoid *option,JSONUInt thisHierarchy,JSONUInt valueType,JSONChar_P name,JSONParameter param);

static JSONVoid myListItemDataEndEventHandler(JSONVoid *option,JSONUInt thisHierarchy,JSONUInt valueType,JSONChar_P param);



static NLIB_RESULT jsonStrCpy(LPTSTR buf,LPCTSTR in,SIZE_T size);




//
//マクロ
//
#define jsonToArray(arr,in) jsonStrCpy((arr),(in),sizeof(arr) / sizeof(arr[0]))

#define jsonDataDecimal(nmc,dt)	if((valueType) == JSONTypeDecimal && wcscmp((name),(nmc)) == 0){(dt) = wcstoul((LPCTSTR)param,NULL,10);goto jsondataend;}
#define jsonDataString(nmc,dt,err)	if((valueType) == JSONTypeString && wcscmp((name),(nmc)) == 0){(err) = jsonToArray((dt),(LPCTSTR)param);goto jsondataend;}


///
///とりあえずマイリスト一覧取得
///
NLIB_RESULT NicoMyList_getDefList(NicoVideoAuth_P pAuth,NicoMyListCallBack callback,LPVOID option){

	
	NLIB_RESULT rslt = NLIB_ERR_CODE_NOTSET;
	
	rslt = NicoMyList_getMyListInternal(pAuth,L"api/deflist/list",callback,option);

	
	return rslt;
	
}

NLIB_DECLSPEC NLIB_RESULT NicoMyList_getMyList(NicoVideoAuth_P pAuth,LPCTSTR id,NicoMyListCallBack callback,LPVOID option){

	TCHAR objectName[LENGTH_512];
	NLIB_RESULT rslt = NLIB_ERR_CODE_NOTSET;
	if(wcslen(id) > ARRAY_LENGTH(objectName) / 2){
		return NLIB_ERR_BUFFER_TOOLITTLE;
	}

	wsprintf(objectName,L"api/mylist/list?group_id=%s",id);
	rslt = NicoMyList_getMyListInternal(pAuth,objectName,callback,option);

	return rslt;
}


static NLIB_RESULT NicoMyList_getMyListInternal(NicoVideoAuth_P pAuth,LPCTSTR objectName,NicoMyListCallBack callback,LPVOID option){
	NLIB_RESULT rslt = NLIB_ERR_CODE_NOTSET;
	NicoMyListItemContainer itemContainer;
	ZeroMemory(&itemContainer,sizeof(itemContainer));
	itemContainer.callBack = callback;
	itemContainer.container.option = option;
	itemContainer.pitem =  NicoMyListItem_new();
	rslt = NicoMyList_common(pAuth,objectName,myListItemStructEventHandler,myListItemDataEventHandler,myListItemDataEndEventHandler,&itemContainer);
	NicoMyListItem_delete(&itemContainer.pitem);

	return rslt;
}

NLIB_DECLSPEC NLIB_RESULT NicoMyList_getMyListGroup(NicoVideoAuth_P pAuth,NicoMyListGroupCallBack callback,LPVOID option){

	NLIB_RESULT rslt = NLIB_ERR_CODE_NOTSET;
	NicoMyListContainer listContainer;
	ZeroMemory(&listContainer,sizeof(listContainer));
	listContainer.callBack = callback;
	listContainer.container.option = option;
	listContainer.pMyList = NicoMyList_new();
	
	rslt = NicoMyList_common(pAuth,L"api/mylistgroup/list",myListStructEventHandler,myListDataEventHandler,myListStructEndEventHandler,&listContainer);
	NicoMyList_delete(&listContainer.pMyList);
	return rslt;
}
NLIB_DECLSPEC NLIB_RESULT NicoMyList_addItem(LPCTSTR video_id){

	NLIB_RESULT rslt = NLIB_ERR_CODE_NOTSET;

	TCHAR addApi[LENGTH_512] = {L"http://www.nicovideo.jp/mylist_add/video/"};
	if(wcslen(video_id) > ARRAY_LENGTH(addApi) /2){

		goto buffererr;
	}
	wcscat(addApi,video_id);
	
	if(ShellExecute(NULL,L"open",addApi,NULL,NULL,SW_SHOWDEFAULT) <= (HINSTANCE)32){
		goto shellerr;
	}
	
	rslt = NLIB_ERR_OK;

	
end:
	return rslt;

shellerr:
	rslt = NLIB_ERR_SHELL_FAILED;
	goto end;

buffererr:
	rslt = NLIB_ERR_BUFFER_TOOLITTLE;
	goto end;

}

//
//プライベート関数
//

static NLIB_RESULT NicoMyList_common(NicoVideoAuth_P pAuth,LPCTSTR objectName,JSONStructEventHandler structHandler,JSONDataEventHandler dataHandler,JSONStructEndEventHandler endHandler,LPVOID item){

	//変数宣言、初期化
	NLIB_RESULT rslt = NLIB_ERR_CODE_NOTSET;
	//HINTERNET hNicoVideoSession = NULL;
	HINTERNET hrequest = NULL;
	TCHAR sendBuffer[LENGTH_512] = {L"Cookie: "};
	JSONParser_P jsonParser = JSONParser_new();
	

	const UINT_PTR readBufSize = LENGTH_65536 * 30;

	LPSTR readBuffer = (LPSTR)malloc(readBufSize);

	DWORD readSize;

	DWORD statusCode;
	DWORD statusCodeSize = sizeof(statusCode);

	

	


	
	
	wcscat(sendBuffer,pAuth->userSession);
	//hNicoVideoSession = WinHttpConnect(hHttpSession,WWW_NICO_VIDEO_DOMEINNAME,INTERNET_DEFAULT_HTTP_PORT,0);

	hrequest = WinHttpOpenRequest(hNicoVideoConnect,L"GET",objectName,L"1.1",(LPCTSTR)WINHTTP_NO_REFERER,(LPCTSTR*)WINHTTP_DEFAULT_ACCEPT_TYPES,0);

	if(hrequest == NULL){
		goto networkerr;
	}



	if(WinHttpSendRequest(hrequest,sendBuffer,-1,WINHTTP_NO_REQUEST_DATA,0,0,0) == FALSE){

		goto networkerr;

	}




	

	if(WinHttpReceiveResponse(hrequest,NULL) == FALSE){

		goto networkerr;

	}


	if (WinHttpQueryHeaders(hrequest,WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER,WINHTTP_HEADER_NAME_BY_INDEX,&statusCode,&statusCodeSize,WINHTTP_NO_HEADER_INDEX) == FALSE){
		goto networkerr;
	}

	if (HTTP_STATUS_OK != statusCode){
		goto networkerr;
	}

	if(ReadHttpBody(hrequest,(LPBYTE)readBuffer,readBufSize,&readSize) == FALSE){
		goto networkerr;

	}

	readBuffer[readSize] = '\0';


	
	{
		JSONResult jsonResult;


		
		
		JSONParser_setEventHandler(jsonParser,structHandler,dataHandler,endHandler);
		jsonResult = JSONParser_Parse(jsonParser,readBuffer,JSON_ENCODE_UTF8,item);



		if(jsonResult != JSON_ERR_OK){

			goto jsonerr;
		}
	}

	rslt = NLIB_ERR_OK;

end:
	free(readBuffer);
	JSONParser_delete(&jsonParser);
	WinHttpCloseHandle(hrequest);
	

	return rslt;

networkerr:
	rslt = NLIB_ERR_FAILED_NETWORK;
	goto end;
jsonerr:
	rslt = NLIB_ERR_JSON_PARSE;
	goto end;

}




static NicoMyListItem_P NicoMyListItem_new(){

	NicoMyListItem_P self = (NicoMyListItem_P)calloc(1,sizeof(*self));

	return self;
}

static VOID NicoMyListItem_delete(NicoMyListItem_P *pitem){

	free(*pitem);
	(*pitem) = NULL;

	return;
}

static NicoMyList_P NicoMyList_new(){

	return (NicoMyList_P)calloc(1,sizeof(NicoMyList));
}

static VOID NicoMyList_delete(NicoMyList_P *pMyList){

	free(*pMyList);
	(*pMyList) = NULL;
	return;

}


static JSONVoid myListItemStructEventHandler(JSONVoid *option,JSONUInt thisHierarchy,JSONUInt valueType,JSONChar_P name){

	NicoMyListHierarchy nord = NicoMyList_Root;

	NicoMyListItemContainer_P self = (NicoMyListItemContainer_P)option;

	switch(self->container.nord){

	case MAKENORD(NicoMyList_Root):

		if(thisHierarchy == JSONTypeRoot && valueType == JSONTypeObject){

			nord = NicoMyList_mylist;

		}
		break;

	case MAKENORD1(NicoMyList_Root,NicoMyList_mylist):

		if(thisHierarchy == JSONTypeObject && valueType == JSONTypeArray && wcscmp(name,L"mylistitem") == 0){

			nord = NicoMyList_mylistitem;

		}

		break;

	case MAKENORD2(NicoMyList_Root,NicoMyList_mylist,NicoMyList_mylistitem):

		if(thisHierarchy == JSONTypeArray && valueType == JSONTypeObject){

			nord = NicoMyList_item;

		}

		break;

	case MAKENORD3(NicoMyList_Root,NicoMyList_mylist,NicoMyList_mylistitem,NicoMyList_item):

		if(thisHierarchy == JSONTypeObject && valueType == JSONTypeObject && wcscmp(name,L"item_data") == 0){

			nord = NicoMyList_item_data;

		}

		break;

	}

	self->container.nord = MAKENORD1(self->container.nord,nord);
	

	return;
}


static JSONVoid myListItemDataEventHandler(JSONVoid *option,JSONUInt thisHierarchy,JSONUInt valueType,JSONChar_P name,JSONParameter param){

	NicoMyListItemContainer_P self = (NicoMyListItemContainer_P)option;
	
	if(thisHierarchy == JSONTypeObject){
		switch(self->container.nord){

		case MAKENORD3(NicoMyList_Root,NicoMyList_mylist,NicoMyList_mylistitem,NicoMyList_item):

			if(valueType == JSONTypeDecimal && wcscmp(name,L"create_time") == 0){

				self->pitem->create_time = wcstoul((LPCTSTR)param,NULL,10);

			} else if(valueType == JSONTypeString && wcscmp(name,L"description") == 0){

				self->container.errcode = jsonToArray(self->pitem->description,(LPCTSTR)param);

			} else if(valueType == JSONTypeString && wcscmp(name,L"item_id") == 0){

				self->container.errcode = jsonToArray(self->pitem->item_id,(LPCTSTR)param);

			} else if(valueType == JSONTypeString && wcscmp(name,L"item_type") == 0){

				self->container.errcode = jsonToArray(self->pitem->item_type,(LPCTSTR)param);

			} else if(valueType == JSONTypeDecimal && wcscmp(name,L"update_time") == 0){

				self->pitem->update_time = wcstoul((LPCTSTR)param,NULL,10);

			} else if(valueType == JSONTypeDecimal && wcscmp(name,L"watch") == 0){

				self->pitem->watch = wcstoul((LPCTSTR)param,NULL,10);
			}

			break;

		case MAKENORD4(NicoMyList_Root,NicoMyList_mylist,NicoMyList_mylistitem,NicoMyList_item,NicoMyList_item_data):

			if(valueType == JSONTypeString && wcscmp(name,L"deleted") == 0){

				self->container.errcode = jsonToArray(self->pitem->itemData.deleted,(LPCTSTR)param);

			} else if(valueType == JSONTypeDecimal && wcscmp(name,L"first_retrieve") == 0){

				self->pitem->itemData.first_retrieve = wcstoul((LPCTSTR)param,NULL,10);

			} else if(valueType == JSONTypeString && wcscmp(name,L"group_type") == 0){

				self->container.errcode = jsonToArray(self->pitem->itemData.group_type,(LPCTSTR)param);

			} else if(valueType == JSONTypeString && wcscmp(name,L"last_res_body") == 0){

				self->container.errcode = jsonToArray(self->pitem->itemData.last_res_body,(LPCTSTR)param);

			} else if(valueType == JSONTypeString && wcscmp(name,L"length_seconds") == 0){

				self->container.errcode = jsonToArray(self->pitem->itemData.length_seconds,(LPCTSTR)param);

			} else if(valueType == JSONTypeString && wcscmp(name,L"mylist_counter") == 0){

				self->container.errcode = jsonToArray(self->pitem->itemData.mylist_counter,(LPCTSTR)param);

			} else if(valueType == JSONTypeString && wcscmp(name,L"num_res") == 0){

				self->container.errcode = jsonToArray(self->pitem->itemData.num_res,(LPCTSTR)param);

			} else if(valueType == JSONTypeString && wcscmp(name,L"thumbnail_url") == 0){

				self->container.errcode = jsonToArray(self->pitem->itemData.thumbnail_url,(LPCTSTR)param);

			} else if(valueType == JSONTypeString && wcscmp(name,L"title") == 0){

				self->container.errcode = jsonToArray(self->pitem->itemData.title,(LPCTSTR)param);

			} else if(valueType == JSONTypeDecimal && wcscmp(name,L"update_time") == 0){

				self->pitem->itemData.update_time = wcstoul((LPCTSTR)param,NULL,10);

			} else if(valueType == JSONTypeString && wcscmp(name,L"video_id") == 0){

				self->container.errcode = jsonToArray(self->pitem->itemData.video_id,(LPCTSTR)param);

			} else if(valueType == JSONTypeString && wcscmp(name,L"view_counter") == 0){

				self->container.errcode = jsonToArray(self->pitem->itemData.view_counter,(LPCTSTR)param);

			} else if(valueType == JSONTypeString && wcscmp(name,L"watch_id") == 0){

				self->container.errcode = jsonToArray(self->pitem->itemData.watch_id,(LPCTSTR)param);

			}

			break;
		}
	}
	return;
}

static JSONVoid myListItemDataEndEventHandler(JSONVoid *option,JSONUInt thisHierarchy,JSONUInt valueType,JSONChar_P name){
	NicoMyListItemContainer_P self = (NicoMyListItemContainer_P)option;
	self->container.nord = UPNORD(self->container.nord);
	if(MAKENORD2(NicoMyList_Root,NicoMyList_mylist,NicoMyList_mylistitem) == self->container.nord){

		self->callBack(self->container.option,self->pitem);
		ZeroMemory(self->pitem,sizeof(*self->pitem));

	}
	return;
}

static JSONVoid myListStructEventHandler(JSONVoid *option,JSONUInt thisHierarchy,JSONUInt valueType,JSONChar_P name){

	NicoMyListContainer_P nicoMyListContainer = (NicoMyListContainer_P)option;
	NicoMyListGroupHierarchy nord;

	switch(nicoMyListContainer->container.nord){

	case MAKENORD(NicoMyListGroup_Root):

		nord = NicoMyListGroup_mylistgrouphead;

		break;


	case MAKENORD1(NicoMyListGroup_Root,NicoMyListGroup_mylistgrouphead):

		nord = NicoMyListGroup_mylistgroup;

		break;

	case MAKENORD2(NicoMyListGroup_Root,NicoMyListGroup_mylistgrouphead,NicoMyListGroup_mylistgroup):

		nord = NicoMyLIstGroup_mylist;

		break;
	}
	nicoMyListContainer->container.nord = MAKENORD1(nicoMyListContainer->container.nord,nord);

	return;
}

static JSONVoid myListDataEventHandler(JSONVoid *option,JSONUInt thisHierarchy,JSONUInt valueType,JSONChar_P name,JSONParameter param){

	NicoMyListContainer_P self = (NicoMyListContainer_P)option;

	switch(self->container.nord){

	case MAKENORD3(NicoMyListGroup_Root,NicoMyListGroup_mylistgrouphead,NicoMyListGroup_mylistgroup,NicoMyLIstGroup_mylist):

		//面倒になったのでマクロを使って値を読み取る

		jsonDataDecimal(L"create_time",self->pMyList->create_time);

		jsonDataString(L"default_sort",self->pMyList->default_sort,self->container.errcode);

		jsonDataString(L"description",self->pMyList->description,self->container.errcode);

		jsonDataString(L"icon_id",self->pMyList->icon_id,self->container.errcode);

		jsonDataString(L"id",self->pMyList->id,self->container.errcode);

		jsonDataString(L"name",self->pMyList->name,self->container.errcode);

		jsonDataString(L"public",self->pMyList->publicType,self->container.errcode);

		jsonDataString(L"sort_order",self->pMyList->sort_order,self->container.errcode);

		jsonDataDecimal(L"update_time",self->pMyList->update_time);

		jsonDataString(L"user_id",self->pMyList->user_id,self->container.errcode);


jsondataend:


		break;

	}

	return;
}

static JSONVoid myListStructEndEventHandler(JSONVoid *option,JSONUInt thisHierarchy,JSONUInt valueType,JSONChar_P param){

	NicoMyListContainer_P self = (NicoMyListContainer_P)option;

	self->container.nord = UPNORD(self->container.nord);

	if(self->container.nord == MAKENORD2(NicoMyListGroup_Root,NicoMyListGroup_mylistgrouphead,NicoMyListGroup_mylistgroup)){

		self->callBack(option,self->pMyList);
		ZeroMemory(self->pMyList,sizeof(*self->pMyList));

	}

	return;
}

static NLIB_RESULT jsonStrCpy(LPTSTR buf,LPCTSTR in,SIZE_T size){

	NLIB_RESULT rslt = NLIB_ERR_OK;

	if(wcslen(in) < size){

		wcscpy(buf,in);

	}else {

		rslt = NLIB_ERR_BUFFER_TOOLITTLE;

	}

	return rslt;
}





