/*  ZZ Open GL graphics plugin
 *  Copyright (c)2009-2010 zeydlitz@gmail.com, arcum42@gmail.com
 *  Based on Zerofrog's ZeroGS KOSMOS (c)2005-2008
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 */

// Realisation of RenderCRTC function ONLY.
// It draw picture direct on screen, so here we have interlacing and frame skipping.

//------------------ Includes
 #include "Util.h"
#include "ZZoglCRTC.h"
#include "GLWin.h"
#include "ZZoglShaders.h"
#include "ZZoglShoots.h"
#include "ZZoglDrawing.h"
#include "rasterfont.h" // simple font
#include <math.h>
#include "ZZoglVB.h"

//------------------ Defines
#if !defined(ZEROGS_DEVBUILD)
#define g_bSaveFrame 0
#define g_bSaveFinalFrame 0
#else
bool g_bSaveFrame = 0;  // saves the current psurfTarget
bool g_bSaveFinalFrame = 0; // saves the input to the CRTC
#endif // !defined(ZEROGS_DEVBUILD)

extern int maxmin;
extern bool g_bCRTCBilinear;
bool g_bDisplayFPS = false;
int g_nFrameRender = 10, g_nFramesSkipped = 0, s_nResolved = 0; // s_nResolved == number of targets resolved this frame
// Helper for skip frames.
int TimeLastSkip = 0;

vector<u32> s_vecTempTextures;		   // temporary textures, released at the end of every frame

// Snapshot variables.
extern bool g_bMakeSnapshot;
extern string strSnapshot;

extern void ExtWrite();
extern void ZZDestroy();
extern void ChangeDeviceSize(int nNewWidth, int nNewHeight);

extern GLuint vboRect;
// Adjusts vertex shader BitBltPos vector v to preserve aspect ratio. It used to emulate 4:3 or 16:9.
void AdjustTransToAspect(float4& v)
{
	double temp;
	float f;
	const float mult = 1 / 32767.0f;

	if (conf.width * GLWin.backbuffer.h > conf.height * GLWin.backbuffer.w) // limited by width
	{
		// change in ratio
		f = ((float)GLWin.backbuffer.w / (float)conf.width) / ((float)GLWin.backbuffer.h / (float)conf.height);
		v.y *= f;
		v.w *= f;

		// scanlines mess up when not aligned right
		v.y += (1 - (float)modf(v.y * (float)GLWin.backbuffer.h * 0.5f + 0.05f, &temp)) * 2.0f / (float)GLWin.backbuffer.h;
		v.w += (1 - (float)modf(v.w * (float)GLWin.backbuffer.h * 0.5f + 0.05f, &temp)) * 2.0f / (float)GLWin.backbuffer.h;
	}
	else // limited by height
	{
		f = ((float)GLWin.backbuffer.h / (float)conf.height) / ((float)GLWin.backbuffer.w / (float)conf.width);
		f -= (float)modf(f * GLWin.backbuffer.w, &temp) / (float)GLWin.backbuffer.w;
		v.x *= f;
		v.z *= f;
	}

	v  *= mult;
}

inline bool FrameSkippingHelper()
{
	bool ShouldSkip = false;

	if (g_nFrameRender > 0)
	{
		if (g_nFrameRender < 8)
		{
			g_nFrameRender++;

			if (g_nFrameRender <= 3)
			{
				g_nFramesSkipped++;
				ShouldSkip = true;
			}
		}
	}
	else
	{
		if (g_nFrameRender < -1)
		{
			g_nFramesSkipped++;
			ShouldSkip = true;
		}

		g_nFrameRender--;
	}

#if defined _DEBUG
	if (timeGetTime() - TimeLastSkip > 15000 && ShouldSkip)
	{
		ZZLog::Debug_Log("ZZogl Skipped frames.");
		TimeLastSkip = timeGetTime();
	}
#endif

	return ShouldSkip;
}

// helper function for save frame in picture.
inline void FrameSavingHelper()
{
	if (g_bSaveFrame)
	{
		if (vb[0].prndr != NULL)
		{
			SaveTexture("frame1.tga", GL_TEXTURE_RECTANGLE_NV, vb[0].prndr->ptex, RW(vb[0].prndr->fbw), RH(vb[0].prndr->fbh));
		}

		if (vb[1].prndr != NULL && vb[0].prndr != vb[1].prndr)
		{
			SaveTexture("frame2.tga", GL_TEXTURE_RECTANGLE_NV, vb[1].prndr->ptex, RW(vb[1].prndr->fbw), RH(vb[1].prndr->fbh));
		}

#ifdef _WIN32
		else 
		{
			DeleteFile(L"frame2.tga");
		}
#endif
	}
}

