/*

Author: Enira (robbert.lambrechts@gmail.com)
Release Date:

ToDo: 
-Optimize:Scrollbar
-Optimize:Static cursor when scrolling
-Save, Load functions

Further implementations:
-Config file
-Using costum graffiti defined by user and saved to configuration file
-Auto training of grafitti recognition system to optimize character recognition
-Adding numbers to grafitti

*/

// Includes
#include <PA9.h>       // Include for PA_Lib
#include <string.h> 
#include <stdlib.h>
#include <stdio.h>

#include "gfx/all_gfx.h"
#include "gfx/all_gfx.c"

//Defines
#define MAIN 0
#define SELECT 1

//Prototyping
void Note_InitScreen();
void Note_InitFAT();
void Note_InitSRAM();
void Note_About();
void Note_SetTitle(char*);
void Note_RemoveBreak(s32*);
void Note_CopyText(char**,char*,s32*);
u8 Note_Calibrate();
s32 Note_GuessChar(u8,u8,s32);
s32 Note_ScrollLine(u8,s32,char*);

void addchar(char*,char);
void removechar(char*);

void Note_InitSRAM()
{
	char verify[10];
	
	PA_LoadData(1,verify,10);
	if (!PA_CompareText(verify, "eDSNotepad"))
	{
		PA_Init16cBg(1, 0);
		PA_SetBgColor(1, 0xDEAD);
		PA_SetBgColor(0, 0xDEAD);
		PA_16cText(1, 20, 50, 255, 192, "SRAM:\nCan I format the SRAM to save\nthe text file?\n(Press Start to do so)", 1, 2, 1000);
		while (!Pad.Newpress.Start) { // Now we wait for A to be pressed...
			PA_WaitForVBL();
		}
		PA_SaveData(1, (void*)Blank, 10013);
		PA_SaveString(1, "eDSNotepad");
		PA_16cClearZone(1,0,0,255,192); 	
		PA_16cText(1, 20, 50, 255, 192, "SRAM:\nPlease reset your DS now.\nThe file has been created.\n", 1, 2, 1000);
		while(1)
		{
			PA_WaitForVBL();		
		}
	}
}

void Debug(char* wrapper)
{	
	PA_Clear8bitBg(0);
	PA_SmartText(0, 0, 20, 255, 40, wrapper, 2, 1, 1, 100);
}

