#include <stdafx.h>
#include <vd2/system/math.h>
#include <vd2/system/time.h>
#include <vd2/VDDisplay/textrenderer.h>
#include "uiwidget.h"
#include "uiprofiler.h"
#include "uicontainer.h"
#include "uimanager.h"

class ATUIProfilerWindow : public ATUIWidget {
public:
	ATUIProfilerWindow();

	void BeginFrame();
	void BeginRegion(ATUIProfileRegion region);
	void EndRegion();

	void AutoSize(const vdpoint32& origin);

protected:
	void OnCreate();
	void OnDestroy();

	void Paint(IVDDisplayRenderer& rdr, sint32 w, sint32 h);

	void RecordRegion(ATUIProfileRegion region, uint64 t);

	int mX;
	int mLastY;
	int mMaxY;
	int mRegionStackHt;
	uint64 mFrameStartTime;
	double mPreciseTicksToPixels;

	uint32 mRegionRectStarts[kATUIProfileRegionCount];

	vdrefptr<IVDDisplayFont> mpFont;

	vdvector<vdrect32> mRegionRects[kATUIProfileRegionCount];
	ATUIProfileRegion mRegionStack[64];
};

ATUIProfilerWindow *g_pATUIProfilerWindow;

ATUIProfilerWindow::ATUIProfilerWindow()
	: mX(0)
	, mLastY(0)
	, mMaxY(200)
	, mRegionStackHt(0)
	, mFrameStartTime(0)
	, mPreciseTicksToPixels(0)
{
	mbHitTransparent = true;

	mMaxY = 200;
	mFrameStartTime = VDGetPreciseTick();
	mPreciseTicksToPixels = VDGetPreciseSecondsPerTick() * (double)mMaxY * 30.0f;

	std::fill(mRegionRectStarts, mRegionRectStarts + kATUIProfileRegionCount, 0);
}

void ATUIProfilerWindow::BeginFrame() {
	Invalidate();

	mRegionStackHt = 0;
	mLastY = 0;

	mX = (mX + 1) & 255;
	mFrameStartTime = VDGetPreciseTick();

	uint32 xdel = (mX + 1) & 255;
	for(int i=0; i<kATUIProfileRegionCount; ++i) {
		vdvector<vdrect32>& v = mRegionRects[i];
		uint32& regionStart = mRegionRectStarts[i];
		uint32 n = v.size();

		while(regionStart < n && v[regionStart].left == xdel)
			++regionStart;

		if (!(xdel & 0x7F)) {
			v.erase(v.begin(), v.begin() + regionStart);
			regionStart = 0;
		}
	}
}

void ATUIProfilerWindow::BeginRegion(ATUIProfileRegion region) {
	if (mRegionStackHt < vdcountof(mRegionStack)) {
		if (mRegionStackHt) {
			uint64 t = VDGetPreciseTick();

			RecordRegion(mRegionStack[mRegionStackHt - 1], t);
		}

		mRegionStack[mRegionStackHt++] = region;
	}
}

void ATUIProfilerWindow::EndRegion() {
	if (mRegionStackHt) {
		uint64 t = VDGetPreciseTick();

		RecordRegion(mRegionStack[--mRegionStackHt], t);
	}
}

void ATUIProfilerWindow::AutoSize(const vdpoint32& origin) {
	int w = 256;
	int h = 200 + 4;

	if (mpFont) {
		VDDisplayFontMetrics metrics;
		mpFont->GetMetrics(metrics);

		h += (metrics.mAscent + metrics.mDescent)*kATUIProfileRegionCount;
	}

	SetArea(vdrect32(16, 48, 16 + w, 48 + h));
}

void ATUIProfilerWindow::OnCreate() {
	mpFont = mpManager->GetThemeFont(kATUIThemeFont_Default);
}

void ATUIProfilerWindow::OnDestroy() {
	mpFont.clear();
}

void ATUIProfilerWindow::Paint(IVDDisplayRenderer& rdr, sint32 w, sint32 h) {
	static const uint32 kColors[]={
		0x808080,
		0x4060E0,
		0xE02010,
		0x20E010,
	};

	VDASSERTCT(vdcountof(kColors) == kATUIProfileRegionCount);

	for(int i=0; i<kATUIProfileRegionCount; ++i) {
		uint32 n = mRegionRects[i].size();
		uint32 pos = mRegionRectStarts[i];

		if (pos < n) {
			rdr.SetColorRGB(kColors[i]);
			rdr.MultiFillRect(&mRegionRects[i][pos], n - pos);
		}
	}

	if (mpFont) {
		VDDisplayTextRenderer& tr = *rdr.GetTextRenderer();

		tr.SetFont(mpFont);
		tr.SetAlignment(VDDisplayTextRenderer::kAlignLeft, VDDisplayTextRenderer::kVertAlignTop);

		int x = 48;
		int y = 204;
		static const wchar_t *const kNames[]={
			L"Idle",
			L"Simulation",
			L"Native messages",
			L"Display tick",
		};

		VDASSERTCT(vdcountof(kNames) == kATUIProfileRegionCount);

		VDDisplayFontMetrics metrics;
		mpFont->GetMetrics(metrics);

		for(size_t i=0; i<vdcountof(kNames); ++i) {
			rdr.SetColorRGB(kColors[i]);
			rdr.FillRect(4, y + (metrics.mAscent + metrics.mDescent)*i, 40, metrics.mAscent + metrics.mDescent);
		}

		tr.SetColorRGB(0xFFFFFF);
		for(size_t i=0; i<vdcountof(kNames); ++i) {
			tr.DrawTextLine(x, y, kNames[i]);
			y += metrics.mAscent + metrics.mDescent;
		}
	}
}

void ATUIProfilerWindow::RecordRegion(ATUIProfileRegion region, uint64 t) {
	int y = VDRoundToInt((double)(t - mFrameStartTime) * mPreciseTicksToPixels);

	if (y < 0)
		return;

	if (y > mMaxY)
		y = mMaxY;

	if (y > mLastY) {
		vdvector<vdrect32>& v = mRegionRects[region];
		int y1 = mMaxY-y;
		int y2 = mMaxY-mLastY;

		if (!v.empty() && v.back().left == mX && v.back().top == y2)
			v.back().top = y1;
		else
			v.push_back(vdrect32(mX, y1, mX+1, y2));

		mLastY = y;
	}
}

void ATUIProfileCreateWindow(ATUIManager *m) {
	if (!g_pATUIProfilerWindow) {
		g_pATUIProfilerWindow = new ATUIProfilerWindow;
		g_pATUIProfilerWindow->AddRef();
		m->GetMainWindow()->AddChild(g_pATUIProfilerWindow);
		g_pATUIProfilerWindow->AutoSize(vdpoint32(16, 48));
	}
}

void ATUIProfileDestroyWindow() {
	if (g_pATUIProfilerWindow) {
		g_pATUIProfilerWindow->Destroy();
		g_pATUIProfilerWindow->Release();
		g_pATUIProfilerWindow = NULL;
	}
}

void ATUIProfileBeginFrame() {
	if (g_pATUIProfilerWindow)
		g_pATUIProfilerWindow->BeginFrame();
}

void ATUIProfileBeginRegion(ATUIProfileRegion region) {
	if (g_pATUIProfilerWindow)
		g_pATUIProfilerWindow->BeginRegion(region);
}

void ATUIProfileEndRegion() {
	if (g_pATUIProfilerWindow)
		g_pATUIProfilerWindow->EndRegion();
}
