#include <vector>
#include <random>
#include <cassert>

#include <iostream>
#include <iomanip>
#include <stdexcept>
#include <cstdio>
#include "tclap/CmdLine.h"
#include "benchmark.hpp"
#include "latticeview.h"

typedef unsigned short store_t;
typedef double calc_t;
typedef std::size_t size_t;
typedef int sint_t;
#include "hoshen_kopel_naive.cpp"

template<typename T>
void create_perc(T& field, calc_t const& p){
	static std::mt19937_64 rand_gen(42);
	static std::uniform_real_distribution<calc_t> rand_num(0,1);

	for(auto& row : field){
		for(auto& col : row) {
			if (rand_num(rand_gen) < p)
				col = 1;
			else
				col = 0;
		}
	}

}

#ifdef IMAGEMAGICK
#define OUTPUT_EXT "png"
#else
#define IMAGEMAGICK false
#define OUTPUT_EXT "ppm"
#endif

int main(int argc, char* argv[]){
	//parse cmd args
	TCLAP::CmdLine cmd("Hoshen kopel", ' ', "0.1 alpha");
	TCLAP::ValueArg<std::string> outputArg("o","output","Output file",false,std::string("output.") + OUTPUT_EXT,"string", cmd);
	TCLAP::ValueArg<size_t> dimArg("N","dim","lattice dim",false,100,"integral type", cmd);
	TCLAP::ValueArg<calc_t> probabilityArg("p","prop","occupation probability",false,0.59,"floating point", cmd);

	cmd.parse( argc, argv );

	//initialize
	const size_t N = dimArg.getValue(); //grid size
	const calc_t p = probabilityArg.getValue(); // 

	std::vector<std::vector<store_t>> field(N, std::vector<store_t>(N));

	create_perc(field, p); //create random cluster
	BENCHMARK(hoshen_kopel_naive(field))(1000); //run algorithm

	if (outputArg.getValue() == "STDOUT")
		print_lattice_stdout(field);
	else {
		//parse output file extension
		std::string output = outputArg.getValue();
		std::string output_ext = output.substr(output.find_last_of(".") + 1);
		std::string output_basename = output.substr(0, output.find_last_of("."));

		if (!(output_ext == "ppm" || output_ext == "png") || output.find_last_of(".") == std::string::npos)
			throw std::invalid_argument("invalid file extension");
		if (!IMAGEMAGICK && output_ext == "png")
			throw std::invalid_argument("png file extension not supported. compile with imagemagick support");
	
		//export ppm file
		print_latticeV2(field,N,N,800,800, (output_basename+".ppm").c_str());

		//convert to png
		if (output_ext == "png") {
			//call imagemagick
			std::ostringstream cmd;
			cmd << IMAGEMAGICK << ' ' << output_basename.c_str() << ".ppm " << output_basename.c_str() << ".png";
			int exit_code = std::system(cmd.str().c_str());
			if (exit_code)
				throw std::runtime_error(std::string("convert to png failed with error code ") + std::to_string(exit_code));

			//delete ppm
			std::remove((output_basename+".ppm").c_str());
		}
	}

	return 0;
}

// benchmark helper macro
using std::chrono::system_clock;
using std::chrono::duration;
#define BENCHMARK(expr)											\
[&] (std::size_t num_ops) {										\
	/* tic */													\
	auto start = system_clock::now();							\
																\
	auto result = expr;											\
	for (int i=1; i< num_ops; i++) {							\
		result = expr;											\
		asm volatile("" : "+r" (result));						\
	}															\
																\
	/* toc */													\
	auto end = system_clock::now();								\
																\
	auto d = duration<double>(end-start).count();				\
	std::cout << "benchmark " << #expr 							\
			  << " " << d << std::endl;							\
	return duration<double>(end-start).count();					\
}