#include "stdafx.h"
#include "nlite_include.h"
using namespace std::tr1;
namespace nlite{




//リスナー検索オブジェクト
struct ListenerPre{

	ListenerData &user;

	ListenerPre(ListenerData &in_user):
		user(in_user)
	{}

	bool operator()(CListenerList::reference listenerData){

		
		return (user.user_id == listenerData.user_id)/* && (user.community == listenerData.community)*/;
	}

};


//
//リスナーデータ
/////////////////////////////////////////////////////////////////////////////////////////////////


ListenerData::ListenerData():
user_id(TEXT("")),
name(TEXT("")),
community(TEXT("")),
bgcolor(INIT_COLOR),
time(0),
onlyCommunityFlag(FALSE),
nameColor(INIT_COLOR)
{}

VOID ListenerData::SetNameColor(COLORREF nameColor_in){
	
	nameColor = nameColor_in;

	return;
}

VOID ListenerData::SetBkColor(COLORREF bkColor_in){
	bgcolor = bkColor_in;

	return;
}

VOID ListenerData::SetUserName(LPCTSTR name){

	this->name = name;


}


CChatData::ViewData::ViewData():
			sellLineColor(INIT_COLOR),
			stringColor(INIT_COLOR),
			backColor(INIT_COLOR),
		  selectFlag(FALSE),
		  height(0)
		{}

VOID CChatData::SetChat(NicoLiveChat &chat_in,ListenerData &listener){
	
	
	this->anonymity = _ttol(chat_in.anonymity);
	this->premium = _ttol(chat_in.premium);
	this->chatBuf = chat_in.chatBuf;
	this->no = _ttol(chat_in.no);

	if(_tcscmp(chat_in.locale,TEXT("jp")) == 0){
		this->locale = cuntry::jp;
	} else {
		this->locale = cuntry::unknown;
	}
	this->mail.reserve(chat_in.mailCount);
	for(UINT_PTR index = 0;index < chat_in.mailCount ;index++){

		this->mail.push_back(chat_in.mail[index]);
	}

	this->name = chat_in.name;


	
	this->listenerData = &listenerList.Register(listener,FALSE);
	tstring target(chat_in.chatBuf);
	
	match_results<tstring::iterator> results;
	if(regex_match(target.begin(),target.end(),results,nliteregex::NAMESERTCHREGEX) == TRUE){
		
		const tstring &name = results.str(results.size() - 1).c_str();
		
		if(nliteProperty.gp.numberNameFlag == TRUE || _tcstol(name.c_str(),NULL,10) == 0){
			this->listenerData->SetUserName(name.c_str());
		}
		
	}
	
	this->vpos = _ttol(chat_in.vpos);
	this->thread = _ttol(chat_in.thread);
	this->date = _ttol(chat_in.date);
	


	return;
}


//
//
//コメントリストクラス
//////////////////////////////////////////////////////////////////////////

//選択行取得用ファンクショナル
struct GetCurSelFunc_t{

	INT_PTR count;
	BOOL findFlag;
	GetCurSelFunc_t():
		count(0)
		{}

	BOOL operator() (const CChatData &target){
		count++;
		return  target.viewData.selectFlag;
	}

};

//挿入位置検索ファンクショナル
struct SeartchSetPos_t{
	time_t time;

	SeartchSetPos_t(time_t in_time):time(in_time){}

