#include "FPSSampler.h"
#include "RendererUtil.h"

using namespace VRUT;

void FPSSampler::GenerateSamples(const std::vector<unsigned int> & samplesCounts, bool subSampling, bool sort)
{
	Vector2 fpsSamples[TILE_SIZE * TILE_SIZE];
	bool sampled[TILE_SIZE * TILE_SIZE];
	memset(sampled, 0, sizeof(bool) * TILE_SIZE * TILE_SIZE);
	int i = 0;
	int j = 0;
	// seed samples
	for (int y = 0; y < TILE_SIZE; y += FPS_SEED_STEP)
	{
		for (int x = 0; x < TILE_SIZE; x += FPS_SEED_STEP)
		{
			int index = y * TILE_SIZE + x;
			if (!sampled[index])
			{
				sampled[index] = true;
				Vector2 sample = Vector2(x, y) + Vector2(0.5f, 0.5f);
				fpsSamples[i++] = sample;
			}
		}
	}
	// sample pixels using farthest point strategy
	Halton halton;
	int x, y, index;
	while (i < TILE_SIZE * TILE_SIZE)
	{
		Vector2 maxSample = fpsSamples[0];
		float maxDist = 0.0f;
		for (int k = 0; k < FPS_ITERATIONS; ++k)
		{
			// sample candidate
			Vector2 sample = TILE_SIZE * halton.Sample2D(j++, 2, 3);
			sample.x = int(sample.x) + 0.5f;
			sample.y = int(sample.y) + 0.5f;
			x = int(sample.x);
			y = int(sample.y);
			index = y * TILE_SIZE + x;
			if (sampled[index])
				continue;
			bool minFound = false;
			// use expanding window to find the nearest sample
			int w = 1;
			while (!minFound)
			{
				for (int u = -w; u <= w; ++u)
				{
					for (int v = 0; v < 4; ++v)
					{
						x = int(sample.x);
						y = int(sample.y);
						if (v <= 1)
						{
							x += u;
							if (v == 0)
								y -= w;
							else
								y += w;
						}
						else 
						{
							y += u;
							if (v == 2)
								x -= w;
							else
								x += w;
						}
						int x0 = x;
						while (x0 < 0)
							x0 += TILE_SIZE;
						int y0 = y;
						while (y0 < 0)
							y0 += TILE_SIZE;
						x0 = IntMod2k(x0 , TILE_SIZE); 
						y0 = IntMod2k(y0 , TILE_SIZE); 
						index = y0 * TILE_SIZE + x0;
						if (sampled[index])
						{
							minFound = true;
							Vector2 sampleTmp = Vector2(x + 0.5f, y + 0.5f);
							Vector2 diff = sample - sampleTmp;
							float dist = sqrt(diff.x * diff.x + diff.y * diff.y);
							if (dist > maxDist)
							{
								maxDist = dist;
								maxSample = sample;
							}
						}
					}
				}
				++w;
			}
		}
		x = int(maxSample.x);
		y = int(maxSample.y);
		index = y * TILE_SIZE + x;
		sampled[index] = true;
		fpsSamples[i++] = maxSample;
	}
	// sort along Hilbert curve
	Hilbert2D hilbert;
	HilbertComparator comparator;
	std::vector<HilbertPair> pairs;
	if (subSampling)
	{
		subSamples.clear();
		subSamples.resize(samplesCounts.size());
	}
	else 
	{
		samples.clear();
		samples.resize(samplesCounts.size());
	}
	int offset = 0;
	float scale = 1.0f / TILE_SIZE;
	for (int i = 0; i < samplesCounts.size(); ++i)
	{
		for (int j = offset; j < __min(offset + int(samplesCounts[i]), TILE_SIZE * TILE_SIZE); ++j)
		{
			int code = hilbert.xy2d(TILE_SIZE, int(fpsSamples[j].x), int(fpsSamples[j].y));
			pairs.push_back(HilbertPair(fpsSamples[j], code));
		}
		if (sort)
			std::sort(pairs.begin(), pairs.end(), comparator);
		for (auto &pair : pairs)
		{
			if (subSampling)
				subSamples[i].push_back(scale * pair.first);
			else
				samples[i].push_back(pair.first);
		}
		offset += pairs.size();
		pairs.clear();
	}
}

FPSSampler::FPSSampler(const std::vector<unsigned int> & samplesCounts, const std::vector<unsigned int> & subSamplesCounts, bool sort)
{
	GenerateSamples(samplesCounts, false, sort);
	GenerateSamples(subSamplesCounts, true, sort);
}

FPSSampler::~FPSSampler()
{
}

bool FPSSampler::GenPixMapIsNecessary(std::map<uint64_t, unsigned int>& m, const Tile* tile, int numPixels)
{
    return false;
}