// Function populated tex0Info[2] array
inline void FrameObtainDispinfo(u32 bInterlace, tex0Info* dispinfo)
{
	for (int i = 0; i < 2; ++i)
	{

		if (!(*(u32*)(PMODE) & (1 << i)))
		{
			dispinfo[i].tw = 0;
			dispinfo[i].th = 0;
			continue;
		}

		GSRegDISPFB* pfb = i ? DISPFB2 : DISPFB1;
		GSRegDISPLAY* pd = i ? DISPLAY2 : DISPLAY1;
		int magh = pd->MAGH + 1;
		int magv = pd->MAGV + 1;

		dispinfo[i].tbp0 =  pfb->FBP << 5;
		dispinfo[i].tbw = pfb->FBW << 6;
		dispinfo[i].tw = (pd->DW + 1) / magh;
		dispinfo[i].th = (pd->DH + 1) / magv;
		dispinfo[i].psm = pfb->PSM;

		// hack!!
		// 2 * dispinfo[i].tw / dispinfo[i].th <= 1, metal slug 4

		if (bInterlace && 2 * dispinfo[i].tw / dispinfo[i].th <= 1 && !(conf.settings().interlace_2x))
		{
			dispinfo[i].th >>= 1;
		}
	}
}

extern bool s_bWriteDepth;

// Something should be done before Renderering the picture.
inline void RenderStartHelper(u32 bInterlace)
{
	if (conf.mrtdepth && pvs[8] == NULL)
	{
		conf.mrtdepth = 0;
		s_bWriteDepth = false;

		ZZLog::Debug_Log("Disabling MRT depth writing\n");
	}

	FlushBoth();

	FrameSavingHelper();

	if (s_RangeMngr.ranges.size() > 0) FlushTransferRanges(NULL);

	SetShaderCaller("RenderStartHelper");

	// reset fba after every frame
	vb[0].fba.fba = 0;
	vb[1].fba.fba = 0;

	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);   // switch to the backbuffer

	glViewport(0, 0, GLWin.backbuffer.w, GLWin.backbuffer.h);

	// if interlace, only clear every other vsync
	if (!bInterlace)
	{
		//u32 color = COLOR_ARGB(0, BGCOLOR->R, BGCOLOR->G, BGCOLOR->B);
		glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
	}

	ZZshSetVertexShader(pvsBitBlt.prog);

	glBindBuffer(GL_ARRAY_BUFFER, vboRect);
	SET_STREAM();
	GL_REPORT_ERRORD();

	if (conf.wireframe()) glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

	DisableAllgl();

	GL_REPORT_ERRORD();

	if (bInterlace) g_PrevBitwiseTexX = -1;  // reset since will be using
}

// Settings for interlace texture multiplied vector;
// The idea is: (x, y) -- position on screen, then interlaced texture get F = 1 or 0 depending
// on image y coords. So if we write valpha.z * F + valpha.w + 0.5, it would be switching odd
// and even strings at each frame.
// valpha.x and y are used for image blending.
inline float4 RenderGetForClip(u32 bInterlace, int interlace, int psm, FRAGMENTSHADER* prog)
{
	SetShaderCaller("RenderGetForClip");

	float4 valpha;
	// first render the current render targets, then from ptexMem

	if (psm == 1)
	{
		valpha.x = 1;
		valpha.y = 0;
	}
	else
	{
		valpha.x = 0;
		valpha.y = 1;
	}

	if (bInterlace)
	{
		if (interlace == (conf.interlace & 1))
		{
			// pass if odd
			valpha.z = 1.0f;
			valpha.w = -0.4999f;
		}
		else
		{
			// pass if even
			valpha.z = -1.0f;
			valpha.w = 0.5001f;
		}
	}
	else
	{
		// always pass interlace test
		valpha.z = 0;
		valpha.w = 1;
	}

	ZZshSetParameter4fv(prog->prog, prog->sOneColor, valpha, "g_fOneColor");

	return valpha;
}

