// SPDX-License-Identifier: MIT
//
// Copyright (c) 2009-2014 Cesar Rincon "NightFox"
//
// NightFox LIB - Funciones basicas y de Debug
// http://www.nightfoxandco.com/
// Version 


#ifdef BLOCKSDS
#define iprintf printf
#endif

// Includes devKitPro
#include <nds.h>

// Includes C
#include <stdio.h>
#include <string.h>
#include <unistd.h>

// Includes propios
#include "nf_basic.h"
#include "nf_defines.h"



// Define la variable global NF_ROOTFOLDER
char NF_ROOTFOLDER[64];



// Funcion NF_Error();
void NF_Error(u16 code, const char* text, u32 value) {

	consoleDemoInit();		// Inicializa la consola de texto
	consoleClear();			// Borra la pantalla
	setBrightness(3, 0);	// Restaura el brillo

	u32 n = 0;	// Variables de uso general

	// Captura el codigo de error
	switch (code) {

		case 101:	// Fichero no encontrado
			iprintf("File %s not found.\n", text);
			break;

		case 102:	// Memoria insuficiente
			iprintf("Out of memory.\n");
			iprintf("%d bytes\n", (int)value);
			iprintf("can't be allocated.\n");
			break;

		case 103:	// No quedan Slots libres
			iprintf("Out of %s slots.\n", text);
			iprintf("All %d slots are in use.\n", (int)value);
			break;

		case 104:	// Fondo no encontrado
			iprintf("Tiled Bg %s\n", text);
			iprintf("not found.\n");
			break;

		case 105:	// Fondo no creado
			iprintf("Background number %d\n", (int)value);
			iprintf("on screen %s is\n", text);
			iprintf("not created.\n");
			break;

		case 106:	// Fuera de rango
			iprintf("%s Id out\n", text);
			iprintf("of range (%d max).\n", (int)value);
			break;

		case 107:	// Insuficientes bloques contiguos en VRAM (Tiles)
			n = (int)((value * 16384) / 1024);
			iprintf("Can't allocate %d\n", (int)value);
			iprintf("blocks of tiles for\n");
			iprintf("%s background\n", text);
			iprintf("Free %dkb of VRAM or try to\n", (int)n);
			iprintf("reload all Bg's again\n");
			break;

		case 108:	// Insuficientes bloques contiguos en VRAM (Maps)
			n = (int)((value * 2048) / 1024);
			iprintf("Can't allocate %d\n", (int)value);
			iprintf("blocks of maps for\n");
			iprintf("%s background\n", text);
			iprintf("Free %dkb of VRAM or try to\n", (int)n);
			iprintf("reload all Bg's again\n");
			break;

		case 109:	// Id ocupada
			iprintf("%s Id.%d\n", text, (int)value);
			iprintf("is already in use.\n");
			break;

		case 110:	// Id no cargada
			iprintf("%s\n", text);
			iprintf("%d not loaded.\n", (int)value);
			break;

		case 111:	// Id no en VRAM
			iprintf("%s\n", text);
			iprintf("%d not in VRAM.\n", (int)value);
			break;

		case 112:	// Sprite no creado
			iprintf("Sprite number %d\n", (int)value);
			iprintf("on screen %s is\n", text);
			iprintf("not created.\n");
			break;

		case 113:	// Memoria VRAM insuficiente
			iprintf("Out of VRAM.\n");
			iprintf("%d bytes for %s\n", (int)value, text);
			iprintf("can't be allocated.\n");
			break;

		case 114:	// La capa de Texto no existe
			iprintf("Text layer on screen\n");
			iprintf("nº %d don't exist.\n", (int)value);
			break;

		case 115:	// Medidas del fondo no compatibles (no son multiplos de 256)
			iprintf("Tiled Bg %s\n", text);
			iprintf("has wrong size.\n");
			iprintf("Your bg sizes must be\n");
			iprintf("dividable by 256 pixels.\n");
			break;

		case 116:	// Archivo demasiado grande
			iprintf("File %s\n", text);
			iprintf("is too big.\n");
			iprintf("Max size for\n");
			iprintf("file is %dkb.\n", (int)(value >> 10));
			break;

		case 117:	// Medidas del fondo affine no compatibles (Solo se admiten 256x256 y 512x512)
			iprintf("Affine Bg %s\n", text);
			iprintf("has wrong size.\n");
			iprintf("Your bg sizes must be\n");
			iprintf("256x256 or 512x512 and\n");
			iprintf("with 256 tiles or less.\n");
			break;

		case 118:	// Medidas del fondo affine no compatibles (Solo se admiten 256x256 y 512x512)
			iprintf("Affine Bg %s\n", text);
			iprintf("only can be created\n");
			iprintf("on layers 2 or 3.\n");
			break;

		case 119:	// Tamaño de la textura ilegal.
			iprintf("Texture id.%d illegal size.\n", (int)value);
			iprintf("Only power of 2 sizes can\n");
			iprintf("be used (8 to 1024).\n");
			break;

		case 120:	// Tamaño de la Sprite ilegal.
			iprintf("Sprite id.%d illegal size.\n", (int)value);
			iprintf("8x8 Sprites can't be used\n");
			iprintf("in 1D_128 mode.\n");
			break;

	}

	iprintf("Error code %d.\n", (int)code);		// Imprime el codigo de error

	// Deten la ejecucion del programa
	while (1) {
		swiWaitForVBlank();
	}

}



