/*
 * Copyright 2003-2015 (C) Raster Software Vigo (Sergio Costas)
 * This file is part of FBZX
 *
 * FBZX 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.
 *
 * FBZX 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, see <http://www.gnu.org/licenses/>.
 *
 */

#include <string.h>

#include "llscreen.hh"
#include "font.h"
#include "osd.hh"
#include <stdio.h>
#include <time.h>

LLScreen *llscreen = NULL;

LLScreen::LLScreen(int16_t resx, int16_t resy, uint8_t depth, bool fullscreen, bool dblbuffer, bool hwsurface, bool setres) {
	int retorno, bucle, valores;

	this->setres          = setres;
	this->fullscreen      = fullscreen;
	this->rotate          = false;
	this->cheight         = (charset.max_top > charset.height_maxtop) ? charset.max_top : charset.height_maxtop;
	this->lines_in_screen = 480 / this->cheight;

	retorno = SDL_Init(SDL_INIT_VIDEO);
	if (retorno != 0) {
		printf("Can't initialize SDL library. Exiting\n");
		exit(1);
	}

	if (SDL_InitSubSystem(SDL_INIT_JOYSTICK)) {
		this->joystick = false;
		printf("Can't initialize JoyStick subsystem\n");
	} else {
		printf("JoyStick subsystem initialized\n");
		this->joystick = true;
		if (SDL_NumJoysticks() > 0) {
			// Open joystick
			for (bucle = 0; bucle < SDL_NumJoysticks(); bucle++) {
				if (NULL == SDL_JoystickOpen(bucle)) {
					printf("Can't open joystick %d\n", bucle);
				}
			}
		}
	}

	// screen initialization
	valores = 0;
	if (fullscreen) {
		valores |= SDL_WINDOW_FULLSCREEN_DESKTOP;
	}

	valores |= SDL_SWSURFACE;

	this->sdlwindow = SDL_CreateWindow("FBZX",
	                                   SDL_WINDOWPOS_UNDEFINED,
	                                   SDL_WINDOWPOS_UNDEFINED, resx, resy, valores);
	this->sdlrenderer = SDL_CreateRenderer(this->sdlwindow, -1, 0);
	if (this->sdlrenderer == NULL) {
		printf("Can't create SDL Renderer. Exiting\n");
		exit(1);
	}

	SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear");
	SDL_RenderSetLogicalSize(this->sdlrenderer, resx, resy);

	this->llscreen = SDL_CreateTexture(this->sdlrenderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, resx, resy);

	this->set_mouse();
	this->width  = resx;
	this->height = resy;
	this->memory = (uint32_t *) malloc(resx * resy * sizeof(uint32_t));

	// we filter all the events, except keyboard events

	// SDL_EventState(SDL_ACTIVEEVENT, SDL_IGNORE);
	SDL_EventState(SDL_MOUSEMOTION, SDL_ENABLE);
	SDL_EventState(SDL_MOUSEBUTTONDOWN, SDL_ENABLE);
	SDL_EventState(SDL_MOUSEBUTTONUP, SDL_ENABLE);
	SDL_EventState(SDL_JOYAXISMOTION, SDL_ENABLE);
	SDL_EventState(SDL_JOYBALLMOTION, SDL_ENABLE);
	SDL_EventState(SDL_JOYHATMOTION, SDL_ENABLE);
	SDL_EventState(SDL_JOYBUTTONDOWN, SDL_ENABLE);
	SDL_EventState(SDL_JOYBUTTONUP, SDL_ENABLE);
	SDL_EventState(SDL_QUIT, SDL_ENABLE);
	SDL_EventState(SDL_SYSWMEVENT, SDL_IGNORE);
	// SDL_EventState(SDL_VIDEORESIZE, SDL_IGNORE);
	SDL_EventState(SDL_USEREVENT, SDL_IGNORE);

	SDL_ShowCursor(SDL_DISABLE);

	printf("Locking screen\n");
}

ifstream *LLScreen::myfopen(string filename, ios_base::openmode mode) {
	char      tmp[4096];
	ifstream *fichero;

	fichero = new ifstream(filename.c_str(), mode);
	if (fichero->is_open()) {
		return (fichero);
	}
	delete(fichero);
	sprintf(tmp, "/usr/share/%s", filename.c_str());
	fichero = new ifstream(tmp, mode);
	if (fichero->is_open()) {
		return (fichero);
	}
	delete(fichero);
	sprintf(tmp, "/usr/local/share/%s", filename.c_str());
	fichero = new ifstream(tmp, mode);
	if (fichero->is_open()) {
		return (fichero);
	}
	delete(fichero);
	return NULL;
}