// Put interlaced texture in use for shader prog.
// Note: if frame interlaced it's th is halved, so we should x2 it.
inline void RenderCreateInterlaceTex(u32 bInterlace, int th, FRAGMENTSHADER* prog)
{
	if (!bInterlace) return;

	int interlacetex = CreateInterlaceTex(2 * th);

	ZZshGLSetTextureParameter(prog->prog, prog->sInterlace, interlacetex, "Interlace");
}

// Well, do blending setup prior to second pass of half-frame drawing
inline void RenderSetupBlending()
{
	// setup right blending
	glEnable(GL_BLEND);
	zgsBlendEquationSeparateEXT(GL_FUNC_ADD, GL_FUNC_ADD);

	if (PMODE->MMOD)
	{
		glBlendColorEXT(PMODE->ALP*(1 / 255.0f), PMODE->ALP*(1 / 255.0f), PMODE->ALP*(1 / 255.0f), 0.5f);
		s_srcrgb = GL_CONSTANT_COLOR_EXT;
		s_dstrgb = GL_ONE_MINUS_CONSTANT_COLOR_EXT;
	}
	else
	{
		s_srcrgb = GL_SRC_ALPHA;
		s_dstrgb = GL_ONE_MINUS_SRC_ALPHA;
	}

	if (PMODE->AMOD)
	{
		s_srcalpha = GL_ZERO;
		s_dstalpha = GL_ONE;
	}
	else
	{
		s_srcalpha = GL_ONE;
		s_dstalpha = GL_ZERO;
	}
	
	zgsBlendFuncSeparateEXT(s_srcrgb, s_dstrgb, s_srcalpha, s_dstalpha);
}

// each frame could be drawn in two stages, so blending should be different for them
inline void RenderSetupStencil(int i)
{
	glStencilMask(1 << i);
	s_stencilmask = 1 << i;
	GL_STENCILFUNC_SET();
}

// do stencil check for each found target i -- texturing stage
inline void RenderUpdateStencil(int i, bool* bUsingStencil)
{
	if (!(*bUsingStencil)) glClear(GL_STENCIL_BUFFER_BIT);

	*bUsingStencil = 1;

	glEnable(GL_STENCIL_TEST);
	GL_STENCILFUNC(GL_NOTEQUAL, 3, 1 << i);
	glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
	glStencilMask(1 << i);
}

// CRTC24 could not be rendered
inline void RenderCRTC24helper(u32 bInterlace, int interlace, int psm)
{
	ZZLog::Debug_Log("ZZogl: CRTC24!!! I'm trying to show something.");
	SetShaderCaller("RenderCRTC24helper");
	// assume that data is already in ptexMem (do Resolve?)
	RenderGetForClip(bInterlace, interlace, psm, &ppsCRTC24[bInterlace]);
	ZZshSetPixelShader(ppsCRTC24[bInterlace].prog);
	
	DrawTriangleArray();
}

// Maybe I do this function global-defined. Calculate bits per pixel for
// each psm. It's the only place with PSMCT16 which have a different bpp.
// FIXME: check PSMCT16S
inline int RenderGetBpp(int psm)
{
	if (psm == PSMCT16S)
	{
		//ZZLog::Debug_Log("ZZogl: 16S target.");
		return 3;
	}

	if (PSMT_ISHALF(psm)) return 2;

	return 4;
}

// We want to draw ptarg on screen, that could be disaligned to viewport.
// So we do aligning it by height.
inline int RenderGetOffsets(int* dby, int* movy, tex0Info& texframe, CRenderTarget* ptarg, int bpp)
{
	*dby += (256 / bpp) * (texframe.tbp0 - ptarg->fbp) / texframe.tbw;

	if (*dby < 0)
	{
		*movy = -*dby;
		*dby = 0;
	}

	return min(ptarg->fbh - *dby, texframe.th - *movy);
}

// BltBit shader calculate vertex (4 coord's pixel) position at the viewport.
inline float4 RenderSetTargetBitPos(int dh, int th, int movy, bool isInterlace)
{
	SetShaderCaller("RenderSetTargetBitPos");
	float4 v;
	// dest rect
	v.x = 1;
	v.y = dh / (float)th;
	v.z = 0;
	v.w = 1 - v.y;

	if (movy > 0) v.w -= movy / (float)th;

	AdjustTransToAspect(v);

	if (isInterlace)
	{
		// move down by 1 pixel
		v.w += 1.0f / (float)dh ;
	}

	ZZshSetParameter4fv(pvsBitBlt.prog, pvsBitBlt.sBitBltPos, v, "g_fBitBltPos");

	return v;
}

