#include "FramebufferManager.h"

#include "VideoConfig.h"
#include "Renderer.h"

#include "Main.h"

FramebufferManagerBase::VirtualXFBListType FramebufferManagerBase::m_virtualXFBList; // Only used in Virtual XFB mode

const XFBSourceBase* FramebufferManagerBase::m_overlappingXFBArray[];

FramebufferManagerBase::~FramebufferManagerBase()
{
	VirtualXFBListType::iterator
		it = m_virtualXFBList.begin(),
		vlend = m_virtualXFBList.end();
	for (; it != vlend; ++it)
		delete it->xfbSource;
}

const XFBSourceBase** FramebufferManagerBase::GetXFBSource(u32 xfbAddr, u32 fbWidth, u32 fbHeight, u32 &xfbCount)
{
	//if (g_ActiveConfig.bUseRealXFB)
	//	return getRealXFBSource(xfbAddr, fbWidth, fbHeight, xfbCount);
	//else
		return getVirtualXFBSource(xfbAddr, fbWidth, fbHeight, xfbCount);
}

inline bool addrRangesOverlap(u32 aLower, u32 aUpper, u32 bLower, u32 bUpper)
{
	return !((aLower >= bUpper) || (bLower >= aUpper));
}

void FramebufferManagerBase::CopyToXFB(u32 xfbAddr, u32 fbWidth, u32 fbHeight, const EFBRectangle& sourceRc)
{
	//if (g_ActiveConfig.bUseRealXFB)
	//	copyToRealXFB(xfbAddr, fbWidth, fbHeight, sourceRc);
	//else
		copyToVirtualXFB(xfbAddr, fbWidth, fbHeight, sourceRc);
}

void FramebufferManagerBase::replaceVirtualXFB()
{
	VirtualXFBListType::iterator
		it = m_virtualXFBList.begin(),
		vlend = m_virtualXFBList.end();

	VirtualXFB *vxfb = &*it;

	const s32 srcLower = vxfb->xfbAddr;
	const s32 srcUpper = vxfb->xfbAddr + 2 * vxfb->xfbWidth * vxfb->xfbHeight;
	const s32 lineSize = 2 * vxfb->xfbWidth;

	while (++it != vlend)
	{
		vxfb = &*it;

		const s32 dstLower = vxfb->xfbAddr;
		const s32 dstUpper = vxfb->xfbAddr + 2 * vxfb->xfbWidth * vxfb->xfbHeight;

		if (dstLower >= srcLower && dstUpper <= srcUpper)
		{
			// invalidate the data
			vxfb->xfbAddr = 0;
			vxfb->xfbHeight = 0;
			vxfb->xfbWidth = 0;
		}
		else if (addrRangesOverlap(srcLower, srcUpper, dstLower, dstUpper))
		{		
			const s32 upperOverlap = (srcUpper - dstLower) / lineSize;
			const s32 lowerOverlap = (dstUpper - srcLower) / lineSize;

			if (upperOverlap > 0 && lowerOverlap < 0)
			{
				vxfb->xfbAddr += lineSize * upperOverlap;
				vxfb->xfbHeight -= upperOverlap;
			}
			else if (lowerOverlap > 0)
			{
				vxfb->xfbHeight -= lowerOverlap;
			}
		}
	}
}

FramebufferManagerBase::VirtualXFBListType::iterator
FramebufferManagerBase::findVirtualXFB(u32 xfbAddr, u32 width, u32 height)
{
	const u32 srcLower = xfbAddr;
	const u32 srcUpper = xfbAddr + 2 * width * height;

	VirtualXFBListType::iterator
		it = m_virtualXFBList.begin(),
		vlend = m_virtualXFBList.end();
	for (; it != vlend; ++it)
	{
		const VirtualXFB &vxfb = *it;

		const u32 dstLower = vxfb.xfbAddr;
		const u32 dstUpper = vxfb.xfbAddr + 2 * vxfb.xfbWidth * vxfb.xfbHeight;

		if (dstLower >= srcLower && dstUpper <= srcUpper)
			return it;
	}

	// That address is not in the Virtual XFB list.
	return m_virtualXFBList.end();
}

