/*
	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License version 2 
	as published by the Free Software Foundation.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA


	Copyright (C) 2006  Thierry Berger-Perrin <tbptbp@gmail.com>
*/
#ifndef HORDE_H
#define HORDE_H

// pthread_cond_broadcast()
/*
	Theory:
	We have a static pool of threads - one per cpu/core -
	1) El capitan bakes a lifo of tasks.			| 1a) Grunts are sleeping on the deployment condition.
	2a) El capitan deploys all grunts.				| 2b) Each grunt greedingly tries to consume tasks.
	3a) El capitan sleeps on the docking condition.	| 3b) When the task pool is exhausted, grunts execute the docking sequence.
*/

#include "specifics.h"
#include "sys_cpu.h"
#include "sys_thread.h"
#include "sys_clock.h"

#include "sys_log.h"	//FIXME:

#include "math_vec.h"	// point_t
namespace horde {
	enum { max_grunts = sys::cpu::max_cpu };

	namespace captain {
		// active grunts
		extern int num_grunts;

		#ifdef WINDOWS
			extern HANDLE deployphore;
			extern HANDLE dockphore[max_grunts];

			// deploy
			static FINLINE void signal() { ReleaseSemaphore(deployphore, num_grunts, 0); }
			// wait for docking
			static FINLINE void wait() { WaitForMultipleObjects(num_grunts, dockphore, true, INFINITE); }
		#else
			extern sem_t deployphore, dockphore;
			static FINLINE void signal() {
				for (int i=0; i<num_grunts; ++i)
					sem_post(&deployphore);
			}
			static FINLINE void wait() {
				for (int i=0; i<num_grunts; ++i)
					sem_wait(&dockphore);
			}
		#endif
	}

	struct grunt_t : public sys::thread_t {
		grunt_t() : sys::thread_t(this) {}

		bool_t init() {
			const bool_t rc = 
				//true;
				sys::cpu::bind_thread(id) &
				//sys::cpu::set_thread_priority(sys::cpu::priority_low);
				//sys::cpu::set_thread_priority(sys::cpu::priority_normal);
				//sys::cpu::set_thread_priority(sys::cpu::priority_high);
				//sys::cpu::set_thread_priority(sys::cpu::priority_max);
				sys::cpu::set_thread_priority(sys::cpu::thread_priority_grunts);
				
			sys::log("horde::grunt_t::init: grunt #%d, %s.\n", id, rc ? "airborne" : "no-go");
			return rc;
		}

		int run();

		#ifdef WINDOWS
			// wait for deployment
			void wait() const { WaitForSingleObject(captain::deployphore, INFINITE); }
			// dock back
			void signal() const { ReleaseSemaphore(captain::dockphore[id], 1, 0); }
		#else
			// wait for deployment
			void wait() const { sem_wait(&captain::deployphore); }
			// dock back
			void signal() const { sem_post(&captain::dockphore); }
		#endif

		int id;
	};

	extern grunt_t grunts[max_grunts];

	static bool_t bootstrap() {
		captain::num_grunts = sys::cpu::num_cpu*1;
		//sys::cpu::set_thread_priority(sys::cpu::priority_high);

		#ifdef WINDOWS
			captain::deployphore = CreateSemaphore(0, 0, max_grunts, 0);
			for (int i=0; i<max_grunts; ++i)
				captain::dockphore[i] = CreateSemaphore(0, 0, 1, 0);
		#else
			sem_init(&captain::deployphore, false, 0);
			sem_init(&captain::dockphore, false, 0);
		#endif

		// spawn grunts
		bool_t ok = true;
		for (int i=0; ok & (i < captain::num_grunts); ++i) {
			grunts[i].id = i;
			ok &= sys::thread_t::spawn(grunts + i);
		}
		return ok;
	}

/*
 *
 *
 *
 *	link fixage
 *
 *
 *
 *
 */





	// where we store job chunks.
	// the contended index should be aligned somehow
	template<typename T>
	struct lifo64_t {
		//enum { max_capacity = 2048 };
		//enum { max_capacity = 8192 };
		enum { max_capacity = 4096 };	// up to 2048x2048 with 32x32 tiles.

		uint64_t		raw[max_capacity];
		mutable int32_t	idx; // mouarf.

		// careful: not serialized
		void reset() { idx = -1; }
		void push(const T &t) {
			++idx;
			raw[idx] = t;
		}

		// contended pop
		bool_t pop(T &t) const {
			const int32_t previous = sys::atomic::xadd32(idx, -1);
			if (previous < 0) return false;

			t = raw[previous];
			return true;
		}
	};

	extern lifo64_t<point_t> lifo;	// a tile lifo.
}

#endif