LLScreen::~LLScreen() {
	SDL_Quit();
}

void LLScreen::do_flip() {
	SDL_UpdateTexture(this->llscreen, NULL, this->memory, this->width * sizeof(Uint32));
	SDL_RenderClear(this->sdlrenderer);
	SDL_RenderCopy(this->sdlrenderer, this->llscreen, NULL, NULL);
	SDL_RenderPresent(this->sdlrenderer);
}

void LLScreen::paint_one_pixel(uint8_t value, uint32_t *address) {
	uint32_t *colour = (uint32_t *) (this->colors + value);
	*address = *colour;
}

void LLScreen::set_palete_entry(uint8_t entry, uint8_t V, bool bw) {
	uint32_t r, g, b, Value, max;

	Value = (uint32_t) V;

	if (entry >= 16) {
		this->ulaplus_palete[entry - 16] = Value;
		r = ((Value << 3) & 0xE0) + ((Value) & 0x1C) + ((Value >> 3) & 0x03);
		g = (Value & 0xE0) + ((Value >> 3) & 0x1C) + ((Value >> 6) & 0x03);
		b = ((Value << 6) & 0xC0) + ((Value << 4) & 0x30) + ((Value << 2) & 0x0C) + ((Value) & 0x03);
	} else {
		max = (V & 0x08) ? 0x000000FF : 0x000000C0;
		r   = (V & 0x02) ? max : 0;
		g   = (V & 0x04) ? max : 0;
		b   = (V & 0x01) ? max : 0;
	}

	if (bw) {
		uint32_t final;
		final = ((r * 30) + (g * 59) + (b * 11)) / 100;
		r = g = b = final;
	}
	// Color mode

	r <<= 16;
	r  &= 0x00FF0000;
	g <<= 8;
	g  &= 0x0000FF00;
	b  &= 0x000000FF;

	colors[entry] = 0xFF000000 | r | g | b;
}

uint8_t LLScreen::get_palete_entry(uint8_t entry) {
	return (this->ulaplus_palete[entry]);
}

void LLScreen::fullscreen_switch() {
	if (this->fullscreen) {
		this->fullscreen = false;
		SDL_SetWindowFullscreen(this->sdlwindow, 0);
	} else {
		this->fullscreen = true;
		if (this->setres) {
			SDL_SetWindowFullscreen(this->sdlwindow, SDL_WINDOW_FULLSCREEN);
		} else {
			SDL_SetWindowFullscreen(this->sdlwindow, SDL_WINDOW_FULLSCREEN_DESKTOP);
		}
	}
	this->set_mouse();
}

void LLScreen::set_mouse() {
	if (this->fullscreen) {
		SDL_SetWindowGrab(this->sdlwindow, SDL_TRUE);
	} else {
		SDL_SetWindowGrab(this->sdlwindow, SDL_FALSE);
	}
}

void LLScreen::set_paletes(bool bw) {
	unsigned int c;

	for (c = 0; c < 80; c++) {
		if (c < 16) {
			this->set_palete_entry(c, c, bw);
		} else {
			set_palete_entry(c, this->ulaplus_palete[c - 16], bw);
		}
	}
}

// prints the ASCII character CHARAC in the framebuffer MEMO, at X,Y with ink color COLOR and paper color BACK, asuming that the screen width is WIDTH

uint8_t LLScreen::printchar(uint8_t carac, int16_t x, int16_t y, uint8_t color, uint8_t back) {
	int       bucle1, bucle2, offset;
	uint32_t *lugar, *lugar2;
	uint8_t   width;
	uint8_t   width2;
	uint8_t   height;
	int8_t    top;
	uint8_t * counter;

	carac -= 32;

	offset  = charset.offsets[carac];
	counter = charset.data + offset;
	width   = *(counter++);
	height  = *(counter++);
	counter++;
	top      = *counter;
	counter += 3;

	// y+=charset.max_top;
	lugar = this->memory + (y * this->width + x);

	if (width < MIN_WIDTH) {
		width2 = MIN_WIDTH;
	} else {
		width2 = width + 1;
	}

	for (bucle1 = 0; bucle1 < (int) this->cheight; bucle1++) {
		lugar2 = lugar;
		if (bucle1 + y >= 480) {
			break;
		}
		for (bucle2 = 0; bucle2 < width2; bucle2++) {
			if ((bucle2 < width) && (bucle1 >= (charset.max_top - top - charset.min_top - 1) && (bucle1 < (charset.max_top - top + height - charset.min_top - 1)) && ((*(counter++)) > 127))) {
				paint_one_pixel(color, lugar2);
			} else {
				paint_one_pixel(back, lugar2);
			}
			lugar2++;
		}
		lugar += this->width;
	}
	return width2;
}

