#pragma once
#include <string>
#include "cl.hpp"

class TestCase {
protected:
	cl::Buffer clInputBuffer;
	cl::Buffer clResultBuffer;
	size_t data_size = 0;
public:
	virtual void collect_results(cl::CommandQueue* queue) = 0;
	virtual void gpu_compute(
		cl::Context* context,
		cl::CommandQueue* queue,
		cl::Program* program,
		cl::Event* Event
	) = 0;
	virtual void cpu_compute() = 0;
	virtual bool validate_results() = 0;
	virtual std::string description() = 0;
};

class Square : public TestCase {
private:
	std::vector<float> gpuResult;
	std::vector<float> sourceData;
	std::vector<float> cpuResult;
public:
	Square();
	void collect_results(cl::CommandQueue* queue);
	void gpu_compute(cl::Context* context, cl::CommandQueue* queue, cl::Program* program, cl::Event* Event);
	void cpu_compute();
	bool validate_results();
	std::string description();
};

class Histogram : public TestCase {
private:
	bool global;
	size_t valueSet;
	std::vector<int> gpuResult;
	std::vector<int> sourceData;
	std::vector<int> cpuResult;
public:
	Histogram(bool _global, int _valueSet, int _data_size);
	void collect_results(cl::CommandQueue* queue);
	void gpu_compute(cl::Context* context, cl::CommandQueue* queue, cl::Program* program, cl::Event* Event);
	void cpu_compute();
	bool validate_results();
	std::string description();
};

class ReduceAdd : public TestCase {
private:
	std::vector<float> gpuResult;
	std::vector<float> sourceData;
	float cpuResult;
public:
	ReduceAdd(size_t max_size);
	void collect_results(cl::CommandQueue* queue);
	void gpu_compute(cl::Context* context, cl::CommandQueue* queue, cl::Program* program, cl::Event* Event);
	void cpu_compute();
	bool validate_results();
	std::string description();
};

class ExclusiveScan : public TestCase {
private:
	std::vector<int> gpuResult;
	std::vector<int> sourceData;
	std::vector<int> cpuResult;
public:
	ExclusiveScan(size_t max_size);
	void collect_results(cl::CommandQueue* queue);
	void gpu_compute(cl::Context* context, cl::CommandQueue* queue, cl::Program* program, cl::Event* Event);
	void cpu_compute();
	bool validate_results();
	std::string description();
};

class Compact : public TestCase {
private:
	std::vector<int> gpuResult;
	std::vector<int> sourceData;
	std::vector<int> cpuResult;
	int limit;
	int result_size;
public:
	Compact(size_t _data_size);
	void gpu_compute(cl::Context* context, cl::CommandQueue* queue, cl::Program* program, cl::Event* Event);
	void cpu_compute();
	void collect_results(cl::CommandQueue* queue);
	bool validate_results();
	std::string description();
};