// Important stuff. We could use these coordinates to change viewport position on the frame.
// For example, use tw / X and tw / X magnify the viewport.
// Interlaced output is little out of VB, it could be seen as an evil blinking line on top
// and bottom, so we try to remove it.
inline float4 RenderSetTargetBitTex(float th, float tw, float dh, float dw, bool isInterlace)
{
	SetShaderCaller("RenderSetTargetBitTex");

	float4 v;
	v = float4(th, tw, dh, dw);

	// Incorrect Aspect ratio on interlaced frames

	if (isInterlace)
	{
		v.y -= 1.0f / conf.height;
		v.w += 1.0f / conf.height;
	}

	ZZshSetParameter4fv(pvsBitBlt.prog, pvsBitBlt.sBitBltTex, v, "g_fBitBltTex");

	return v;
}

// Translator for POSITION coordinates (-1.0:+1.0f at x axis, +1.0f:-1.0y at y) into target frame ones.
// We don't need x coordinate, because interlacing is y-axis only.
inline float4 RenderSetTargetBitTrans(int th)
{
	SetShaderCaller("RenderSetTargetBitTrans");
	float4 v = float4(float(th), -float(th), float(th), float(th));
	ZZshSetParameter4fv(pvsBitBlt.prog, pvsBitBlt.fBitBltTrans, v, "g_fBitBltTrans");
	return v;
}

// use g_fInvTexDims to store inverse texture dims
// Seems, that Targ shader does not use it
inline float4 RenderSetTargetInvTex(int bInterlace, int tw, int th, FRAGMENTSHADER* prog)
{
	SetShaderCaller("RenderSetTargetInvTex");

	float4 v = float4(0, 0, 0, 0);

	if (prog->sInvTexDims)
	{
		v.x = 1.0f / (float)tw;
		v.y = 1.0f / (float)th;
		v.z = (float)0.0;
		v.w = -0.5f / (float)th;
		ZZshSetParameter4fv(prog->prog, prog->sInvTexDims, v, "g_fInvTexDims");
	}

	return v;
}

// Metal Slug 5 hack (as was written). If target tbp not equal to framed fbp, than we look for a better possibility,
// Note, than after true result iterator it could not be used.
inline bool RenderLookForABetterTarget(int fbp, int tbp, list<CRenderTarget*>& listTargs, list<CRenderTarget*>::iterator& it)
{
	if (fbp == tbp) return false;

	// look for a better target (metal slug 5)
	list<CRenderTarget*>::iterator itbetter;

	for (itbetter = listTargs.begin(); itbetter != listTargs.end(); ++itbetter)
	{
		if ((*itbetter)->fbp == tbp) break;
	}

	if (itbetter != listTargs.end())
	{
		it = listTargs.erase(it);
		return true;
	}

	return false;
}

inline void RenderCheckForMemory(tex0Info& texframe, list<CRenderTarget*>& listTargs, int i, bool* bUsingStencil, int interlace, int bInterlace);