int main(int argc, char ** argv)
{
	s32 nletter = 0; 
	s32 curpos = 0;
	s32 scrollpos = 0;
	s32 breakpoint[2]={0,0};
	s32 i;
	char letter = 0; 
	char text[10002]=""; 
	char filename[12]="UNTITLED.TXT";
	char *clipboard = NULL;
	char *so = NULL;
	u8 status = MAIN;
	//char wrapper[200];
	
	PA_Init();
	PA_InitVBL();
	
	//Note_InitFAT();											//Initialize FAT(Further refernece)
	Note_InitSRAM();
	
	Note_InitScreen();										//Initialize screens
	PA_UsePAGraffiti(Note_Calibrate()); 				//Init custom grafitti chars
		
	Note_SetTitle(filename);								//Set Title
	
	while(1)
	{
		if(status == SELECT)
		{
			if(Stylus.Newpress)								//Set cursor
			{
				curpos = Note_GuessChar(Stylus.X,Stylus.Y,nletter);
			}
			else if(Pad.Newpress.X)							//Switch mode
			{
				status = MAIN;
				PA_SwitchScreens();
			}
		}
		else if(status == MAIN)								//Switch mode
		{
			if(Pad.Newpress.X)
			{
				status = SELECT;
				PA_SwitchScreens();
			}
			
			if(PA_MoveSprite(0))								//If the scrollbar gets moved
			{
				if(PA_MovedSprite.Y<3)
					PA_MovedSprite.Y = 3;
				if(PA_MovedSprite.Y>174)
					PA_MovedSprite.Y = 174;
					
				PA_SetSpriteXY(0, 0, 211, PA_MovedSprite.Y);
				curpos=0; //ToDo: optimize
				
				scrollpos=Note_ScrollLine((PA_MovedSprite.Y-3)*100/171,nletter,text);
			}
			
			if(PA_SpriteTouched(1))							//New file
			{
				nletter = 0;curpos = 0;scrollpos = 0;
				breakpoint[0] = 0;breakpoint[1] = 0;
				strcpy(filename,"UNTITLED.TXT");strcpy(text,"");
			}
			if(PA_SpriteTouched(2))							//Open file
			{
				so = malloc(10012*sizeof(char));
				PA_LoadString(1, so);
				strcpy(text,so+11);
				
				nletter = strlen(text);
				curpos = 0;scrollpos = 0;
				breakpoint[0] = 0;breakpoint[1] = 0;
				free(so);
			}
			if(PA_SpriteTouched(3))							//Save file
			{
				so = malloc(10012*sizeof(char));
				strcpy(so,"eDSNotepad");
				strcpy(so+11,text);
				PA_SaveString(1, so);
				free(so);
			}
			if(PA_SpriteTouched(4))							//About
			{
				Note_About();
			}
			
			if(PA_SpriteTouched(5))							//Cut
			{
				if(breakpoint[1] && (breakpoint[1]-breakpoint[0]))
				{
					Note_CopyText(&clipboard,text,breakpoint);
					
					//Update curpos
					if(curpos+scrollpos > breakpoint[0] && curpos+scrollpos < breakpoint[1])
						curpos = breakpoint[0];
					else if(curpos+scrollpos > breakpoint[1])
						curpos = curpos - (breakpoint[1]-breakpoint[0]);
					
					//Remove selection from text: Broken
					for(i=breakpoint[0];i<breakpoint[1];i++)
					{
						if(breakpoint[0]+1 < nletter)
							removechar(&text[breakpoint[0]+1]);
						else
							text[nletter-1] = '\0';
							
						nletter--;
					}
					
					Note_RemoveBreak(breakpoint);
				}
			}
			if(PA_SpriteTouched(6))							//Copy
			{
				if(breakpoint[1] && (breakpoint[1]-breakpoint[0]))
				{
					Note_CopyText(&clipboard,text,breakpoint);
					Note_RemoveBreak(breakpoint);
				}
			}
			if(PA_SpriteTouched(7))							//Paste
			{
				if(clipboard)
				{
					for(i=0;i<strlen(clipboard);i++)
					{
						addchar(&text[scrollpos+curpos],clipboard[i]);
						nletter++;
						curpos++;
					}
					//Avoid flood of this button
					for(i=0;i<10;i++)
						PA_WaitForVBL();
				}
			}
			
			if(Stylus.Y > 97 && Stylus.X < 207)			//Keyboard
			{
				letter = PA_CheckKeyboard();
		
				if ((letter > 31 || letter == '\n') && nletter < 10000)
				{
					if(curpos+scrollpos != nletter)
						addchar(&text[curpos+scrollpos],letter);
					else
						text[scrollpos+curpos+1] = '\0';
				
					text[scrollpos+curpos] = letter;
					nletter++;
					curpos++;
				}
				else if ((letter == PA_BACKSPACE)&&nletter) 
				{
					if(curpos+scrollpos != nletter)
						removechar(&text[curpos+scrollpos]);
					else
						text[nletter-1] = '\0';
					
					nletter--;
					curpos--;
				}
			}
			
			else if(Stylus.Y < 97 && Stylus.X < 207)	//Grafitti
			{
				if(Stylus.Released) 
					PA_Clear8bitBg(0);
			
				PA_8bitDraw(0, 2);
			
				letter = PA_CheckLetter();
			
				if (((letter > 31 || letter == '\n') && letter != '\f') && nletter < 10000)
				{ 
					if(curpos != nletter)
						addchar(&text[curpos+scrollpos],letter);
					else
						text[curpos+1] = '\0';
				
					text[curpos] = letter;
					nletter++;
					curpos++;
				}
				else if ((letter == PA_BACKSPACE)&&nletter) 
				{
					if(curpos+scrollpos != nletter)
						removechar(&text[curpos+scrollpos]);
					else
						text[nletter-1] = '\0';
					
					nletter--;
					curpos--;
				}
				else if(letter == '\f') 					//If touppercase
				{
					text[scrollpos+curpos-1] = text[scrollpos+curpos-1]-32;
				}			
			}
		}
		
		//Breakpoints
		if(Pad.Newpress.Y)
		{
			if(breakpoint[0] && breakpoint[1])
			{
				Note_RemoveBreak(breakpoint);
			}
			else if(breakpoint[0] && !breakpoint[1])
			{
				breakpoint[1]=curpos+scrollpos;
				PA_SetSpriteXY(1, 2, PA_16cLetterPos.Letter[curpos].X, PA_16cLetterPos.Letter[curpos].Y);
					
				if(breakpoint[0] > breakpoint[1]) //1 will always be the biggest
				{
					i = breakpoint[1];
					breakpoint[1] = breakpoint[0];
					breakpoint[0] = i;
				}
			}
			else
			{
				breakpoint[0]=curpos+scrollpos;
				PA_SetSpriteXY(1, 1, PA_16cLetterPos.Letter[curpos].X, PA_16cLetterPos.Letter[curpos].Y);
			}
		}
			
		//D-Pad movement
		if(Pad.Newpress.Left && curpos)
			curpos--;
		if(Pad.Newpress.Right && curpos+scrollpos != nletter)
			curpos++;
		
		//Scroll a little bit if it goes out of bound		
		if(PA_16cLetterPos.Letter[curpos].Y >= 176)
		{
			do
			{
				scrollpos++;
				curpos--;
			}while(!(text[scrollpos] == ' ' || text[scrollpos] == '\n'));
		}
		
		PA_16cClearZone(1,7,20,248,184); 								//Clear text area
		PA_16cText(1,10,24,242,184,&text[scrollpos],10,2,1000); 	//Write text
			
		//Move cursor		
		PA_SetSpriteXY(1, 0, PA_16cLetterPos.Letter[curpos].X, PA_16cLetterPos.Letter[curpos].Y);
		PA_WaitForVBL();
	}	
	return 0;
}






