// Fill out your copyright notice in the Description page of Project Settings.


#include "CsdkMessageRingBuffer.h"
#include "Log/CsdkLogMacros.h"
#include "Message/Tool/MessageTool.h"
#include "Message/Tool/PullMsgAutoDeleteTask.h"
#include "Session/Tool/SessionTool.h"
#include "Tools/CsdkCommonTools.h"
#include "Tools/CsdkCommonHttp.h"


constexpr int32 MsgContainerSize =256;
constexpr int32 MsgContainerRealSize =MsgContainerSize +1 ;

using namespace heroim::client::v3beta::dto::message::model;
using namespace heroim::client::v3beta::dto::message;

FCsdkMessageRingBuffer::FCsdkMessageRingBuffer(const FCsdkSessionId& InSessionId)
{
	SessionId = InSessionId;
	CachedMessages.SetNum(MsgContainerRealSize);
}


void FCsdkMessageRingBuffer::AddReferencedObjects(FReferenceCollector& Collector)
{
	Collector.AddReferencedObjects(CachedMessages);
}

bool FCsdkMessageRingBuffer::TryPushFront(UCsdkMessageBase* Msg)
{
	if(IsFull())//队列满时，头部不允许插入
	{
		return false;
	}
	if(IsEmpty() || At(0)->Sequence == Msg->Sequence +1)
	{
		const int32 NewHead = (Head-1+MsgContainerRealSize) % MsgContainerRealSize;
		MsgId2ArrayIndex.Add(Msg->MsgId,NewHead);
		CachedMessages[NewHead] = Msg;
		Head = NewHead;
		return true;
	}
	return false;
}

void FCsdkMessageRingBuffer::PushBack(UCsdkMessageBase* Msg)
{
	const int32 NewTail = (Tail+1) % MsgContainerRealSize;
	//循环数组满了
	if(NewTail == Head)
	{
		//移除缓存在NewTail的MsgId
		MsgId2ArrayIndex.Remove(CachedMessages[NewTail]->MsgId);
		CachedMessages[Tail] = Msg;
		//新增缓存在Tail的MsgId
		MsgId2ArrayIndex.Add(Msg->MsgId,Tail);
		Tail = NewTail;
		Head =(++Head) % MsgContainerRealSize;
	}
	else//循环数组没满
	{
		CachedMessages[Tail] = Msg;
		MsgId2ArrayIndex.Add(Msg->MsgId,Tail);
		Tail = NewTail;
	}
	if(IsFull() &&!bRemoteHeadHasMore) //压入后缓存已满，说明服务器还有数据
	{
		bRemoteHeadHasMore = true;
	}
	
}


UCsdkMessageBase* FCsdkMessageRingBuffer::operator[](int32 Index) const
{
	checkf(Index <Size(),TEXT("Index must less than size"))
	return At(Index);
}

inline UCsdkMessageBase* FCsdkMessageRingBuffer::At(int32 Index) const
{
	checkf(Index <Size(),TEXT("Index must less than size"))
	return CachedMessages[(Head+Index)%MsgContainerRealSize];
}



TArray<UCsdkMessageBase*> FCsdkMessageRingBuffer::GetFilteredMsgs( const FString& AnchorMsgId, const int32 DesiredPullCount,
	const ECsdkMsgPullDirection Direction) const
{
	TArray<UCsdkMessageBase*> Result;
  	const int32 AnchorIndex = GetIndexByAnchorId(AnchorMsgId);
	if(AnchorIndex >= 0) //说明缓存有命中
	{
		if(Direction == ECsdkMsgPullDirection::Forward) //老消息
		{
			for (int32 i = AnchorIndex - 1, Count = 0; i >= 0 /*缓存还有数据*/ && Count < DesiredPullCount /*未拉取足够数量*/; i--)
			{
				if (SessionId.Type == ECsdkSessionType::PrivateChat) //私聊不屏蔽消息
				{
					Result.Add(At(i));
					Count++;
				}
				else
				{
					if (!FCsdkCommonTools::GetBlockManager().IsUserIdBlocked(At(i)->Sender.Id)) //未被拉黑
					{
						Result.Add(At(i));
						Count++;
					}
				}
			}
			for (int32 i = 0; i < Result.Num() / 2; i++)
			{
				Result.Swap(i, Result.Num() - 1 - i);
			}
		}
		else //新消息
		{
			for (int32 i = AnchorIndex + 1, Count = 0; Count < DesiredPullCount && i < Size(); ++i)
			{
				if (SessionId.Type == ECsdkSessionType::PrivateChat) //私聊不屏蔽消息
				{
					Result.Add(At(i));
					Count++;
				}
				else
				{
					if (!FCsdkCommonTools::GetBlockManager().IsUserIdBlocked(At(i)->Sender.Id)) //未被拉黑
					{
						Result.Add(At(i));
						Count++;
					}
				}
			}
		}
	}
	return Result;
}

int32 FCsdkMessageRingBuffer::Capacity()
{
	return MsgContainerSize;
}

int32 FCsdkMessageRingBuffer::Size() const
{
	return (Tail-Head+MsgContainerRealSize) % MsgContainerRealSize;
}

bool FCsdkMessageRingBuffer::IsFull() const
{
	return Size() == Capacity();
}

bool FCsdkMessageRingBuffer::IsEmpty() const
{
	return Size() ==0 ;
}

int32 FCsdkMessageRingBuffer::GetIndexByAnchorId(const FString& AnchorMsgId) const
{
	if(AnchorMsgId.IsEmpty())
	{
		return Size();
	}
	
	if(const auto IndexPtr = MsgId2ArrayIndex.Find(AnchorMsgId))
	{
		return (*IndexPtr-Head+MsgContainerRealSize) % MsgContainerRealSize;
	}
	
	return -1;
}

bool FCsdkMessageRingBuffer::Contains(const FString& MsgId) const
{
	return MsgId2ArrayIndex.Contains(MsgId);
}

void FCsdkMessageRingBuffer::Empty()
{
	MsgId2ArrayIndex.Empty();
	CachedMessages.Empty();
	CachedMessages.SetNum(MsgContainerRealSize);
	Head = 0;
	Tail = 0;
}