// First try to draw frame from targets. 
inline void RenderCheckForTargets(tex0Info& texframe, list<CRenderTarget*>& listTargs, int i, bool* bUsingStencil, int interlace, int bInterlace)
{
	// get the start and end addresses of the buffer
	int bpp = RenderGetBpp(texframe.psm);
	GSRegDISPFB* pfb = i ? DISPFB2 : DISPFB1;

	int start, end;
	GetRectMemAddress(start, end, texframe.psm, 0, 0, texframe.tw, texframe.th, texframe.tbp0, texframe.tbw);

	// We need share list of targets between functions
	s_RTs.GetTargs(start, end, listTargs);

	for (list<CRenderTarget*>::iterator it = listTargs.begin(); it != listTargs.end();)
	{
		CRenderTarget* ptarg = *it;

		if (ptarg->fbw == texframe.tbw && !(ptarg->status&CRenderTarget::TS_NeedUpdate) && ((256 / bpp)*(texframe.tbp0 - ptarg->fbp)) % texframe.tbw == 0)
		{
			int dby = pfb->DBY;
			int movy = 0;
			
			if (RenderLookForABetterTarget(ptarg->fbp, texframe.tbp0, listTargs, it)) continue;

			if (g_bSaveFinalFrame) SaveTexture("frame1.tga", GL_TEXTURE_RECTANGLE_NV, ptarg->ptex, RW(ptarg->fbw), RH(ptarg->fbh));

			// determine the rectangle to render
			int dh = RenderGetOffsets(&dby, &movy, texframe, ptarg, bpp);

			if (dh >= 64)
			{

				if (ptarg->fbh - dby < texframe.th - movy && !(*bUsingStencil))
					RenderUpdateStencil(i, bUsingStencil);
				else if (ptarg->fbh - dby > 2 * ( texframe.th - movy )) 
				{
					// Sometimes calculated position onscreen is misaligned, ie in FFX-2 intro. In such case some part of image are out of
					// border's and we should move it manually.
					dby -= ((ptarg->fbh - dby) >> 2) -  ((texframe.th + movy) >> 1) ;
				}

				SetShaderCaller("RenderCheckForTargets");

				// Texture
				float4 v = RenderSetTargetBitTex((float)RW(texframe.tw), (float)RH(dh), (float)RW(pfb->DBX), (float)RH(dby), INTERLACE_COUNT);

				// dest rect
				v = RenderSetTargetBitPos(dh, texframe.th, movy, INTERLACE_COUNT);
				v = RenderSetTargetBitTrans(ptarg->fbh);
				v = RenderSetTargetInvTex(bInterlace, texframe.tbw, ptarg->fbh, &ppsCRTCTarg[bInterlace]) ; 	// FIXME. This is no use

				float4 valpha = RenderGetForClip(bInterlace, interlace, texframe.psm, &ppsCRTCTarg[bInterlace]);

				// inside vb[0]'s target area, so render that region only
				ZZshGLSetTextureParameter(ppsCRTCTarg[bInterlace].prog, ppsCRTCTarg[bInterlace].sFinal, ptarg->ptex, "CRTC target");
				RenderCreateInterlaceTex(bInterlace, texframe.th, &ppsCRTCTarg[bInterlace]);

				ZZshSetPixelShader(ppsCRTCTarg[bInterlace].prog);

				DrawTriangleArray();

				if (abs(dh - (int)texframe.th) <= 1) return;

				if (abs(dh - (int)ptarg->fbh) <= 1)
				{
					it = listTargs.erase(it);
					continue;
				}
			}
		}

		++it;
	}
	RenderCheckForMemory(texframe, listTargs, i, bUsingStencil, interlace, bInterlace);
}


// The same as the previous, but from memory.
// If you ever wondered why a picture from a minute ago suddenly flashes on the screen (say, in Mana Khemia),
// this is the function that does it.
inline void RenderCheckForMemory(tex0Info& texframe, list<CRenderTarget*>& listTargs, int i, bool* bUsingStencil, int interlace, int bInterlace)
{
	float4 v;
	
	for (list<CRenderTarget*>::iterator it = listTargs.begin(); it != listTargs.end(); ++it)
	{
		(*it)->Resolve();
	}

	// context has to be 0
	if (bInterlace >= 2) ZZLog::Error_Log("CRCR Check for memory shader fault.");

	//if (!(*bUsingStencil)) RenderUpdateStencil(i, bUsingStencil);
		
	SetShaderCaller("RenderCheckForMemory");

	float w1, h1, w2, h2;
	if (g_bCRTCBilinear)
	{
		w1 = texframe.tw;
		h1 = texframe.th;
		w2 = -0.5f;
		h2 = -0.5f;
		SetTexVariablesInt(0, 2, texframe, false, &ppsCRTC[bInterlace], 1);
	}
	else
	{
		w1 = 1;
		h1 = 1;
		w2 = -0.5f / (float)texframe.tw;
		h2 = -0.5f / (float)texframe.th;
		SetTexVariablesInt(0, 0, texframe, false, &ppsCRTC[bInterlace], 1);
	}
	
	if (g_bSaveFinalFrame) SaveTex(&texframe, g_bSaveFinalFrame - 1 > 0);
	
	// Fixme: Why is this here?
	// We should probably call RenderSetTargetBitTex instead.
	v = RenderSetTargetBitTex(w1, h1, w2, h2, INTERLACE_COUNT);

	// finally render from the memory (note that the stencil buffer will keep previous regions)
	v = RenderSetTargetBitPos(1, 1, 0, INTERLACE_COUNT);
	v = RenderSetTargetBitTrans(texframe.th);
	v = RenderSetTargetInvTex(bInterlace, texframe.tw, texframe.th, &ppsCRTC[bInterlace]);
	float4 valpha = RenderGetForClip(bInterlace, interlace, texframe.psm, &ppsCRTC[bInterlace]);

	ZZshGLSetTextureParameter(ppsCRTC[bInterlace].prog, ppsCRTC[bInterlace].sMemory, vb[0].pmemtarg->ptex->tex, "CRTC memory");
	RenderCreateInterlaceTex(bInterlace, texframe.th, &ppsCRTC[bInterlace]);
	ZZshSetPixelShader(ppsCRTC[bInterlace].prog);
	
	DrawTriangleArray();
}

