// Copyright 2016 The SwiftShader Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "Direct3DSwapChain8.hpp"

#include "Direct3DDevice8.hpp"
#include "Config.hpp"
#include "Configurator.hpp"
#include "Debug.hpp"

#include "FrameBufferDD.hpp"
#include "FrameBufferGDI.hpp"

namespace D3D8
{
	Direct3DSwapChain8::Direct3DSwapChain8(Direct3DDevice8 *device, D3DPRESENT_PARAMETERS *presentParameters) : device(device), presentParameters(*presentParameters)
	{
		frameBuffer = 0;

		for(int i = 0; i < 3; i++)
		{
			backBuffer[i] = 0;
		}

		reset(presentParameters);
	}

	Direct3DSwapChain8::~Direct3DSwapChain8()
	{
		release();
	}

	long Direct3DSwapChain8::QueryInterface(const IID &iid, void **object)
	{
		TRACE("");

		if(iid == IID_IDirect3DSwapChain8 ||
		   iid == IID_IUnknown)
		{
			AddRef();
			*object = this;

			return S_OK;
		}

		*object = 0;

		return NOINTERFACE(iid);
	}

	unsigned long Direct3DSwapChain8::AddRef()
	{
		TRACE("");

		return Unknown::AddRef();
	}

	unsigned long Direct3DSwapChain8::Release()
	{
		TRACE("");

		return Unknown::Release();
	}

	long Direct3DSwapChain8::Present(const RECT *sourceRect, const RECT *destRect, HWND destWindowOverride, const RGNDATA *dirtyRegion)
	{
		TRACE("");

		#if PERF_PROFILE
			profiler.nextFrame();
		#endif

		void *source = backBuffer[0]->lockInternal(0, 0, 0, sw::LOCK_READONLY, sw::PUBLIC);   // FIXME: External
		sw::Format format = backBuffer[0]->getInternalFormat();
		int stride = backBuffer[0]->getInternalPitchB();

		if(!sourceRect && !destRect)   // FIXME: More cases?
		{
			frameBuffer->flip(destWindowOverride, source, format, stride);
		}
		else   // TODO: Check for SWAPEFFECT_COPY
		{
			sw::Rect sRect(0, 0, 0, 0);
			sw::Rect dRect(0, 0, 0, 0);

			if(sourceRect)
			{
				sRect.x0 = sourceRect->left;
				sRect.y0 = sourceRect->top;
				sRect.x1 = sourceRect->right;
				sRect.y1 = sourceRect->bottom;
			}

			if(destRect)
			{
				dRect.x0 = destRect->left;
				dRect.y0 = destRect->top;
				dRect.x1 = destRect->right;
				dRect.y1 = destRect->bottom;
			}

			frameBuffer->blit(destWindowOverride, source, sourceRect ? &sRect : nullptr, destRect ? &dRect : nullptr, format, stride);
		}

		backBuffer[0]->unlockInternal();   // FIXME: External

		return D3D_OK;
	}

	long Direct3DSwapChain8::GetBackBuffer(unsigned int index, D3DBACKBUFFER_TYPE type, IDirect3DSurface8 **backBuffer)
	{
		TRACE("");

		if(!backBuffer/* || type != D3DBACKBUFFER_TYPE_MONO*/)
		{
			return INVALIDCALL();
		}

		if(index >= 3 || this->backBuffer[index] == 0)
		{
			return INVALIDCALL();
		}

		this->backBuffer[index]->AddRef();
		*backBuffer = this->backBuffer[index]; 

		return D3D_OK;
	}

	void Direct3DSwapChain8::reset(D3DPRESENT_PARAMETERS *presentParameters)
	{
		release();

		this->presentParameters = *presentParameters;

		ASSERT(presentParameters->BackBufferCount <= 3);   // Maximum of three back buffers

		if(presentParameters->BackBufferCount == 0)
		{
			presentParameters->BackBufferCount = 1;
		}

		D3DDEVICE_CREATION_PARAMETERS creationParameters;
		device->GetCreationParameters(&creationParameters);

		HWND windowHandle = presentParameters->hDeviceWindow ? presentParameters->hDeviceWindow : creationParameters.hFocusWindow;
			
		int width = 0;
		int height = 0;

		if(presentParameters->Windowed && (presentParameters->BackBufferHeight == 0 || presentParameters->BackBufferWidth == 0))
		{
			RECT rectangle;
			GetClientRect(windowHandle, &rectangle);

			width = rectangle.right - rectangle.left;
			height = rectangle.bottom - rectangle.top;
		}
		else
		{
			width = presentParameters->BackBufferWidth;
			height = presentParameters->BackBufferHeight;
		}

		frameBuffer = createFrameBufferWin(windowHandle, presentParameters->BackBufferWidth, presentParameters->BackBufferHeight, presentParameters->Windowed == FALSE, true);

		lockable = presentParameters->Flags & D3DPRESENTFLAG_LOCKABLE_BACKBUFFER;

		backBuffer[0] = 0;
		backBuffer[1] = 0;
		backBuffer[2] = 0;

		for(int i = 0; i < (int)presentParameters->BackBufferCount; i++)
		{
			backBuffer[i] = new Direct3DSurface8(device, this, width, height, presentParameters->BackBufferFormat, D3DPOOL_DEFAULT, presentParameters->MultiSampleType, lockable, D3DUSAGE_RENDERTARGET);
			backBuffer[i]->bind();
		}
	}

	void Direct3DSwapChain8::release()
	{
		delete frameBuffer;
		frameBuffer = 0;

		for(int i = 0; i < 3; i++)
		{
			if(backBuffer[i])
			{
				backBuffer[i]->unbind();
				backBuffer[i] = 0;
			}
		}
	}

	void Direct3DSwapChain8::screenshot(void *destBuffer)
	{
		frameBuffer->screenshot(destBuffer);
	}

	void Direct3DSwapChain8::setGammaRamp(sw::GammaRamp *gammaRamp, bool calibrate)
	{
		frameBuffer->setGammaRamp(gammaRamp, calibrate);
	}

	void Direct3DSwapChain8::getGammaRamp(sw::GammaRamp *gammaRamp)
	{
		frameBuffer->getGammaRamp(gammaRamp);
	}

	void *Direct3DSwapChain8::lockBackBuffer(int index)
	{
		return backBuffer[index]->lockInternal(0, 0, 0, sw::LOCK_READWRITE, sw::PUBLIC);   // FIXME: External
	}
	
	void Direct3DSwapChain8::unlockBackBuffer(int index)
	{
		backBuffer[index]->unlockInternal();   // FIXME: External
	}
}
