#include <nds.h>
#include <stdio.h>
#include <vector>
#include <deque>
#include <array>
#include <consts.h>
#include <game_state.h>
#include <game_logic.h>
#include <game_render.h>
#include <game_audio.h>
#include <game_input.h>

#include "tilemap.h"

using namespace std;

volatile uint8_t frame = 0;
volatile bool internals = false;

GameState gameState;

volatile uint16_t &ticks = gameState.ticks;
volatile uint16_t &snakelength = gameState.snakelength;
/***
 * 0 = up
 * 1 = right
 * 2 = down
 * 3 = left
 ***/
volatile uint16_t &snakeDirection = gameState.snakeDirection;
volatile uint16_t &score = gameState.score;

bool &gameOver = gameState.gameOver;
bool &paused = gameState.paused;
bool &hardMode = gameState.hardMode;

std::deque<std::array<int, 3>> &snake = gameState.snake;
std::vector<std::array<int, 2>> &apples = gameState.apples;
std::vector<std::array<int, 2>> &bad_apples = gameState.bad_apples;

// 31x23
PrintConsole topScreen;
PrintConsole bottomScreen;

int sound;

AudioContext audioContext;

RenderAssets renderAssets;

const void * get_sprite_pointer(uint16 spriteIndex) {
	return &tilemapTiles[spriteIndex*16];
}

void setup() {
	// Set up screens
	
	videoSetMode(MODE_0_2D);
	videoSetModeSub(MODE_0_2D);
	
	vramSetBankC(VRAM_C_SUB_BG);
	vramSetBankD(VRAM_D_SUB_SPRITE);
	consoleInit(&topScreen, 3,BgType_Text4bpp, BgSize_T_256x256, X_MAX, 0, true, true);
	consoleInit(&bottomScreen, 3,BgType_Text4bpp, BgSize_T_256x256, X_MAX, 0, false, true);
	oamInit(&oamSub, SpriteMapping_1D_32, false);

	renderAssets.topScreen = &topScreen;
	renderAssets.bottomScreen = &bottomScreen;

	renderAssets.gfx_segment = oamAllocateGfx(&oamSub, SpriteSize_8x8, SpriteColorFormat_256Color);
	renderAssets.gfx_segment_small = oamAllocateGfx(&oamSub, SpriteSize_8x8, SpriteColorFormat_256Color);
	renderAssets.gfx_head = oamAllocateGfx(&oamSub, SpriteSize_8x8, SpriteColorFormat_256Color);
	renderAssets.gfx_apple = oamAllocateGfx(&oamSub, SpriteSize_8x8, SpriteColorFormat_256Color);
	renderAssets.gfx_apple_rotten = oamAllocateGfx(&oamSub, SpriteSize_8x8, SpriteColorFormat_256Color);
	renderAssets.gfx_tail = oamAllocateGfx(&oamSub, SpriteSize_8x8, SpriteColorFormat_256Color);

	dmaCopy(get_sprite_pointer(1), renderAssets.gfx_segment, 64);
	dmaCopy(get_sprite_pointer(2), renderAssets.gfx_segment_small, 64);
	dmaCopy(get_sprite_pointer(3), renderAssets.gfx_head, 64);
	dmaCopy(get_sprite_pointer(5), renderAssets.gfx_apple, 64);
	dmaCopy(get_sprite_pointer(11), renderAssets.gfx_apple_rotten, 64);

	consoleSelect(&topScreen);
	

	dmaCopy(tilemapPal,SPRITE_PALETTE_SUB,tilemapPalLen);

	audio_setup(audioContext);

}
void handle_vblank() {
	frame++;
	consoleSelect(&topScreen);
	draw_stats(gameState, internals, audio_is_music_enabled(audioContext), frame);
	oamUpdate(&oamSub);
	//flashy_colors();
}

void do_tick() {
	if (!paused) {
		ticks++;
		if (!gameOver){
			const bool wasGameOver = gameOver;
			
			/*if (hardMode) {
				timerStop(0);
				timerStart(0, ClockDivider_1024, TIMER_FREQ_1024(ticks+5), do_tick);
			}*/
			
			update_snake(gameState);
			audio_play_plop(audioContext);
			check_collision_self(gameState);
			if (check_collision_apples(gameState)) {
				audio_play_pickup(audioContext);
			}
			if (check_collision_apples_rotten(gameState)) {
				audio_play_boom(audioContext);
			}
			spawn_apples(gameState);
			spawn_apples_rotten(gameState);
			if (!wasGameOver && gameOver) {
				audio_stop_music();
				audio_play_fail(audioContext);
			}
			blink_apples(gameState, renderAssets.gfx_apple);
			//consoleSelect(&bottomScreen);
			draw_game(gameState, renderAssets);
		} 
	}
}
	
void init_game() {
	
	if (hardMode) {
		timerStart(0, ClockDivider_1024, TIMER_FREQ_1024(9), do_tick);
		setBackdropColorSub(RGB15(10,5,5));
		score = 0;
		snakelength = START_LENGTH_HARD;
		snake = { {2,1,1}, {1,1,1} };
		apples.clear();
		bad_apples.clear();
		snakeDirection = 1;
		ticks = 0;
		gameOver=false;
		consoleSelect(&topScreen);
		consoleClear();
		consoleSelect(&bottomScreen);
		consoleClear();
		oamClear(&oamSub,0,0);
		audio_start_music(audioContext);
	} 
	else
	{
		timerStart(0, ClockDivider_1024, TIMER_FREQ_1024(5), do_tick);
		setBackdropColorSub(RGB15(5,5,5));
		score = 0;
		snakelength = START_LENGTH;
		snake = { {4,1,1}, {3,1,1}, {2,1,1}, {1,1,1} };
		snakeDirection = 1;
		ticks = 0;
		gameOver=false;
		consoleSelect(&topScreen);
		consoleClear();
		consoleSelect(&bottomScreen);
		consoleClear();
		oamClear(&oamSub,0,0);
		audio_start_music(audioContext);
	}
	
	
	 
}

int main(void) {

	// Set up VBlank handler
	irqSet(IRQ_VBLANK, handle_vblank);

	setup();
	init_game();

	// Start timer for game ticks

	InputContext inputContext;
	inputContext.state = &gameState;
	inputContext.renderAssets = &renderAssets;
	inputContext.audio = &audioContext;
	inputContext.internals = &internals;
	inputContext.topScreen = &topScreen;
	inputContext.init_game = init_game;

 
	while(1) {
		if (!handle_input_frame(inputContext)) {
			break;
		}
		
	}

	return 0;
}