void FramebufferManagerBase::copyToVirtualXFB(u32 xfbAddr, u32 fbWidth, u32 fbHeight, const EFBRectangle& sourceRc)
{
	const VirtualXFBListType::iterator it = findVirtualXFB(xfbAddr, fbWidth, fbHeight);
	
	VirtualXFB *vxfb = NULL;

	if (m_virtualXFBList.end() == it)
	{
		if (m_virtualXFBList.size() >= MAX_VIRTUAL_XFB)
		{
			PanicAlert("Requested creating a new virtual XFB although the maximum number has already been reached! Report this to the devs");
			return;
			// TODO, possible alternative to failing: just delete the oldest virtual XFB:
			// delete m_virtualXFBList.back().xfbSource;
			// m_virtualXFBList.pop_back();
		}
		else
		{
			// create a new Virtual XFB and place it at the front of the list
			VirtualXFB v;
			m_virtualXFBList.push_front(v);
			vxfb = &m_virtualXFBList.front();
		}
	}
	else
	{
		vxfb = &*it;
		delete vxfb->xfbSource;
	}

	vxfb->xfbAddr = xfbAddr;
	vxfb->xfbWidth = fbWidth;
	vxfb->xfbHeight = fbHeight;

	const float scaleX = RendererBase::GetXFBScaleX();
	const float scaleY = RendererBase::GetXFBScaleY();

	TargetRectangle targetSource;
	targetSource.top = (int)(sourceRc.top * scaleY);
	targetSource.bottom = (int)(sourceRc.bottom * scaleY);
	targetSource.left = (int)(sourceRc.left * scaleX);
	targetSource.right = (int)(sourceRc.right * scaleX);

	const unsigned int target_width = targetSource.right - targetSource.left;
	const unsigned int target_height = targetSource.bottom - targetSource.top;

	vxfb->xfbSource = g_framebuffer_manager->CreateXFBSource(target_width, target_height);
	if (NULL == vxfb->xfbSource)
	{
		PanicAlert("Failed to create virtual XFB");
		return;
	}

	// why do both of these have a height/width/addr ?
	vxfb->xfbSource->srcAddr = xfbAddr;
	vxfb->xfbSource->srcWidth = fbWidth;
	vxfb->xfbSource->srcHeight = fbHeight;

	vxfb->xfbSource->texWidth = target_width;
	vxfb->xfbSource->texHeight = target_height;

	if (m_virtualXFBList.end() != it)
	{
		// move this Virtual XFB to the front of the list.
		m_virtualXFBList.splice(m_virtualXFBList.begin(), m_virtualXFBList, it);

		// keep stale XFB data from being used
		replaceVirtualXFB();
	}
  
	g_renderer->ResetAPIState(); // reset any game specific settings

	vxfb->xfbSource->CopyEFB(RendererBase::ConvertEFBRectangle(sourceRc));

	g_renderer->RestoreAPIState();
}

const XFBSourceBase** FramebufferManagerBase::getVirtualXFBSource(u32 xfbAddr, u32 fbWidth, u32 fbHeight, u32 &xfbCount)
{
	xfbCount = 0;

	if (0 == m_virtualXFBList.size())  // no Virtual XFBs available
		return NULL;

	u32 srcLower = xfbAddr;
	u32 srcUpper = xfbAddr + 2 * fbWidth * fbHeight;

	VirtualXFBListType::reverse_iterator
		it = m_virtualXFBList.rbegin(),
		vlend = m_virtualXFBList.rend();
	for (; it != vlend; ++it)
	{
		VirtualXFB* vxfb = &*it;

		u32 dstLower = vxfb->xfbAddr;
		u32 dstUpper = vxfb->xfbAddr + 2 * vxfb->xfbWidth * vxfb->xfbHeight;

		if (addrRangesOverlap(srcLower, srcUpper, dstLower, dstUpper))
		{
			m_overlappingXFBArray[xfbCount] = vxfb->xfbSource;
			xfbCount++;
		}
	}

	return &m_overlappingXFBArray[0];
}