void LLScreen::do_screenshot(void) {
    int width, height;
	char filename[256];
    SDL_QueryTexture(this->llscreen, NULL, NULL, &width, &height);
    SDL_Surface* surface = SDL_CreateRGBSurface(0, width, height, 32, 0, 0, 0, 0);
    SDL_RenderReadPixels(this->sdlrenderer, NULL, surface->format->format, surface->pixels, surface->pitch);
	time_t t = time(NULL);
  	struct tm tm = *localtime(&t);
	sprintf(filename, "fbzx_screenshot_%d_%02d_%02d_%02d_%02d_%02d.bmp", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
    SDL_SaveBMP(surface, filename);
	osd->set_message("Done screenshot", 2000);
}

void LLScreen::paint_picture(string filename) {
	ifstream *fichero;
	int       bucle1, bucle2;
	uint8_t   valor;
	uint32_t *buffer, *buffer2;

	buffer = this->memory;

	this->clear_screen();
	fichero = myfopen(filename, ios::in | ios::binary);
	if (fichero == NULL) {
		osd->set_message("Keymap picture not found", 2000);
		return;
	}
	if (!this->rotate) {
		for (bucle1 = 0; bucle1 < 344; bucle1++) {
			for (bucle2 = 0; bucle2 < 640; bucle2++) {
				fichero->read((char *) &valor, 1);
				paint_one_pixel(valor, buffer);
				buffer++;
			}
		}
	} else {
		buffer += 479;
		for (bucle1 = 0; bucle1 < 344; bucle1++) {
			buffer2 = buffer;
			for (bucle2 = 0; bucle2 < 640; bucle2++) {
				fichero->read((char *) &valor, 1);
				paint_one_pixel(valor, buffer);
				buffer += 480;
			}
			buffer = buffer2 - 1;
		}
	}
	fichero->close();
	delete fichero;
}

void LLScreen::clear_screen() {
	unsigned int bucle;
	uint32_t *   buffer;

	buffer = this->memory;

	for (bucle = 0; bucle < ((this->width) * (this->height)); bucle++) {
		*(buffer++) = 0;
	}
}

// prints the string CADENA in X,Y (centered if X=-1), with colors COLOR and BACK

void LLScreen::print_string(string o_cadena, int16_t x, float y, uint8_t ink, uint8_t paper) {
	unsigned int   length;
	int            ncarac, bucle, xx;
	uint16_t       xxx, yyy;
	unsigned char *cadena = (unsigned char *) o_cadena.c_str();
	unsigned char *str2;

	length = 0;
	ncarac = 0;
	for (str2 = cadena; *str2; str2++) {
		uint8_t  c;
		uint8_t  l;
		uint32_t offset;

		c = *str2;
		if (c >= ' ') {
			ncarac++;
			offset = charset.offsets[c - 32];
			l      = *(charset.data + offset);
			if (l < MIN_WIDTH) {
				l = MIN_WIDTH;
			} else {
				l++;
			}
			length += l;
		}
	}

	if (length > this->width) {
		if (x >= 0) {
			xx = x;
		} else {
			xx = 0;
		}
	} else {
		if (x == -1) { // we want it centered
			xx = (this->width / 2) - (length / 2);
		} else {
			xx = x;
		}
	}

	xxx = xx;
	if (y < 0) {
		yyy = 480 + y * this->cheight;
	} else {
		yyy = y * this->cheight;
	}
	str2 = cadena;
	for (bucle = 0; bucle < ncarac; bucle++) {
		uint8_t increment;
		while ((*str2) < ' ') {
			if ((*str2) == 1) {
				ink   = *(str2 + 1);
				str2 += 2;
				continue;
			}
			if (*str2 == 2) {
				paper = *(str2 + 1);
				str2 += 2;
				continue;
			}
			printf("Error de cadena %d %s\n", *str2, cadena);
			str2++;
		}
		increment = this->printchar(*str2, xxx, yyy, ink, paper);
		xxx      += increment;
		if (xxx >= width - charset.max_w) {
			xxx  = 0;
			yyy += this->cheight;
		}
		str2++;
	}
}