	bool operator()(const CChatData &target){

		return time > target.date;
	}
};

CCommentList::CCommentList(){}

CCommentList::~CCommentList(){}

CCommentList::iterator CCommentList::GetCurSel(){
	auto end = this->end();
	auto ite = this->begin();

	for(;ite != end && ite->viewData.selectFlag == FALSE;++ite);

	return ite ;
}


INT_PTR CCommentList::GetCurSelNo(){

	auto end = this->end();
	auto ite = this->begin();
	INT_PTR no = 0;
	for(;ite != end && ite->viewData.selectFlag == FALSE;++ite,++no);

	return ite != end ? no : -1;

}


CCommentList::iterator CCommentList::OnChatReceve(NicoLiveChat_P chatData_in,LPCTSTR communityID, UINT_PTR commnetCountSum,CCommentListWindow &listWindow){

	CChatData chatData;
	ListenerData listenerData;
	listenerData.user_id = chatData_in->user_id;
	listenerData.time = time(NULL);
	listenerData.community = communityID;
	chatData.SetChat(*chatData_in,listenerData);
	iterator rslt;
	if(empty() == TRUE || back().date <= chatData.date){
		chatList.push_back(chatData);
		rslt = (--chatList.end());
		if(nlite::nliteProperty.crp.commentReadFlag == TRUE){
			
			g_speaker.SynthesisAsync((const speaker::TChar_P)(LPCTSTR)rslt->chatBuf);
		}
	}else {

		rslt = chatList.insert(std::find_if(chatList.begin(),chatList.end(),SeartchSetPos_t(chatData.date)),chatData);
	}
	
	if(shortcut ==chatList.end())shortcut = chatList.begin();

	return rslt;
}

CCommentList::iterator CCommentList::GetCurSelItem(){
	auto rslt = std::find_if(chatList.begin(),chatList.end(),GetCurSelFunc_t());

	return rslt;
}


VOID CCommentList::SetCurSel(CCommentList::iterator cursel){
	
	auto end = this->end();
	if(cursel == end)goto end;
	{
		auto curselItem = this->GetCurSelItem();
	
		if(curselItem != end){
			curselItem->viewData.selectFlag = FALSE;
		}
	
		
		cursel->viewData.selectFlag = TRUE;
	}
end:

	return;
}


CCommentList::reference CCommentList::GetChatAt(UINT_PTR no){

	iterator data = chatList.begin();
	std::advance(data,no);
	return *data;
}


UINT_PTR CCommentList::Size(){

	return chatList.size();
}

BOOL CCommentList::empty(){

	return chatList.empty();
}



CCommentList::iterator CCommentList::begin(){

	return chatList.begin();
}

CCommentList::iterator CCommentList::end(){
	
	return chatList.end();
}


CCommentList::reverse_iterator CCommentList::rbegein(){

	
	return chatList.rbegin();
}



		
CCommentList::reverse_iterator CCommentList::rend(){

	return chatList.rend();
}

VOID CCommentList::OnDestroy(){
	
	chatList.clear();
	
}

VOID CCommentList::OnConnect(){
	OnDestroy();
	shortcut = chatList.begin();
}

CCommentList::reference CCommentList::back(){

	return chatList.back();
}

///
///ショートカットイテレータを取得
///
CCommentList::iterator CCommentList::GetShortCut(){

	return shortcut;
}
		
///
///ショートカットを設定
///
BOOL CCommentList::SetShortCut(UINT_PTR no){


	if(static_cast<INT_PTR>(chatList.size()) <= no)return FALSE;

	shortcut = chatList.begin();
	std::advance(shortcut,no);

	return TRUE;

}



VOID CListenerList::BkColorCollect(CListenerColorCollector &target){

	iterator end = m_listenerList.end();
	std::for_each(m_listenerList.begin(), end,target);
}

CListenerList::reference CListenerList::Register(ListenerData &listener,BOOL bUpdate){

	//関数オブジェクト生成
	ListenerPre listenerPre(listener);


	iterator begin = m_listenerList.begin();
	iterator end = m_listenerList.end();

	//登録予定のリスナーデータがすでに登録されているか検索
	iterator rslt = std::find_if(begin,end,listenerPre);


	//リスナーデータが登録されていない場合
	if(rslt == end){

		//リスナーデータを登録
		m_listenerList.push_back(listener);
		return m_listenerList.back();



		//リスナーデータが登録されている場合で、更新フラグがtrueの場合
	} else if(bUpdate == TRUE){

		//リスナーデータを更新
		*rslt = listener;

		
	}
	
	return *rslt;

}

BOOL CListenerList::ReadProperty(LPCTSTR fileName){
	BOOL rslt = FALSE;
	CAtlFile	userSettingFile;
	ULONGLONG userSettingFileSize;
	
	
	if(SUCCEEDED(userSettingFile.Create(fileName,GENERIC_READ,FILE_SHARE_READ,OPEN_ALWAYS)) == FALSE || SUCCEEDED(userSettingFile.GetSize(userSettingFileSize)) == FALSE){

		throw Exception(TEXT("リスナー設定ファイルが開けませんでした。このエラーが続くようであれば、一度設定ファイルを削除して再度試してみてください"),__LINE__,TEXT(__FILE__),TEXT(__FUNCTION__));
	}
	std::vector<char> userSettingBuf(static_cast<UINT_PTR>(userSettingFileSize / sizeof(char) + (1 * sizeof(char))));
	userSettingFile.Read(&userSettingBuf[0],userSettingBuf.size());
	LPSTR userSettingStart = strstr(&userSettingBuf[0],"<");
	if(userSettingStart != NULL){
		ReadUserSettingXML rusx(nlite::listenerList);
		rusx.Parse(userSettingStart);
	}


	return rslt;
}
#define NLITE_ATTRIBUTE_WRITE_STR(s,n)	s->WriteAttributeString(NULL, _tcsstr(TEXT(#n),TEXT("."))  != NULL ? _tcsstr(TEXT(#n),TEXT(".")) + 1 : TEXT(#n), NULL, n)

#define NLITE_ATTRIBUTE_WRITE_INT(s,n)	\
	{\
	TCHAR buffer[sizeof(n) * 8];\
	_stprintf(buffer,TEXT("%d"),static_cast<INT>(n));\
	s->WriteAttributeString(NULL, _tcsstr(TEXT(#n),TEXT(".")) + 1, NULL,buffer );\
}
BOOL CListenerList::WriteProperty(LPCTSTR fileName){
	BOOL rslt = FALSE;


	CComPtr<IXmlWriter> pWriter;
	CreateXmlWriter(__uuidof(IXmlWriter), reinterpret_cast<void**>(&pWriter), 0);
	CComPtr<IStream> pStream;
	SHCreateStreamOnFile(fileName, STGM_CREATE | STGM_WRITE, &pStream);

	pWriter->SetOutput(pStream);

	// インデント有効化
	pWriter->SetProperty(XmlWriterProperty_Indent, TRUE);

	// <?xml version="1.0" encoding="UTF-8"?>
	pWriter->WriteStartDocument(XmlStandalone_Omit);

	pWriter->WriteStartElement(NULL,TEXT("listenerData"),NULL);


	auto begin = m_listenerList.begin();
	auto end = m_listenerList.end();

	//リスナーリストから保存対象のデータのみxml形式で保存
	for(;begin != end;++begin){
		if(begin->name.Length() > 0 || begin->nameColor != ListenerData::INIT_COLOR || begin->bgcolor != ListenerData::INIT_COLOR){
			pWriter->WriteStartElement(NULL,TEXT("user"),NULL);
			if(begin->name.Length() > 0) NLITE_ATTRIBUTE_WRITE_STR(pWriter,(*begin).name);
			if(begin->community.Length()> 0) NLITE_ATTRIBUTE_WRITE_STR(pWriter,(*begin).community);
			NLITE_ATTRIBUTE_WRITE_INT(pWriter,(*begin).time);
			if(begin->bgcolor != ListenerData::INIT_COLOR)NLITE_ATTRIBUTE_WRITE_INT(pWriter,(*begin).bgcolor);
			if(begin->nameColor != ListenerData::INIT_COLOR)NLITE_ATTRIBUTE_WRITE_INT(pWriter,(*begin).nameColor);
			pWriter->WriteString(begin->user_id);
			pWriter->WriteEndElement();
		}
	}
	pWriter->WriteEndDocument();
	rslt = TRUE;
	return rslt;
}


ReadUserSettingXML::ReadUserSettingXML(CListenerList &in_listenerList):m_listenerList(in_listenerList),userNodeFlag(FALSE){}

#define NLITE_READ_USER_STRING(at,l,t) if(_tcscmp(at[0],TEXT(#t)) == 0)l.t = at[1]
#define NLITE_READ_USER_INT(at,l,t)		if(_tcscmp(at[0],TEXT(#t))==0)l.t = _tcstol((const wchar_t*)at[1],NULL,10)

	void ReadUserSettingXML::OnStartElement (const XML_Char *pszName, const XML_Char **papszAttrs){

		if(_tcscmp(pszName,TEXT("user")) == 0){

			this->userNodeFlag = TRUE;


			for(;papszAttrs[0] != NULL;papszAttrs += 2){

				NLITE_READ_USER_STRING(papszAttrs,listenerBuff,name);
				NLITE_READ_USER_STRING(papszAttrs,listenerBuff,community);
				NLITE_READ_USER_INT(papszAttrs,listenerBuff,bgcolor);
				NLITE_READ_USER_INT(papszAttrs,listenerBuff,nameColor);
				NLITE_READ_USER_INT(papszAttrs,listenerBuff,time);
				
			}

		}

		return;
	}

	void ReadUserSettingXML::OnEndElement (const XML_Char *pszName){

		if(_tcscmp(pszName,TEXT("user")) == 0){

			listenerList.Register(listenerBuff,TRUE);
			this->userNodeFlag = FALSE;
			this->listenerBuff.~ListenerData();
			new(&listenerBuff) ListenerData();

		}

		return;
	}

	void ReadUserSettingXML::OnCharacterData (const XML_Char *pszData, int nLength){

		if(this->userNodeFlag == TRUE){

			
			if(_tcstol(listenerBuff.user_id,NULL,10) != 0 || ((difftime(time(NULL),604800)) <listenerBuff.time)){
				listenerBuff.user_id.Append(pszData,nLength);
			}



		}

		

		return;
	}

}