// Funcion NF_SetRootFolder();
void NF_SetRootFolder(const char* folder) {

	if (strcmp(folder, "NITROFS") == 0) {	// Si se debe iniciar el modo NitroFS y FAT

		// Define NitroFS como la carpeta inicial
		snprintf(NF_ROOTFOLDER, sizeof(NF_ROOTFOLDER), "%s", "");
		// Check if NitroFS exists.
		// NitroFS must be mounted beforehand for this to work.
		if(access("nitro:/", F_OK) == 0) {
			// NitroFS ok
			// Si es correcto, cambia al ROOT del NitroFS
			chdir("nitro:/");
		} else {
			// Fallo. Deten el programa
			consoleDemoInit();	// Inicializa la consola de texto
			if (NF_GetLanguage() == 5) {
				iprintf("Error iniciando NitroFS.\n");
				iprintf("Programa detenido.\n\n");
				iprintf("Verifica que tu flashcard\n");
				iprintf("es compatible con Argv.\n");
				iprintf("Si no lo es, intenta usar el\n");
				iprintf("Homebrew Menu para ejecutarla.\n\n");
			} else {
				iprintf("NitroFS Init Error.\n");
				iprintf("Abnormal termination.\n\n");
				iprintf("Check if your flashcard is\n");
				iprintf("Argv compatible.\n");
				iprintf("If not, try to launch the ROM\n");
				iprintf("using the Homebrew Menu.\n\n");
			}
			iprintf("http://sourceforge.net/projects/devkitpro/files/hbmenu/");
			// Bucle infinito. Fin del programa
			while(1) {
				swiWaitForVBlank();
			}
		}

	} else {	// Si se debe iniciar solo la FAT

		// Define la carpeta inicial de la FAT
		snprintf(NF_ROOTFOLDER, sizeof(NF_ROOTFOLDER), "%s", folder);

		// Check where the NDS is running from
		bool init_ok = false;

		// First, try to detect the drive where the NDS ROM is. If argv has been
		// provided by the loader, it will contain the drive and the path of the
		// ROM.
		if (__system_argv->argvMagic == ARGV_MAGIC && __system_argv->argc >= 1) {
			if (strncmp(__system_argv->argv[0], "fat:", 4) == 0) {
				// If argv starts by "fat:", try to setup "fat:" as root
				if (access("fat:/", F_OK) == 0) {
					chdir("fat:/");
					init_ok = true;
				}
			} else if (strncmp(__system_argv->argv[0], "sd:", 3) == 0) {
				// If argv starts by "sd:", try to setup "sd:" as root
				if (access("sd:/", F_OK) == 0) {
					chdir("sd:/");
					init_ok = true;
				}
			}
		}

		// Second, try to bruteforce the detection. Check if there is access to
		// the SD card of the DSi first. If not, try with DLDI.
		if (!init_ok) {
			if (access("sd:/", F_OK) == 0) {
				chdir("sd:/");
				init_ok = true;
			} else if (access("fat:/", F_OK) == 0) {
				chdir("fat:/");
				init_ok = true;
			}
		}

		// If that didn't work, give up.
		if (!init_ok) {
			// Fallo. Deten el programa
			consoleDemoInit();	// Inicializa la consola de texto
			if (NF_GetLanguage() == 5) {
				iprintf("Error iniciando FAT.\n");
				iprintf("Programa detenido.\n\n");
				iprintf("Verifica que tu flashcard es\n");
				iprintf("compatible con DLDI y la ROM\n");
				iprintf("este parcheada correctamente.\n");
			} else {
				iprintf("FAT Init Error.\n");
				iprintf("Abnormal termination.\n\n");
				iprintf("Check if your flashcard is\n");
				iprintf("DLDI compatible and the ROM\n");
				iprintf("is correctly patched.\n");
			}
			// Bucle infinito. Fin del programa
			while(1) {
				swiWaitForVBlank();
			}
		}
	}
}




// Funcion NF_DmaMemCopy();
void NF_DmaMemCopy(void* destination, const void* source, u32 size) {

	// Funcion basada en la documentacion de Coranac
	// http://www.coranac.com/2009/05/dma-vs-arm9-fight/

	// Datos de origen y destino
	u32 src = (u32)source;
	u32 dst = (u32)destination;

	// Verifica si los datos estan correctamente alineados
	if ((src | dst) & 1) {

		// No estan alineados para un DMA copy
		// Se realiza un copia con el memcpy();
		memcpy(destination, source, size);

	} else {

		// Estan alineados correctamente

		// Espera a que el canal 3 de DMA este libre
		while (dmaBusy(3));

		// Manda el cache a la memoria
		DC_FlushRange(source, size);

		// Dependiendo de la alineacion de datos, selecciona el metodo de copia
		if ((src | dst | size) & 3) {
			// Copia de 16 bits
			 dmaCopyHalfWords(3, source, destination, size);
		} else {
			// Copia de 32 bits
			dmaCopyWords(3, source, destination, size);
		}

		// Evita que el destino sea almacenado en cache
		DC_InvalidateRange(destination, size);

	}

}



// Funcion NF_GetLanguage();
u8 NF_GetLanguage(void) {

	// Asegurate que el valor devuelto corresponde a los
	// contenidos en los BITS 0, 1 y 2 de la direccion de memoria
	return (NF_UDATA_LANG & 0x07);

}