extern RasterFont* font_p;

void DrawText(const char* pstr, int left, int top, u32 color)
{
	FUNCLOG
	ZZshGLDisableProfile();

	float4 v;
	v.SetColor(color);
	glColor3f(v.z, v.y, v.x);

	font_p->printString(pstr, left * 2.0f / (float)GLWin.backbuffer.w - 1, 1 - top * 2.0f / (float)GLWin.backbuffer.h, 0);
	ZZshGLEnableProfile();
}

// Put FPS counter on screen (not in window title)
inline void DisplayFPS()
{
	char str[64];
	int left = 10, top = 15;
	sprintf(str, "%.1f fps", fFPS);

	DrawText(str, left + 1, top + 1, 0xff000000);
	DrawText(str, left, top, 0xffc0ffff);
}

// SnapeShoot helper
inline void MakeSnapshot()
{
	
	if (!g_bMakeSnapshot) return;
	
	char str[64];
	int left = 200, top = 15;
	sprintf(str, "ZeroGS %d.%d.%d - %.1f fps %s", zgsrevision, zgsbuild, zgsminor, fFPS, s_frameskipping ? " - frameskipping" : "");

	DrawText(str, left + 1, top + 1, 0xff000000);
	DrawText(str, left, top, 0xffc0ffff);

	if (SaveRenderTarget(strSnapshot != "" ? strSnapshot.c_str() : "temp.jpg", GLWin.backbuffer.w, -GLWin.backbuffer.h, 0))  //(conf.options.tga_snap)?0:1) ) {
	{
		char str[255];
		sprintf(str, "saved %s\n", strSnapshot.c_str());
		ZZAddMessage(str, 500);
	}
	
	g_bMakeSnapshot = false;
}

// call to destroy video resources
void ZZReset()
{
	FUNCLOG
	s_RTs.ResolveAll();
	s_DepthRTs.ResolveAll();

	vb[0].nCount = 0;
	vb[1].nCount = 0;

	memset(s_nResolveCounts, 0, sizeof(s_nResolveCounts));
	s_nLastResolveReset = 0;

	icurctx = -1;
	g_vsprog = g_psprog = 0;

	ZZGSStateReset();
	ZZDestroy();
	//clear_drawfn();
	if (ZZKick != NULL) delete ZZKick;
}

// Put new values on statistic variable
inline void CountStatistics()
{
	if (s_nWriteDepthCount > 0)
	{
		assert(conf.mrtdepth);

		if (--s_nWriteDepthCount <= 0)
		{
			s_bWriteDepth = false;
		}
	}

	if (s_nWriteDestAlphaTest > 0)
	{
		if (--s_nWriteDestAlphaTest <= 0)
		{
			s_bDestAlphaTest = false;
		}
	}

	if (g_nDepthUsed > 0) --g_nDepthUsed;

	s_ClutResolve = 0;
	g_nDepthUpdateCount = 0;
}

// This all could be easily forefeit
inline void AfterRendererUnimportantJob()
{
	ProcessMessages();

	if (g_bDisplayFPS) DisplayFPS();

	// Swapping buffers, so we could use another window
	GLWin.SwapGLBuffers();

	// clear all targets
	if (conf.wireframe()) s_nWireframeCount = 1;

	if (g_bMakeSnapshot) MakeSnapshot();

	CaptureFrame();
	CountStatistics();

	if (s_nNewWidth >= 0 && s_nNewHeight >= 0) 
	{
		// If needed reset
		ZZReset();

		ChangeDeviceSize(s_nNewWidth, s_nNewHeight);
		s_nNewWidth = s_nNewHeight = -1;
	}

	maxmin = 608;
}