//Funtions:
//--------------------------------------------------------------------------------------------------

void Note_InitFAT()
{
	if(!FAT_InitFiles()) // No FAT available :(
	{
		PA_Init16cBg(1, 0);
		PA_SetBgColor(1, 0xDEAD);
		PA_SetBgColor(0, 0xDEAD);
		PA_16cText(1, 20, 50, 255, 192, "Error:\nCouldn't load function FAT^InitFiles.\nPlease use a build compatible with\nthis type of flash card.", 1, 2, 1000);
		while(1);
	}
}

void Note_About()
{
	PA_ShowBg(1,1);
	while(!Stylus.Newpress)
		PA_WaitForVBL();
	PA_HideBg(1,1);
}

void Note_CopyText(char **clipboard,char *text,s32 breakpoint[2])
{	
	if(*clipboard)
		free(*clipboard);
	//Allocate memory
	*clipboard = malloc((breakpoint[1]-breakpoint[0]+1)*sizeof(char));
	//Copy
	strncpy(*clipboard,&text[breakpoint[0]],breakpoint[1]-breakpoint[0]);
	clipboard[breakpoint[1]-breakpoint[0]+1]='\0';

	//char wrapper[200];		
	//sprintf(wrapper,">>%s//%d,%d,%d",*clipboard,strlen(*clipboard),breakpoint[1],breakpoint[0]);
	//Debug(wrapper);
}

void Note_RemoveBreak(s32 *breakpoint)
{
	PA_SetSpriteXY(1,1,256,0);
	PA_SetSpriteXY(1,2,256,0);
	*breakpoint = 0;
	*(breakpoint+1) = 0;
}

s32 Note_ScrollLine(u8 percentage,s32 limit, char* text)
{
	s32 point;
	
	point=limit*percentage/100;
	//Scroll words and newlines only; you cannot scroll half words
	if(point)
		while(--point && !(*(text+point) == ' ' || *(text+point) == '\n'));
	
	if(point <= 0)
		point = 0;
	else
		point++;
	
	return point;
}

