void VRemoteDebugPilot::RemoveContext(std::map< OpaqueDebuggerContext, VContextDescriptor* >::iterator& inCtxIt)
{
	XBOX::VError			err;
	XBOX::VString			resp;

	if (fState != STOPPED_STATE)
	{
		// ctx could already have been removed by ABORT
		if (inCtxIt != fContextArray.end())
		{
			if ((*inCtxIt).second->fPage)
			{
				ReleaseRefCountable(&(*inCtxIt).second->fPage);
			}

			if (fState == CONNECTED_STATE)
			{
				resp = CVSTR("{\"method\":\"removeContext\",\"contextId\":\"");
				resp.AppendLong8((sLONG8)((*inCtxIt).first));
				resp += CVSTR("\",\"id\":\"");
				resp += fClientId;
				resp += CVSTR("\"}");
				err = SendToBrowser( resp );
				if (err)
				{
					fWS->Close();
					fState = DISCONNECTING_STATE;
				}
			}
			inCtxIt->second->Release();
			fContextArray.erase(inCtxIt);
		}
	}
}
LRESULT CWebRTCAPI::OnMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) {
	if (uMsg == MainWnd::WindowMessages::UI_THREAD_CALLBACK)	{
		//Find out which conductor this was sent from
		ConductorCallback* cb = reinterpret_cast<ConductorCallback*>(lParam);

		PeerConnectionWrapper* pcw = nullptr;
		for (auto c : connections) {
			if (c->GetEasyRtcId() == cb->easyRtcId_){
				pcw = c;
				break;
			}
		}
		if (pcw != nullptr) {
			pcw->UIThreadCallback(static_cast<int>(wParam), cb->data_);
		}
		else if (cb->easyRtcId_ == "local") {
			// special case - if we get a frame message for "local", we should just send it up to the browser.
			auto msg = reinterpret_cast<std::string*>(cb->data_);
			SendToBrowser(*msg);
			delete msg;
		}
		else {
			LOG(INFO) << " Could not find PeerConnectionWrapper for given id:" << cb->easyRtcId_;
		}
	}
	return S_OK;
}
XBOX::VError VRemoteDebugPilot::SendContextToBrowser(
												std::map< OpaqueDebuggerContext,VContextDescriptor* >::iterator& inCtxIt)
{
	XBOX::VString				resp;
	XBOX::VString				brkptsStr;

	VChromeDebugHandler::GetJSONBreakpoints(brkptsStr);

	resp = CVSTR("{\"method\":\"updateContext\",\"contextId\":\"");
	resp.AppendLong8((sLONG8)(*inCtxIt).first);
	resp += CVSTR("\",\"debugLineNb\":");
	resp.AppendLong((*inCtxIt).second->fDebugInfos.fLineNb);
	resp += CVSTR(",\"debugFileName\":\"");
	resp += (*inCtxIt).second->fDebugInfos.fFileName;
	resp += CVSTR("\",\"debugReason\":\"");
	resp += (*inCtxIt).second->fDebugInfos.fReason;
	resp += CVSTR("\",\"debugComment\":\"");
	resp += (*inCtxIt).second->fDebugInfos.fComment;
	resp += CVSTR("\",\"sourceLine\":\"");
	resp += (*inCtxIt).second->fDebugInfos.fSourceLine;
	resp += CVSTR("\",\"id\":\"");
	resp += fClientId;
	resp += "\",\"breakpoints\":";
	resp += brkptsStr;
	resp += CVSTR("}");
	return SendToBrowser( resp );
}
void CWebRTCAPI::SendSelfie(){
	std::stringstream selfieStream;
	std::string* base64bitmap = mainWindow->GetSelfie();

	// Optimized json construction for frame
	if (base64bitmap && *base64bitmap != "") {
		selfieStream << "{\"pluginMessage\":{\"data\":\"data:image/jpg;base64," << *base64bitmap << "\", \"message\":\"gotSelfie\"}}";
		SendToBrowser(selfieStream.str());
		delete base64bitmap;
	}
}
void VRemoteDebugPilot::TreatNewContextMsg(RemoteDebugPilotMsg_t& ioMsg)
{
	XBOX::VError			err;
	XBOX::VString			resp;

	if (fState != STOPPED_STATE)
	{
		if ( testAssert(fContextArray.size() < K_NB_MAX_CTXS) )
		{

			VContextDescriptor*		newCtx = new VContextDescriptor();
			*(ioMsg.outctx) = (OpaqueDebuggerContext)sRemoteDebugPilotContextId;
			sRemoteDebugPilotContextId++;
			std::pair< OpaqueDebuggerContext, VContextDescriptor* >	l_pair( *(ioMsg.outctx), newCtx);
			std::pair< std::map< OpaqueDebuggerContext, VContextDescriptor* >::iterator, bool >	newElt =
					fContextArray.insert( l_pair );

			if (testAssert(newElt.second))
			{
				if (fState == CONNECTED_STATE)
				{
					resp = CVSTR("{\"method\":\"newContext\",\"contextId\":\"");
					resp.AppendLong8((sLONG8)(*(ioMsg.outctx)));
					resp += CVSTR("\",\"id\":\"");
					resp += fClientId;
					resp += CVSTR("\"}");

					err = SendToBrowser( resp );
					if (err)
					{
						fWS->Close();
						fState = DISCONNECTING_STATE;
					}
				}
				else
				{
					err = VE_OK;
				}
				if (!err)
				{
					fPipeStatus = VE_OK;
				}
			}
		}
	}
	fPipeOutSem.Unlock();
}
void CWebRTCAPI::SendWindowHandle(HWND wnd) {
	LOG(INFO) << __FUNCTION__;
	ASSERT(wnd != NULL);
	uint32_t wndPtr = reinterpret_cast<uint32_t>(wnd);

	Json::StyledWriter writer;
	Json::Value json;
	Json::Value pluginMessage;

	pluginMessage["data"] = wndPtr;
	pluginMessage["message"] = "gotWindowHandle";
	json["pluginMessage"] = pluginMessage;

	//std::string* str = new std::string(writer.write(json));

	SendToBrowser(writer.write(json) /* *str */);
}
void VRemoteDebugPilot::TreatDeactivateContextMsg(RemoteDebugPilotMsg_t& ioMsg)
{
	std::map< OpaqueDebuggerContext, VContextDescriptor* >::iterator ctxIt = fContextArray.find(ioMsg.ctx);
	XBOX::VError			err;
	XBOX::VString			resp;

	if (fState != STOPPED_STATE)
	{
		if (ctxIt != fContextArray.end())
		{
			if (!ioMsg.hideOnly)
			{
				VChromeDbgHdlPage*	page = (*ctxIt).second->fPage;
				if (page)
				{
					page->Stop();
					ReleaseRefCountable(&(*ctxIt).second->fPage);
				}
			}
			fPipeStatus = VE_OK;

			if ( (fState == CONNECTED_STATE) && ((*ctxIt).second->fUpdated) )
			{
				resp = CVSTR("{\"method\":\"hideContext\",\"contextId\":\"");
				resp.AppendLong8((sLONG8)((*ctxIt).first));
				resp += CVSTR("\",\"id\":\"");
				resp += fClientId;
				resp += CVSTR("\"}");
				err = SendToBrowser( resp );
				if (err)
				{
					fWS->Close();
					fState = DISCONNECTING_STATE;
				}
			}
			(*ctxIt).second->fUpdated = false;
		}
	}
	fPipeOutSem.Unlock();
}
void VRemoteDebugPilot::HandleConnectedState()
{
	XBOX::VError			err;
	VSize					msgLen;
	bool					isMsgTerminated;
	sBYTE*					tmpStr;
	sBYTE*					endStr;
	unsigned long long		id;
	XBOX::VString			resp;

	msgLen = K_MAX_SIZE;
	err = fWS->ReadMessage((void*)fTmpData,msgLen,isMsgTerminated);
	if (err)
	{
		DebugMsg("VRemoteDebugPilot::HandleConnectedState fWS->ReadMessage pb\n");
		fWS->Close();
		fState = DISCONNECTING_STATE;
		return;
	}
	if (msgLen)
	{
		if (msgLen < K_MAX_SIZE)
		{
			fTmpData[msgLen] = 0;
		}
		else
		{
			fTmpData[K_MAX_SIZE-1] = 0;
		}
		DebugMsg("VRemoteDebugPilot::HandleConnectedState received msg(len=%d)='%s'\n",msgLen,fTmpData);

		{
			tmpStr = strstr(fTmpData,K_DBG_PROTOCOL_ABORT_STR);
			if (tmpStr && fClientId.GetLength())
			{
				tmpStr += strlen(K_DBG_PROTOCOL_ABORT_STR);
				endStr = strchr(tmpStr,'"');
				*endStr = 0;
				id = atoi(tmpStr);
				std::map< OpaqueDebuggerContext, VContextDescriptor* >::iterator	ctxIt = fContextArray.find((OpaqueDebuggerContext)id);
				if ( testAssert(ctxIt != fContextArray.end()) )
				{
					if (!(*ctxIt).second->fPage)
					{
						if ((*ctxIt).second->fSem)
						{
							(*ctxIt).second->fSem->Unlock();
							ReleaseRefCountable(&(*ctxIt).second->fSem);
						}
						RemoveContext(ctxIt);
					}
					else
					{
						VChromeDbgHdlPage*	l_page = (*ctxIt).second->fPage;
						testAssert(l_page->Abort() == VE_OK);
					}
					//RemoveContext(id);
				}
			}
			else
			{
				tmpStr = strstr(fTmpData,K_DBG_PROTOCOL_GET_URL_STR);
				if (tmpStr && fClientId.GetLength())
				{
					tmpStr += strlen(K_DBG_PROTOCOL_GET_URL_STR);
					endStr = strchr(tmpStr,'"');
					*endStr = 0;
					id = atoi(tmpStr);
					std::map< OpaqueDebuggerContext, VContextDescriptor* >::iterator	ctxIt = fContextArray.find((OpaqueDebuggerContext)id);
					if ( testAssert(ctxIt != fContextArray.end()) )
					{
						// test if a a page has already been attributed to this context
						if (testAssert(!(*ctxIt).second->fPage))
						{
							xbox_assert((*ctxIt).second->fTraceContainer);
							(*ctxIt).second->fPage = new VChromeDbgHdlPage((*ctxIt).second->fTraceContainer);
							(*ctxIt).second->fPage->Init(id,fHTTPServer,NULL);
							err = (*ctxIt).second->fPage->Start((OpaqueDebuggerContext)id);
							/*sPages[l_i].value.fFifo.Reset();
							sPages[l_i].value.fOutFifo.Reset();*/
						}
						if (!err)
						{
							resp = CVSTR("{\"method\":\"setURLContext\",\"contextId\":\"");
							resp.AppendLong8(id);
							resp += CVSTR("\",\"url\":\"");
							resp.AppendLong8((*ctxIt).second->fPage->GetPageNb());
							resp += CVSTR("\",\"id\":\"");
							resp += fClientId;
							resp += CVSTR("\"}");
							err = SendToBrowser( resp );
						}

						if (err)
						{
							fWS->Close();
							fState = DISCONNECTING_STATE;
							return;
						}
					}
					else
					{
						DebugMsg("VRemoteDebugPilot::HandleConnectedState unknown context = %d\n",id);
					}
				}
				else
				{
					DebugMsg("VRemoteDebugPilot::HandleConnectedState received msg2(len=%d)='%s'\n",msgLen,fTmpData);
				}
			}
		}
	}
}
void VRemoteDebugPilot::HandleConnectingState()
{
	XBOX::VError			err;
	VSize					msgLen;
	bool					isMsgTerminated;
	sBYTE*					tmpStr;
	sBYTE*					endStr;
	unsigned long long		id;
	XBOX::VString			resp;

	msgLen = K_MAX_SIZE;
	err = fWS->ReadMessage((void*)fTmpData,msgLen,isMsgTerminated);
	if (err)
	{
		DebugMsg("VRemoteDebugPilot::HandleConnectingState fWS->ReadMessage pb\n");
	}

	if (!err && msgLen)
	{
		if (msgLen < K_MAX_SIZE)
		{
			fTmpData[msgLen] = 0;
		}
		else
		{
			fTmpData[K_MAX_SIZE-1] = 0;
		}
		DebugMsg("VRemoteDebugPilot::HandleConnectingState received msg(len=%d)='%s'\n",msgLen,fTmpData);
		tmpStr = (sBYTE*)strstr((const char*)fTmpData,K_DBG_PROTOCOL_CONNECT_STR);
		if (tmpStr && !fClientId.GetLength())
		{
			tmpStr += strlen(K_DBG_PROTOCOL_CONNECT_STR);
			endStr = (sBYTE*)strchr((const char*)tmpStr,'"');
			if (endStr)
			{
				*endStr = 0;
				resp = CVSTR("{\"result\":\"ok\",\"solution\":\"");
				resp += fSolutionName;
				resp += ("\",\"needsAuthentication\":");
				resp += ( fNeedsAuthentication ? CVSTR("true") : CVSTR("false") );
				resp += (",\"id\":\"");
				fClientId = VString(tmpStr);
				resp += fClientId;
				resp += CVSTR("\"}");
				err = SendToBrowser( resp );
				if (!err)
				{
					if (fNeedsAuthentication)
					{
						fState = DISCONNECTING_STATE;
						fDisconnectTime.FromSystemTime();
						return;
					}
					else
					{
						//DisplayContexts();

						// here the client connection has been accepted without need of authentication
						// we send the already existing contexts
						std::map< OpaqueDebuggerContext, VContextDescriptor* >::iterator	ctxIt = fContextArray.begin();
						while( !err && (ctxIt != fContextArray.end()) )
						{
							resp = CVSTR("{\"method\":\"newContext\",\"contextId\":\"");
							resp.AppendLong8((sLONG8)((*ctxIt).first));
							resp += CVSTR("\",\"id\":\"");
							resp += fClientId;
							resp += CVSTR("\"}");
							err = SendToBrowser( resp );

							if ( !err && (*ctxIt).second->fUpdated )
							{
								if ((*ctxIt).second->fSem == NULL)
								{
									(*ctxIt).second->fSem = new XBOX::VSemaphore(0);
									(*ctxIt).second->fSem->Retain();
								}
								err = SendContextToBrowser(ctxIt);
							}
							ctxIt++;
						}
						if (!err)
						{
							fState = CONNECTED_STATE;
						}
					}
				}
			}
			else
			{
				err = VE_INVALID_PARAMETER;
			}
		}
		else
		{
			DebugMsg("VRemoteDebugPilot::HandleConnectingState first message shoud be 'connect'\n");
			err = VE_INVALID_PARAMETER;
		}
	}

	if (err)
	{
		DebugMsg("VRemoteDebugPilot::HandleConnectingState fWS->ReadMessage pb\n");
		fWS->Close();
		fState = DISCONNECTING_STATE;
	}
}