extern u32 s_uFramebuffer;

// Swich Framebuffers
inline void AfterRendererSwitchBackToTextures()
{
	glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, s_uFramebuffer);

	g_MemTargs.DestroyCleared();

	if (s_vecTempTextures.size() > 0)
		glDeleteTextures((GLsizei)s_vecTempTextures.size(), &s_vecTempTextures[0]);

	s_vecTempTextures.clear();

	if (EXTWRITE->WRITE & 1)
	{
		ZZLog::Warn_Log("EXTWRITE!");
		ExtWrite();
		EXTWRITE->WRITE = 0;
	}

	if (conf.wireframe()) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

	glEnable(GL_SCISSOR_TEST);

	if (icurctx >= 0)
	{
		vb[icurctx].bVarsSetTarg = false;
		vb[icurctx].bVarsTexSync = false;
		vb[0].bVarsTexSync = false;
	}
}

// Reset Targets Helper, for hack.
inline void AfterRendererAutoresetTargets()
{
	if (conf.settings().auto_reset)
	{
		s_nResolveCounts[s_nCurResolveIndex] = s_nResolved;
		s_nCurResolveIndex = (s_nCurResolveIndex + 1) % ArraySize(s_nResolveCounts);

		int total = 0;

		for (int i = 0; i < ArraySize(s_nResolveCounts); ++i) total += s_nResolveCounts[i];

		if (total / ArraySize(s_nResolveCounts) > 3)
		{
			if (s_nLastResolveReset > (int)(fFPS * 8))
			{
				// reset
				ZZLog::Error_Log("Video memory reset.");
				s_nLastResolveReset = 0;
				memset(s_nResolveCounts, 0, sizeof(s_nResolveCounts));

				s_RTs.ResolveAll();
				return;
//				s_RTs.Destroy();
//				s_DepthRTs.ResolveAll();
//				s_DepthRTs.Destroy();
//
//				vb[0].prndr = NULL;
//				vb[0].pdepth = NULL;
//				vb[0].bNeedFrameCheck = 1;
//				vb[0].bNeedZCheck = 1;
//				vb[1].prndr = NULL;
//				vb[1].pdepth = NULL;
//				vb[1].bNeedFrameCheck = 1;
//				vb[1].bNeedZCheck = 1;
			}
		}

		s_nLastResolveReset++;
	}

	if (s_nResolved > 8) 
		s_nResolved = 2;
	else if (s_nResolved > 0)
		--s_nResolved;
}

int count = 0;
// The main renderer function
void RenderCRTC(int interlace)
{
	if (FrameSkippingHelper()) return;

	u32 bInterlace = SMODE2->INT && SMODE2->FFMD && (conf.interlace < 2);

	RenderStartHelper(bInterlace);

	bool bUsingStencil = false;
	tex0Info dispinfo[2];

	FrameObtainDispinfo(bInterlace, dispinfo);

	// start from the last circuit
	for (int i = !PMODE->SLBG; i >= 0; --i)
	{
		tex0Info& texframe = dispinfo[i];

		if (texframe.th <= 1) continue;
		if (SMODE2->INT && SMODE2->FFMD) 
		{
			texframe.th >>= 1;

			// Final Fantasy X-2 issue here.
			if (conf.interlace == 2 && texframe.th >= 512) 
				texframe.th >>= 1;
		}
		
		if (i == 0) RenderSetupBlending();
		if (bUsingStencil) RenderSetupStencil(i);

		if (texframe.psm == 0x12)
		{
			RenderCRTC24helper(bInterlace, interlace, texframe.psm);
			continue;
		}

		// We shader targets between two functions, so declare it here;
		list<CRenderTarget*> listTargs;

		// if we could not draw image from target's do it from memory
		RenderCheckForTargets(texframe, listTargs, i, &bUsingStencil, interlace, bInterlace);
	}

	GL_REPORT_ERRORD();

	glDisable(GL_BLEND);

	AfterRendererUnimportantJob();
	AfterRendererSwitchBackToTextures();
	AfterRendererAutoresetTargets();
}
