
#include "LoginManager.h"
#include "EventSystem/Command/CsdkCommand.h"
#include "EventSystem/Notify/CsdkNotify.h"
#include "State/AuthState.h"
#include "State/LinkState.h"
#include "State/UserLoginState.h"
#include "State/OfflineState.h"
#include "State/OnlineState.h"
#include "Tools/Parser/TransData.h"
#include "Log/LogChatSdk.h"
#include "Log/CsdkLogMacros.h"
#include "State/AuthCodeLoginState.h"
#include "Login/Data/CsdkLoginStatus.h"
#include "WebSocketConnection.h"
#include "Common/CsdkErrorCode.h"

void FLoginManager::Reset()
{
	SwitchState(ELoginStateId::PreLogin);
	bHasLogin = false;
	LoginCallback.Unbind();
	Token.Empty();
	if(WebSocketConnection)
	{
		WebSocketConnection->Close();
		WebSocketConnection = nullptr;
	}
	FCsdkCommonTools::GetFriendManager().Reset();
	FCsdkCommonTools::GetBlockManager().Reset();
	FCsdkCommonTools::GetUserManager().Reset();
	FCsdkCommonTools::GetSessionManager().Reset();
	FCsdkCommonTools::GetMessageManager().Reset();
	FCsdkCommonTools::GetChatRoomManager().Reset();
	StateMap.Empty();
}

void FLoginManager::PrepareLogin()
{
	Reset();
	WebSocketConnection = MakeShareable( new FWebSocketConnection);
	StateMap.Add(ELoginStateId::Link,MakeShareable(new FLinkState{*this,WebSocketConnection}));
	StateMap.Add(ELoginStateId::Auth,MakeShareable(new FAuthState{*this, WebSocketConnection}));
	StateMap.Add(ELoginStateId::Online,MakeShareable(new FOnlineState(*this, WebSocketConnection)));
	StateMap.Add(ELoginStateId::Offline,MakeShareable(new FOfflineState(*this, WebSocketConnection)));
}

void FLoginManager::Login(const FCsdkUser& User, const FLoginCallback& InCallBack)
{
	PrepareLogin();
	LoginCallback = InCallBack;
	StateMap.Add(ELoginStateId::UserLogin,MakeShareable(new FUserLoginState{*this,User}));
	SwitchState(ELoginStateId::UserLogin);
}

void FLoginManager::Login(const FString& InAuthCode, const FLoginCallback& InCallBack)
{
	PrepareLogin();
	LoginCallback = InCallBack;
	StateMap.Add(ELoginStateId::AuthCode,MakeShareable(new FAuthCodeLoginState{*this,InAuthCode}));
	SwitchState(ELoginStateId::AuthCode);
}

void FLoginManager::Logout()
{
	Reset();
}


void FLoginManager::SwitchState(const ELoginStateId NewStateId)
{
	if(NewStateId == CurrentState)
	{
		return;
	}
	CSDK_LOG(LogChatSdk,Log,TEXT("Switch State from %s to %s"),*StaticEnum<ELoginStateId>()->GetNameStringByIndex((int32)CurrentState),*StaticEnum<ELoginStateId>()->GetNameStringByIndex((int32)NewStateId));
	if(StateMap.Contains(CurrentState))
	{
		StateMap[CurrentState]->Leave();
	}
	CurrentState = NewStateId;
	if(StateMap.Contains(CurrentState))
	{
		StateMap[CurrentState]->Enter();
	}
}

void FLoginManager::OnRawMsgRecvEvent(const TArray<uint8>& RecvData) const
{
	if(!FCsdkCommonTools::IsAlive())
	{
		//SDK不再存活
		return;
	}
	//每次收到消息均通知online系统重置心跳
	const FTransData TransData = FTransData::Deserialize(RecvData);
	switch (TransData.GetOpCode())
	{
	case ServerEOpCode::OpAuthReply:
		{
			StateMap[ELoginStateId::Auth]->OnWebSocketNotify(TransData.GetBody());
			break;
		}
	case ServerEOpCode::OpHeartbeatReply:
#if !UE_BUILD_SHIPPING
		//CSDK_LOG(LogChatSdk,Log,TEXT("OnHeartBeat Recieve"));
#endif
		break;
	case ServerEOpCode::OpSysNotify:
		{
			FCsdkNotify::SysNotify(TransData.GetBody());
		}
		break;
	case ServerEOpCode::OpSysCommand:
		{
			FCsdkCommand::SysCommand(TransData.GetBody());
		}
		break;
	case ServerEOpCode::OpMsgPackage:
		{
			FCsdkCommonTools::GetMessageManager().MsgNotify(TransData.GetBody());
		}
	default:
		break;
	}
	if(StateMap.Contains(ELoginStateId::Online))
	{
		StateMap[ELoginStateId::Online]->OnWebSocketNotify(TransData.GetBody());
	}
}

void FLoginManager::OnWebSocketClosed(int64 StatusCode, const FString& Reason, bool bWasClean)
{
	if(bHasLogin)
	{
		SwitchState(ELoginStateId::Offline);
	}
}

void FLoginManager::OnConnect()
{
	CSDK_LOG(LogChatSdk,Log,TEXT("Connect websocket Success"));
	SwitchState(ELoginStateId::Auth);
}

void FLoginManager::OnConnectionError(const FString& Error)
{
	CSDK_LOG(LogChatSdk,Log,TEXT("Connect websocket Failed! with Error: %s"),*Error);
	if(!bHasLogin)//登录阶段才调用回调，断线重连啥都不干
	{
		FLoginCallback Callback = MoveTemp(LoginCallback);
		LoginCallback.Unbind();      // 先解绑防重入，与 StateBase.cpp:25 保持一致
		Callback.ExecuteIfBound(ECsdkErrorCode::UnReachable, Error);
	}
}




