// frt.h
/*
 * FRT - A Godot platform targeting single board computers
 * Copyright (c) 2017-2019  Emanuele Fornara
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

#ifndef FRT_H
#define FRT_H

#define FRT_WINDOW_TITLE "Pandemonium / FRT"
#define FRT_VERSION "1.1.0"

#include <stdint.h>

struct InputModifierState;

namespace frt {

void fatal(const char *format, ...)
#ifdef __GNUC__
__attribute__((format(printf, 1, 2)))
#endif
;

struct Module {
	virtual const char *get_id() const = 0;
	virtual bool probe() = 0;
	virtual void cleanup() = 0;
	virtual bool handle_meta(int gd_code, bool pressed) { return false; }
};

struct EventDispatcher {
	virtual void dispatch_events() = 0;
};

struct EventHandler {
	virtual void handle_event() = 0;
};

struct Value {
	enum Type {
		Bool,
		Int,
		Float,
		String,
	} t;
	union {
		bool b;
		int i;
		float f;
		const char *s;
	} u;
	Value(bool v)
		: t(Bool) { u.b = v; }
	Value(int v)
		: t(Int) { u.i = v; }
	Value(float v)
		: t(Float) { u.f = v; }
	Value(const char *v)
		: t(String) { u.s = v; }
};

struct Param {
	const char *name;
	Value value;
	enum Source {
		Default,
		CommandLine,
		ProjectSettings,
	} source;
	Param(const char *name_, Value value_)
		: name(name_), value(value_), source(Default) {}
};

class App {
public:
	static const int max_modules = 30;
	static const int max_contexts = 10;
	static const int max_dispatchers = 10;
	void register_(Module *module);
	int size() const { return nm; }
	Module *get(int i) const { return m[i]; }
	Module *get(const char *id) const;
	Module *probe(const char *ids[]);
	Module *probe_single();
	Module *probe(const char *id) {
		const char *ids[] = { id, 0 };
		return probe(ids);
	}
	void **get_context(const char *key);
	void add_dispatcher(EventDispatcher *dispatcher);
	void remove_dispatcher(EventDispatcher *dispatcher);
	void dispatch_events();
	void quit() { running = false; }
	bool is_running() const { return running; }
	void parse_args(int *argc, char ***argv);
	int get_n_of_params() const;
	Param *get_param(int i) const;
	Param *get_param(const char *name) const;
	bool get_bool_param(const char *name) const {
		return get_param(name)->value.u.b;
	}
	int get_int_param(const char *name) const {
		return get_param(name)->value.u.i;
	}
	float get_float_param(const char *name) const {
		return get_param(name)->value.u.f;
	}
	const char *get_string_param(const char *name) const {
		return get_param(name)->value.u.s;
	}
	static App *instance();

private:
	App()
		: nm(0), nc(0), nd(0), running(true) {}
	Module *m[max_modules];
	struct {
		const char *key;
		void *value;
	} c[max_contexts];
	EventDispatcher *d[max_dispatchers];
	int nm, nc, nd;
	bool running;
};

struct RegisterModule {
	RegisterModule(Module *module) {
		App::instance()->register_(module);
	}
};

#define FRT_REGISTER(C)    \
	static C C##_instance; \
	static frt::RegisterModule C##_helper(&C##_instance);

struct Vec2 {
	int x;
	int y;
	Vec2(int x_ = 0, int y_ = 0)
		: x(x_), y(y_) {}
};

struct ContextGL {
	virtual void release_current() = 0;
	virtual void make_current() = 0;
	virtual void swap_buffers() = 0;
	virtual int get_window_width() = 0;
	virtual int get_window_height() = 0;
	virtual bool initialize() = 0;
	virtual void set_use_vsync(bool use) = 0;
	virtual bool is_using_vsync() const = 0;
};

struct Video : public Module {
	virtual Vec2 get_screen_size() const = 0;
	virtual Vec2 get_view_size() const = 0;
	virtual void set_title(const char *title) = 0;
	virtual Vec2 move_pointer(const Vec2 &screen) = 0;
	virtual void show_pointer(bool enable) = 0;
	virtual ContextGL *create_the_gl_context(int version, Vec2 view) = 0;
	virtual bool provides_quit() = 0;
};

struct Keyboard : public Module {
	struct Handler {
		virtual void handle_keyboard_key(int gd_code, bool pressed, uint32_t unicode, bool echo) = 0;
	};
	virtual void set_handler(Handler *handler) = 0;
	virtual void get_modifier_state(InputModifierState &state) const = 0;
};

struct Mouse : public Module {
	enum Button {
		Left,
		Middle,
		Right,
		WheelUp,
		WheelDown
	};
	struct Handler {
		virtual void handle_mouse_button(Button button, bool pressed) = 0;
		virtual void handle_mouse_motion(Vec2 pos) = 0;
	};
	virtual Vec2 get_pos() const = 0;
	virtual void set_size(Vec2 size) = 0;
	virtual void set_handler(Handler *handler) = 0;
};

struct Env {
	Video *video;
	Keyboard *keyboard;
	Mouse *mouse;
};

struct EnvProbe : public Module {
	virtual void probe_env(Env *env) = 0;
};

struct Runnable : public Module {
	virtual void setup_env(Env *env) = 0;
	virtual void run_() = 0;
	virtual int get_exit_code_() = 0;
};

} // namespace frt

#ifdef FRT_MOCK_GODOT_INPUT_MODIFIER_STATE

struct InputModifierState {
	bool shift;
	bool alt;
	bool control;
	bool meta;
};

#endif // FRT_MOCK_GODOT_INPUT_MODIFIER_STATE

#ifdef FRT_TEST
#define FRT_GLES_VERSION 2
#else
#define FRT_GLES_VERSION 2
#endif // FRT_TEST

#endif // FRT_H