#define __STDC_LIMIT_MACROS
#include "Image.hpp"
#include "Plate.hpp"

#include <Graphics/Graphics.hpp>
#include <Graphics/GLExtensions.hpp>

#include <Basis/Error.hpp>

#include <SDL.h>
#include <SDL_thread.h>

#include <cassert>

REQUIRE_GL_EXTENSION( GL_ARB_texture_rectangle )

Image::Image(float *mass) {
	inv_mass.resize(PlateSize*PlateSize);
	//int32_t min = INT32_MAX, max = INT32_MIN;
	for (unsigned int i = 0; i < PlateSize * PlateSize; ++i) {
		inv_mass[i] = (int32_t)(float(1 << 16) - float(1 << 15) * mass[i] + 0.5f);
	//	if (inv_mass[i] < min) min = inv_mass[i];
	//	if (inv_mass[i] > max) max = inv_mass[i];
	}
	//INFO("Image has range " << min << " to " << max);
	glGenTextures(1, &texture);
	glBindTexture(GL_TEXTURE_RECTANGLE_ARB, texture);
	glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0, GL_LUMINANCE, PlateSize, PlateSize, 0, GL_LUMINANCE, GL_FLOAT, mass);
	glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0);
}

Image::~Image() {
	glDeleteTextures(1, &texture);
	texture = 0;
}

#include <set>
#include <list>
#include <fstream>

#include <dirent.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>

using std::set;
using std::list;
using std::ifstream;

namespace ImageWatcher {

SDL_mutex *new_images_lock = NULL;

bool quit_watcher_thread = false;

list< vector< float > > new_images;

int image_watcher_thread(void *) {
	set< string > have;
	//skip special files:
	have.insert(".");
	have.insert("..");
	DIR *dir = opendir("images");
	if (dir == NULL) {
		ERROR("Cannot open directory '" << "images" << "'");
		return 0;
	}
	while (!quit_watcher_thread) {
		struct dirent *ent;
		while ((ent = readdir(dir))) {
			string name = string(ent->d_name);
			if (have.count(name)) continue;
			have.insert(name);
			{ //now actually read the image.
				ifstream im(("images/" + name).c_str());
				char p, five;
				if (!im.read(&p, 1) || !im.read(&five,1) || p != 'P' || five != '5') {
					ERROR("Bad magic on '" << name << "'.");
					continue;
				}
				unsigned int sx, sy, depth;
				if (!(im >> sx >> sy >> depth) || sx != PlateSize || sy != PlateSize || depth > 255) {
					ERROR("Bad size or depth on '" << name << "'.");
					continue;
				}
				char nl;
				if (!im.read(&nl, 1) || nl != '\n') {
					ERROR("Expecting single newline in '" << name << "'.");
					continue;
				}
				vector< uint8_t > raster(PlateSize * PlateSize);
				if (!im.read((char *)(&raster[0]), PlateSize * PlateSize)) {
					ERROR("Can't read raster from '" << name << "'.");
					continue;
				}
				//actually build an image:
				vector< float > values(PlateSize * PlateSize);
				for (unsigned int row = 0; row < PlateSize; ++row) {
					for (unsigned int i = 0; i < PlateSize; ++i) {
						values[(PlateSize - 1 - row) * PlateSize + i] = raster[row * PlateSize + i] / float(depth);
					}
				}
				SDL_LockMutex(new_images_lock);
				new_images.push_back(values);
				SDL_UnlockMutex(new_images_lock);
			}
		}
		sleep(1);
	}
	return 0;
}

SDL_Thread *watcher_thread = NULL;

void init() {
	quit_watcher_thread = false;
	assert(new_images_lock == NULL);
	new_images_lock = SDL_CreateMutex();
	watcher_thread = SDL_CreateThread(image_watcher_thread, NULL);
}

using std::cout;
using std::endl;

void deinit() {
	assert(new_images_lock != NULL);
	SDL_DestroyMutex(new_images_lock);
	new_images_lock = NULL;
	quit_watcher_thread = true;
	cout << "Waiting for watcher thread to die..."; cout.flush();
	SDL_WaitThread(watcher_thread, NULL);
	watcher_thread = NULL;
	cout <<  "  dead!" << endl;
}

void get_new_images(vector< Image * > &into) {
	assert(new_images_lock);
	SDL_LockMutex(new_images_lock);
	while (!new_images.empty()) {
		assert(new_images.back().size() == PlateSize * PlateSize);
		Image *image = new Image(&(new_images.back()[0]));
		new_images.pop_back();
		into.push_back(image);
	}
	SDL_UnlockMutex(new_images_lock);
}

};
