/*
 * demoscene-eo, an ASCII art demoscene written as a gift for Emmanuel Otton retirement 
 * Copyright (C) 2019  Ludovic Pouzenc <ludovic@pouzenc.fr>
 *  
 * This file is part of demoscene-eo.
 *
 *  demoscene-eo is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  demoscene-eo 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 demoscene-eo.  If not, see <http://www.gnu.org/licenses/>
 */
#include "main.h"
#include "scene00.h"
#include "scene01.h"
#include "scene02.h"
#define SCENE_COUNT 3
#define SCENE_NEXT do { scene = (scene+1)%SCENE_COUNT; } while(0)

#define EXPR_MIN_SIZE ( ge.w<80 || ge.h<25 )
#define TEXT_MIN_SIZE "80x25"

int main(void)
{
	int done=0, lastscene=-1, scene=0, paused=0, res;
	Uint32 sdl_win_flags = SDL_WINDOW_OPENGL;
	graphical_env_t ge;
	scene00_env_t s00e;
	scene01_env_t s01e;
	scene02_env_t s02e;

	caca_event_t caca_ev;
	SDL_RendererInfo renderer_info;
	SDL_Event sdl_ev;

	// Initialize libcaca (http://caca.zoy.org/doxygen/libcaca/caca_8h.html)
	ge.dp = caca_create_display(NULL);
	if(!ge.dp) return 1;
	caca_set_display_title(ge.dp, "demoscene-eo");
	caca_set_display_time(ge.dp, 16666); // 1e6µs/60 = 16666.66... It is ~60 fps
	ge.cv = caca_get_canvas(ge.dp);

	ge.d = caca_create_dither(32, 256, 256, 256*4, 0x00ff0000, 0x0000ff00, 0x000000ff, 0);
	if ( !ge.d ) return 2;
	caca_set_dither_color(ge.d, "16");

	// Initialize SDL (http://wiki.libsdl.org/SDL_CreateWindowAndRenderer)
	// Useful snippet : https://gist.github.com/koute/7391344
	res = SDL_Init(SDL_INIT_VIDEO);
	if ( res == -1) return 3;
	SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
	SDL_GL_SetAttribute( SDL_GL_ACCELERATED_VISUAL, 1 );
	SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 8 );
	SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 8 );
	SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 8 );
	SDL_GL_SetAttribute( SDL_GL_ALPHA_SIZE, 8 );
	SDL_GL_SetAttribute( SDL_GL_CONTEXT_MAJOR_VERSION, 3 );
	SDL_GL_SetAttribute( SDL_GL_CONTEXT_MINOR_VERSION, 2 );
	SDL_GL_SetAttribute( SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE );	

	//XXX for final version, consider sdl_win_flags |= SDL_WINDOW_HIDDEN;
	res = SDL_CreateWindowAndRenderer(256, 256, sdl_win_flags, &ge.sdl_win, &ge.sdl_rndr);
	if ( res == -1) return 4;
	SDL_SetWindowTitle(ge.sdl_win, "SDL/GL debug");
	res = SDL_GetRendererInfo(ge.sdl_rndr, &renderer_info);
	if ( res < 0 ) return 5;
	if ( !(renderer_info.flags & SDL_RENDERER_ACCELERATED)) return 6;
	if ( !(renderer_info.flags & SDL_RENDERER_TARGETTEXTURE)) return 7;
	ge.sdl_target = SDL_CreateTexture(ge.sdl_rndr, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, 256, 256);
	if ( ge.sdl_target == NULL ) return 9;
	ge.raw_target = malloc(256*256*4);
	if ( ge.raw_target == NULL ) return 10;

	// Initialize OpenGL
	ge.gl_ctx = SDL_GL_CreateContext(ge.sdl_win);
	if ( ge.gl_ctx == NULL ) return 11;

	// Main libcaca loop for caca window (OpenGL could be used in sceneN_next())
	ge.framecount=0;
	do {
		// Check canvas size at every frame because of unreliable CACA_EVENT_RESIZE
		ge.w = caca_get_canvas_width(ge.cv);
		ge.h = caca_get_canvas_height(ge.cv);
		if ( EXPR_MIN_SIZE ) {
			caca_set_color_ansi(ge.cv, CACA_BLACK, CACA_WHITE);
			caca_put_str(ge.cv, 0, 0, "Need a minimum of " TEXT_MIN_SIZE " terminal");
		} else if (!paused) {
			// init / free if scene transition
			if ( lastscene != scene ) {
				switch(lastscene) {
					case 0: scene00_free(&ge, &s00e); break;
					case 1: scene01_free(&ge, &s01e); break;
					case 2: scene02_free(&ge, &s02e); break;
				}
			}
			while ( lastscene != scene ) {
				ge.sdl_ticks = SDL_GetTicks();
				ge.sc_framecount = 0;
				switch(scene) {
					case 0: res = scene00_init(&ge, &s00e); break;
					case 1: res = scene01_init(&ge, &s01e); break;
					case 2: res = scene02_init(&ge, &s02e); break;
				}
				// If scene init fail, skip to the next one
				if (res) SCENE_NEXT; else lastscene = scene;
			}
			// Compute current scene0 frame
			ge.sdl_ticks = SDL_GetTicks(); // This value wraps if the program runs for more than ~49 days
			switch(scene) {
				case 0: res = scene00_next(&ge, &s00e); break;
				case 1: res = scene01_next(&ge, &s01e); break;
				case 2: res = scene02_next(&ge, &s02e); break;
			}
			if (res) SCENE_NEXT;
			ge.framecount++;
			ge.sc_framecount++;
		}
		// Display it
		caca_refresh_display(ge.dp); // Auto framerate limiting (see caca_set_display_time())

		// Event handling for the libcaca "window" (depending on CACA_DRIVER env variable)
		if ( caca_get_event(ge.dp, CACA_EVENT_KEY_PRESS|CACA_EVENT_RESIZE|CACA_EVENT_QUIT, &caca_ev, 0) ) {
			switch(caca_get_event_type(&caca_ev)) {
					case CACA_EVENT_QUIT:
						done = 1;
						break;
					case CACA_EVENT_KEY_PRESS:
						switch(caca_get_event_key_ch(&caca_ev)) {
							case 'q': done = 1; break;
							case 'p': paused = !paused; break;
							case CACA_KEY_ESCAPE: SCENE_NEXT; break;
						}
						break;
						/* On Debian 10, no CACA_EVENT_RESIZE when using x11 CACA_DRIVER
					case CACA_EVENT_RESIZE:
						w = caca_get_event_resize_width(&caca_ev);
						h = caca_get_event_resize_height(&caca_ev);
						small = EXPR_MIN_SIZE;
						break;
						*/
					default:
						break;
			}
		}
		// Event handling for the SDL/GL window (debug purposes)
		while(SDL_PollEvent(&sdl_ev)) {
			switch(sdl_ev.type) {
				case SDL_QUIT:
					done = 1;
					break;
				case SDL_KEYDOWN:
					switch(sdl_ev.key.keysym.sym) {
						case SDLK_q: done = 1; break;
						case SDLK_p: paused = !paused; break;
						case SDLK_ESCAPE: SCENE_NEXT; break;
					}
					break;
				default:
					break;
			}
		}
	} while(!done);

	caca_free_dither(ge.d);
	caca_free_display(ge.dp);
	ge.cv=NULL;
	SDL_GL_DeleteContext(ge.gl_ctx);
	SDL_DestroyTexture(ge.sdl_target);
	SDL_DestroyRenderer(ge.sdl_rndr);
	SDL_DestroyWindow(ge.sdl_win);
	SDL_Quit();
	return 0;
}