s32 Note_GuessChar(u8 x,u8 y, s32 boundry)
{
	s32 pos=0;
	
	//If clicked above the text; save some CPU cycles. Just point to the start position
	if(y < 20)
		return 0;
	
	//Searching Y position first, then X
	while(PA_16cLetterPos.Letter[++pos].Y + 9 < y);
	while(PA_16cLetterPos.Letter[pos++].X <= x);
	
	if(pos > boundry)
		return boundry;
	else
		return pos-1;
}

u8 Note_Calibrate()
{
	//AddShapes
	PA_RecoAddShape('\f', "@@AAAAAACGIIIII"); //'\f' is used as touppercase character
	
	return 1; //Future reference: read user defined shapes
}

void Note_InitScreen()
{
	//Screen moduses
	PA_Init8bitBg(0,0); //Load 8 bit
	PA_Init16cBg(1,2);  //Text
	
	//Palettes
	PA_LoadPal(PAL_BG0, background_Pal);
	PA_LoadPal(PAL_BG1, background_Pal);
	PA_DualLoadSpritePal(0, (void*)user_interface_Pal);
	PA_LoadSpritePal(0, 1, (void*)buttons_Pal); 
	PA_LoadSpritePal(0, 2, (void*)edit_Pal);
	
	//Backgrounds
	PA_LoadSimpleBg(0, 2, back_Tiles, back_Map, BG_256X256, 0, 1);
	PA_LoadSimpleBg(1, 3, top_Tiles, top_Map, BG_256X256, 0, 1);
	PA_LoadSimpleBg(1, 1, about_box_Tiles, about_box_Map, BG_256X256, 0, 1);
	PA_HideBg(1,1);
	
	//Keyboard
	PA_InitKeyboard(1); 
	PA_KeyboardIn(0, 96);
	PA_SetKeyboardColor(0, 1);
	
	//Sprites
	PA_CreateSprite(1, 0, (void*)cursor_Sprite, OBJ_SIZE_8X16, 1, 0, 0, 0);
	PA_CreateSprite(1, 1, (void*)break_Sprite, OBJ_SIZE_8X16, 1, 0, 255, 0);
	PA_CreateSprite(1, 2, (void*)break_Sprite, OBJ_SIZE_8X16, 1, 0, 255, 0);
	PA_CreateSprite(0, 0, (void*)slider_Sprite, OBJ_SIZE_16X16, 1, 0, 211, 3);
	PA_CreateSprite(0, 1, (void*)new_Sprite, OBJ_SIZE_16X16, 1, 1, 231, 7);
	PA_CreateSprite(0, 2, (void*)open_Sprite, OBJ_SIZE_16X16, 1, 1, 231, 26);
	PA_CreateSprite(0, 3, (void*)save_Sprite, OBJ_SIZE_16X16, 1, 1, 231, 45);
	PA_CreateSprite(0, 4, (void*)about_Sprite, OBJ_SIZE_16X16, 1, 1, 231, 64);
	PA_CreateSprite(0, 5, (void*)cut_Sprite, OBJ_SIZE_16X16, 1, 2, 231, 98);
	PA_CreateSprite(0, 6, (void*)copy_Sprite, OBJ_SIZE_16X16, 1, 2, 231, 117);
	PA_CreateSprite(0, 7, (void*)paste_Sprite, OBJ_SIZE_16X16, 1, 2, 231, 136);
}

void Note_SetTitle(char* title)
{
	//Set title and clear position first
	PA_16cClearZone(1,7,7,248,20);
	PA_16cText(1,10,10,255,192,title,10,2,12);
}


/*String functions
  ----------------
  (To be optimized) Not tested: usage of strcpy() instead of shifting characters
*/
void addchar(char *text, char letter)
{
	char temp;
	do
	{
		temp = *text;
		*text = letter;
		letter = temp;
	}while(*(text++));
}

void removechar(char *text)
{
	do
	{
		*(text-1) = *text;
	}while(*(text++));
}
//--------------------------------------------------------------------------------------------------
