#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <malloc.h>

#include "screen.h"

#define TEXT_COLOR 0x000000
#define TEXT_SIZE 16

#if 0
void debugOut(const char *fmt, ...)
{
	// Need to make room for the sprintf'd text
	char *out = (char *)memalign(32, 1024);
	
	// Build using sprintf
	va_list marker;
	va_start(marker,fmt);
	vsprintf(out,fmt,marker);
	va_end(marker);
	
	// Output to a debug file
	FILE *fp = fopen("fat3://debug.log", "a");
	fputs(out, fp);
	fclose(fp);
	
	// Free memory
	free(out);
}
#endif

static void exitMenu(void *data)
{
	// Callback for closing menu
	((Screen *)data)->closeMenu();
}

static void exitProgram(void *data)
{
	// Callback for closing program
	((Screen *)data)->exitNow();
}

static void toggleLight(void *data)
{
	// Callback for closing program
	((Screen *)data)->toggleFlashing();
}

static void moveUp(void *data)
{
	// Move screen
	int x = ((Settings *)data)->getOffsetX();
	int y = ((Settings *)data)->getOffsetY();
	
	y--;
	
	((Settings *)data)->setOffset(x, y);
}

static void moveDown(void *data)
{
	// Move screen
	int x = ((Settings *)data)->getOffsetX();
	int y = ((Settings *)data)->getOffsetY();
	
	y++;
	
	((Settings *)data)->setOffset(x, y);
}

static void moveLeft(void *data)
{
	// Move screen
	int x = ((Settings *)data)->getOffsetX();
	int y = ((Settings *)data)->getOffsetY();
	
	x--;
	
	((Settings *)data)->setOffset(x, y);
}

static void moveRight(void *data)
{
	// Move screen
	int x = ((Settings *)data)->getOffsetX();
	int y = ((Settings *)data)->getOffsetY();
	
	x++;
	
	((Settings *)data)->setOffset(x, y);
}

static void prevTheme(void *data)
{
	((Screen *)data)->setPrevTheme();
}

static void nextTheme(void *data)
{
	((Screen *)data)->setNextTheme();
}

void Screen::setPrevTheme()
{
	strcpy(menuThemeName, themes->getPrev(menuThemeName));
	setThemeCaptions();
}

void Screen::setNextTheme()
{
	strcpy(menuThemeName, themes->getNext(menuThemeName));
	setThemeCaptions();
}

void Screen::setThemeCaptions()
{
	// Set up caption
	themeLabel->setCaption(menuThemeName);
	
	// Previous button
	if(themes->getPrev(menuThemeName))
	{
		prev->setVisible(true);
		prev->setCaption(themes->getPrev(menuThemeName));
	}
	else
	{
		prev->setVisible(false);
	}
	
	// Next button
	if(themes->getNext(menuThemeName))
	{
		next->setVisible(true);
		next->setCaption(themes->getNext(menuThemeName));
	}
	else
	{
		next->setVisible(false);
	}
}

void Screen::exitNow()
{
	// Exit program
	settingsManager.saveSettings();
	shouldExit = true;
}

void Screen::closeMenu()
{
	// Figure out if we need to retheme...
	if(strcmp(menuThemeName, settingsManager.getTheme()) != 0)
	{	
		// yes!
		char currentDir[MAXPATHLEN];		
		
		FileList *fl = (FileList *)findWidget(WIDGET_FILELIST);
		fl->getDir(currentDir);
		
		LoadableImage *tImage = (LoadableImage *)findWidget(WIDGET_MENU);
		Rect tRect = tImage->getAbsoluteDimensions();
		
		bool replay = false;
		if(sound.getSoundState() == SOUND_PLAYING)
		{
			replay = true;
			sound.pauseSound();
		}
		
		// Set new theme
		settingsManager.setTheme(menuThemeName);
		
		// Init once everything is set
		initSkin();
		
		fl = (FileList *)findWidget(WIDGET_FILELIST);
		fl->populate(currentDir);
		
		tImage = (LoadableImage *)findWidget(WIDGET_MENU);		
		tImage->move(tRect.x, tRect.y);
		
		if(replay)
		{
			sound.playSound();
		}
	}
	
	settingsManager.saveSettings();
	
	// Close menu
	direction = -1;
	menuFinal += 1;
}

void Screen::toggleFlashing()
{
	// Toggle light
	sound.enableDiskLight(!sound.getDiskLightEnabled());
	
	// Set caption accordingly
	if(sound.getDiskLightEnabled())
	{
		wiilight->setCaption("On");
	}
	else
	{
		wiilight->setCaption("Off");
	}
}

Screen::Screen()
{	
	// Figure out available devices
	File tFile;
	tFile.enumerateInterfaces();
	
	// Don't exit yet
	shouldExit = false;
	menuOpen = false;
	
	// Don't display notice yet
	noticeFrame = 0;
	noticeOpen = false;
	
	// Ensure nothing on the drawlist
	drawList = NULL;
	
	// Initialize libwiisprite video
	mainWindow.InitVideo();
	
	// Ensure we have a new range
	GX_InvalidateTexAll();
	
	// Display loading label
	Label *lLabel = new Label();
	lLabel->move(0, NOTICE_POS);
	lLabel->resize(640, 30);
	lLabel->setFont("DejaVu");
	lLabel->setSize(20);
	lLabel->setColor(0xFFFFFF);
	lLabel->setCentered(true);
	lLabel->setCaption("Loading...");
	lLabel->draw();
	
	// Draw entire GX region
	mainWindow.Flush();
	
	delete lLabel;
	
	// Load list of themes
	themes = new Directory(DATAPATH "themes");
	
	// Load settings
	settingsManager.loadSettings();
	
	// Load default stuff
	initSkin();
}

Screen::~Screen()
{
	// Empty
}

void Screen::initSkin()
{	
	// Force all controls to delete
	while(drawList)
	{
		// Force remove
		removeWidget(drawList->uuid);
	}
	
	// Ensure unique id's are always available
	uidUnique = -1;
	lastUid = 0;
	
	// Remember theme name
	strncpy(menuThemeName, settingsManager.getTheme(), 127);
	menuThemeName[127] = 0;
	
	// Figure out pathname
	char pathName[MAXPATHLEN];
	sprintf(pathName, "%sthemes/%s/", DATAPATH, settingsManager.getTheme());
	
	// Generate theme name
	char fileName[MAXPATHLEN];
	strcpy(fileName, pathName);
	strcat(fileName, "theme.xml");
	
	// Load theme
	windowManager.loadTheme(fileName);
	
	// Initialize background color
	uint32_t bgHex;
	windowManager.getLongHex("Application.BGColor", &bgHex, 0xFFFFFF);
	mainWindow.SetBackground(Color(bgHex).getGXColor());
	
	// Load cursor
	char cursorName[MAXPATHLEN];
	
	windowManager.getString("Cursor.FileName", cursorName);
	strcpy(fileName, pathName);
	strcat(fileName, cursorName);
	
	cursorImage.DestroyImage();
	if(cursorImage.LoadImage(fileName) != IMG_LOAD_ERROR_NONE)
	{
		// Try loading from default
		if(cursorImage.LoadImage(DATAPATH "images/cursor.png") != IMG_LOAD_ERROR_NONE) exit(0);
	}
	
	// Set cursor
	cursorSprite.SetImage(&cursorImage);
	cursorSprite.SetPosition(320, 240);
	
	// Load cursor hotspot
	if(windowManager.getRect("Cursor.HotSpot", &offset) < 2)
	{
		offset.x = 0;
		offset.y = 0;
	}
	
	initParentControl();
	initFiles();
	initNowPlaying();
	initCurrentTime();
	initLength();
	initPlayControls();
	initEQ();
	initSeekBar();
	initMenu();
	
	// Must be added last to draw on top
	initNotice();
}

void Screen::initParentControl()
{
	// Create dummy control to move the rest around on
	LoadableImage *parent = new LoadableImage();
	
	// Move to position
	parent->move(X_LEFT, Y_TOP);
	
	// Try to load background
	char bgName[MAXPATHLEN];
	windowManager.getString("Application.BGGraphic", bgName);
	
	// If it fails, oh well its transparent
	parent->setImage(bgName);
	
	// Add to list as master
	addWidget(parent, WIDGET_MASTER);
}

void Screen::initFiles()
{
	// Create file list
	FileList *fileList = new FileList();
	
	// Get location to place file list
	Rect fLoc;
	windowManager.getRect("FileList.Position", &fLoc, (Rect){ 5, 25, 612, 352 });
	
	// Initialize the file list
	fileList->setDimensions(fLoc);
	fileList->populate("/");
	
	// Insert into widget display list
	fileList->setParent(findWidget(WIDGET_MASTER));
	addWidget(fileList, WIDGET_FILELIST);
}

void Screen::initNowPlaying()
{
	// Create currently playing object
	CurrentPlaying *curPlay = new CurrentPlaying();
	
	// Get location to place
	Rect fLoc;
	windowManager.getRect("NowPlaying.Position", &fLoc, (Rect){ 5, 0, 538, 24 });
	
	// Initialize the now playing list
	curPlay->setDimensions(fLoc);
	
	// Add to widget list
	curPlay->setParent(findWidget(WIDGET_MASTER));
	addWidget(curPlay, WIDGET_NOUID);
}

void Screen::initLength()
{
	// Create currently playing object
	CurrentLength *curLength = new CurrentLength(CR_LENGTH);
	
	// Get location to place
	Rect fLoc;
	windowManager.getRect("CurrentLength.Position", &fLoc, (Rect){ 543, 0, 74, 24 });
	
	// Initialize the now playing list
	curLength->setDimensions(fLoc);
	
	// Add to widget list
	curLength->setParent(findWidget(WIDGET_MASTER));
	addWidget(curLength, WIDGET_NOUID);
}

void Screen::initCurrentTime()
{
	// Create currently playing object
	CurrentLength *curTime = new CurrentLength(CR_TIME);
	
	// Get location to place
	Rect fLoc;
	windowManager.getRect("CurrentPlayTime.Position", &fLoc, (Rect){ 543, 380, 74, 24 });
	
	// Initialize the now playing list
	curTime->setDimensions(fLoc);
	
	// Add to widget list
	curTime->setParent(findWidget(WIDGET_MASTER));
	addWidget(curTime, WIDGET_NOUID);
}

void Screen::initPlayControls()
{
	// Create play/pause
	PlayControl *pc = new PlayControl(CONTROL_PLAYPAUSE);
	
	// Get location to place
	Rect fLoc;
	if(windowManager.getRect("PlayPause.Position", &fLoc) < 2)
	{
		// Defaults
		fLoc.x = 5;
		fLoc.y = 380;
	}
	
	// Move to location
	pc->move(fLoc.x, fLoc.y);
	
	// Add to list
	pc->setParent(findWidget(WIDGET_MASTER));
	addWidget(pc, WIDGET_NOUID);
	
	// Create stop
	pc = new PlayControl(CONTROL_STOP);
	
	// Get location to place
	if(windowManager.getRect("Stop.Position", &fLoc) < 2)
	{
		// Defaults
		fLoc.x = 90;
		fLoc.y = 380;
	}
	
	// Move to location
	pc->move(fLoc.x, fLoc.y);
	
	// Add to list
	pc->setParent(findWidget(WIDGET_MASTER));
	addWidget(pc, WIDGET_NOUID);
	
	// Create previous
	pc = new PlayControl(CONTROL_PREV);
	
	// Get location to place
	if(windowManager.getRect("Previous.Position", &fLoc) < 2)
	{
		// Defaults
		fLoc.x = 175;
		fLoc.y = 380;
	}
	
	// Move to location
	pc->move(fLoc.x, fLoc.y);
	pc->tieToFileList(findWidget(WIDGET_FILELIST));
	
	// Add to list
	pc->setParent(findWidget(WIDGET_MASTER));
	addWidget(pc, WIDGET_NOUID);
	
	// Create previous
	pc = new PlayControl(CONTROL_NEXT);
	
	// Get location to place
	if(windowManager.getRect("Next.Position", &fLoc) < 2)
	{
		// Defaults
		fLoc.x = 260;
		fLoc.y = 380;
	}
	
	// Move to location
	pc->move(fLoc.x, fLoc.y);
	pc->tieToFileList(findWidget(WIDGET_FILELIST));
	
	// Add to list
	pc->setParent(findWidget(WIDGET_MASTER));
	addWidget(pc, WIDGET_NOUID);
}

void Screen::initEQ()
{
	// Create eq
	EQ *eq = new EQ();
	
	// Get location to place
	Rect fLoc;
	windowManager.getRect("GraphicEQ.Position", &fLoc, (Rect){ 345, 380, 160, 56 });
	
	// Initialize the now playing list
	eq->setDimensions(fLoc);
	
	// Add to widget list
	eq->setParent(findWidget(WIDGET_MASTER));
	addWidget(eq, WIDGET_NOUID);
}

void Screen::initSeekBar()
{
	// Create eq
	SeekBar *seek = new SeekBar();
	
	// Get location to place
	Rect fLoc;
	if(windowManager.getRect("SeekBar.Position", &fLoc) < 2)
	{
		fLoc.x = 345;
		fLoc.y = 445;
	}
	
	// Initialize the now playing list
	seek->move(fLoc.x, fLoc.y);
	
	// Add to widget list
	seek->setParent(findWidget(WIDGET_MASTER));
	addWidget(seek, WIDGET_NOUID);
}

void Screen::initNotice()
{
	// Create notice graphic
	LoadableImage *gImage = new LoadableImage();
	
	// Load image
	char bgName[MAXPATHLEN];
	windowManager.getString("Notice.BGGraphic", bgName);
	
	// If it fails, oh well its transparent
	gImage->setImage(bgName);
	gImage->setTransparency(0);
	
	// Place well offscreen
	gImage->move(-1000, -1000);
	gImage->setTouchable(false);
	
	gImage->setParent(findWidget(WIDGET_MASTER));
	addWidget(gImage, WIDGET_NOTICE);
	
	// Set up text part
	Label *gLabel = new Label();	
	{
		char tFont[256];
		windowManager.getString("Notice.Font.Face", tFont, "DejaVu");	
		gLabel->setFont(tFont);
		
		int textSize;
		windowManager.getLong("Notice.Font.Size", &textSize, TEXT_SIZE);
		gLabel->setSize(textSize);
		
		uint32_t textColor;
		windowManager.getLongHex("Notice.Font.Color", &textColor, TEXT_COLOR);
		gLabel->setColor(Color(textColor));
	}
	
	// Set up size
	Rect tRect = gImage->getDimensions();
	gLabel->resize(tRect.width, tRect.height);
	
	// Set up text offset
	int offset;
	windowManager.getLong("Notice.TextOffset", &offset, 0);
	gLabel->move(0, offset);
	gLabel->setTransparency(0);
	gLabel->setCentered(true);
	gLabel->setTouchable(false);
	
	// Add to global widgets
	gLabel->setParent(gImage);
	addWidget(gLabel, WIDGET_NOTICE_CAPTION);
}

void Screen::initMenu()
{
	LoadableImage *tImage = new LoadableImage();
	
	// Figure out path
	char menuPath[MAXPATHLEN];
	windowManager.getString("Menu.BGGraphic", menuPath);
	
	// Set path
	if(tImage->setImage(menuPath))
	{			
		// Get width/height so we can move to center
		Rect tRect = tImage->getDimensions();
		tImage->move(-1000, -1000);
		
		// Add to widget list
		tImage->setParent(findWidget(WIDGET_MASTER));
		addWidget(tImage, WIDGET_MENU);
		
		// Create label with instructions
		Label *insLabel = new Label();
		
		// Ensure tied in
		insLabel->move(5, 5);
		insLabel->resize(tRect.width - 10, tRect.height - 10);
		
		// Set up instructions (no wordwrap for now)
		insLabel->setCaption("Press HOME to close.");
		
		// Add to widget list
		insLabel->setParent(tImage);
		addWidget(insLabel, WIDGET_MENU_CHILD);
		
		// Create button widgets to display options
		Button *button = new Button();
		
		Rect tButtonRect = button->getDimensions();
		button->move(5, tRect.height - (5 + tButtonRect.height));
		button->setCaption("Exit DMP");
		button->downCallback(exitProgram, this);
		
		button->setParent(tImage);
		addWidget(button, WIDGET_MENU_CHILD);
		
		button = new Button();
		
		button->move((tRect.width / 2) + 5, tRect.height - (5 + tButtonRect.height));
		button->setCaption("Close Menu");
		button->downCallback(exitMenu, this);
		
		button->setParent(tImage);
		addWidget(button, WIDGET_MENU_CHILD);
		
		// Wii light setting
		{
			// Create label with wii light instructions
			insLabel = new Label();
			
			// Ensure tied in
			insLabel->move(5, 30);
			insLabel->resize((tRect.width / 2) - 5, 30);
			
			// Set up instructions (no wordwrap for now)
			insLabel->setCaption("Wii light flash");
			
			// Add to widget list
			insLabel->setParent(tImage);
			addWidget(insLabel, WIDGET_MENU_CHILD);
			
			// Option to toggle the wii light
			button = new Button();
			
			button->move((tRect.width / 2) + 5, 30);
			
			// Current light status
			if(sound.getDiskLightEnabled())
			{
				button->setCaption("On");
			}
			else
			{
				button->setCaption("Off");
			}
			
			// Remember which button it is
			wiilight = button;
			button->downCallback(toggleLight, this);
			
			button->setParent(tImage);
			addWidget(button, WIDGET_MENU_CHILD);
		}
		
		// Screen position setting
		{
			// Create label with wii light instructions
			insLabel = new Label();
			
			// Ensure tied in
			insLabel->move(5, 64);
			insLabel->resize(tRect.width - 10, 30);
			
			// Set up instructions (no wordwrap for now)
			insLabel->setCentered(true);
			insLabel->setCaption("Screen Position");
			
			// Add to widget list
			insLabel->setParent(tImage);
			addWidget(insLabel, WIDGET_MENU_CHILD);
			
			// Option to move the screen
			button = new Button();
			
			button->move((tRect.width / 2) - (tButtonRect.width / 2), 90);			
			button->setCaption("Up");
			
			// Remember which button it is
			button->downCallback(moveUp, &settingsManager);
			
			button->setParent(tImage);
			addWidget(button, WIDGET_MENU_CHILD);
			
			// Option to move the screen
			button = new Button();
			
			Rect tButtonRect = button->getDimensions();
			button->move(5, 122);
			button->setCaption("Left");
			button->downCallback(moveLeft, &settingsManager);
			
			button->setParent(tImage);
			addWidget(button, WIDGET_MENU_CHILD);
			
			button = new Button();
			
			button->move((tRect.width / 2) + 5, 122);
			button->setCaption("Right");
			button->downCallback(moveRight, &settingsManager);
			
			button->setParent(tImage);
			addWidget(button, WIDGET_MENU_CHILD);
			
			// Option to move the screen
			button = new Button();
			button->move((tRect.width / 2) - (tButtonRect.width / 2), 154);			
			button->setCaption("Down");
			
			// Remember which button it is
			button->downCallback(moveDown, &settingsManager);
			
			button->setParent(tImage);
			addWidget(button, WIDGET_MENU_CHILD);
		}
		
		// Theme selection
		{
			// Create label with theme caption
			insLabel = new Label();
			
			// Ensure tied in
			insLabel->move(5, 188);
			insLabel->resize((tRect.width / 2) - 5, 30);
			
			// Set up instructions (no wordwrap for now)
			insLabel->setCaption("Theme:");
			
			// Add to widget list
			insLabel->setParent(tImage);
			addWidget(insLabel, WIDGET_MENU_CHILD);
			
			// Create label with theme name
			themeLabel = new Label();
			
			// Ensure tied in
			themeLabel->move((tRect.width / 2) + 5, 188);
			themeLabel->resize((tRect.width / 2) - 5, 30);
			
			// Set up instructions (no wordwrap for now)
			themeLabel->setCaption(menuThemeName);
			
			// Add to widget list
			themeLabel->setParent(tImage);
			addWidget(themeLabel, WIDGET_MENU_CHILD);
			
			// Option to switch themes
			prev = new Button();
			
			Rect tButtonRect = prev->getDimensions();
			prev->move(5, 214);
			prev->downCallback(prevTheme, this);
			
			prev->setParent(tImage);
			addWidget(prev, WIDGET_MENU_CHILD);
			
			next = new Button();
			
			next->move((tRect.width / 2) + 5, 214);
			next->downCallback(nextTheme, this);
			
			next->setParent(tImage);
			addWidget(next, WIDGET_MENU_CHILD);
			
			// Set captions
			setThemeCaptions();
			
		}
	}
}

bool Screen::update()
{
	// Handle notices
	if(notice.newNotice())
	{
		// Ensure notice open
		noticeOpen = true;
		
		// Grab notice
		char tNotice[1024];
		notice.getNotice(tNotice);
		
		// Set notice widget
		LoadableImage *gLabel = (LoadableImage *)findWidget(WIDGET_NOTICE);
		Label *gCaption = (Label *)findWidget(WIDGET_NOTICE_CAPTION);
		
		// Just in case
		if(gCaption && gLabel)
		{
			// Display text
			gCaption->setCaption(tNotice);
			
			// Calculate position
			Rect tRect = gLabel->getAbsoluteDimensions();			
			int x = (640 - tRect.width) / 2;
			
			// Move into position
			gLabel->move(x, NOTICE_POS);
		}
		
		// Only change if we are already faded in, else just continue fading in
		if(noticeFrame > NOTICE_FADE)
		{
			if(noticeFrame > (NOTICE_TOTAL - NOTICE_FADE))
			{
				// Change fading out to fading in at the same alpha
				noticeFrame = (NOTICE_TOTAL - noticeFrame);
			}
			else
			{
				// Just ensure its visible for a good while
				noticeFrame = NOTICE_FADE;
			}
		}
	}
	
	// Ensure that notices get displayed
	if(noticeOpen)
	{
		// Set notice widget
		LoadableImage *gLabel = (LoadableImage *)findWidget(WIDGET_NOTICE);
		Label *gCaption = (Label *)findWidget(WIDGET_NOTICE_CAPTION);
		
		// Just in case
		if(gLabel && gCaption)
		{
			// Next frame
			noticeFrame++;
			
			if(noticeFrame >= NOTICE_TOTAL)
			{
				// Move out of the way
				gLabel->move(-1000, -1000);
				
				noticeOpen = false;
				noticeFrame = 0;
			}
			else
			{
				if(noticeFrame < NOTICE_FADE)
				{
					// Set fade in
					gLabel->setTransparency((noticeFrame * 255) / NOTICE_FADE);
					gCaption->setTransparency((noticeFrame * 255) / NOTICE_FADE);
				}
				else if(noticeFrame > (NOTICE_TOTAL - NOTICE_FADE))
				{
					// Set fade out
					gLabel->setTransparency((NOTICE_TOTAL - noticeFrame) * 255 / NOTICE_FADE);
					gCaption->setTransparency((NOTICE_TOTAL - noticeFrame) * 255 / NOTICE_FADE);
				}
				else
				{
					// Full visible
					gLabel->setTransparency(255);
					gCaption->setTransparency(255);
				}
			}
		}
	}
	
	// Read cursor
	cursor.read();
	
	// Grab input
	u32 buttons = cursor.getButtonsDown();
	
	if(buttons & WPAD_BUTTON_HOME)
	{
		if(!menuOpen)
		{
			// Spawn menu
			LoadableImage *tImage = (LoadableImage *)findWidget(WIDGET_MENU);
			
			// Set path
			if(tImage)
			{			
				// Get width/height so we can move to center
				Rect tRect = tImage->getDimensions();
				tImage->move((640 - tRect.width) / 2, -tRect.height);
				
				// Set up for animation
				menuFinal = (480 - tRect.height) / 2;
				direction = 1;
				
				// Remember menu opening
				menuOpen = true;
			}
			else
			{
				// No menu graphic, just exit
				return true;
			}
		}
		else
		{
			// Exit menu gracefully
			closeMenu();
		}
	}
	
	if(!menuOpen)
	{
		if(buttons & WPAD_BUTTON_1)
		{
			// Play/pause
			Sound sound;
			
			switch(sound.getSoundState())
			{
				case SOUND_UNLOADED:
					// Nothing we can do
					break;
				case SOUND_STOPPED:
					// Start playing media
					sound.playSound();
					break;
				default:
					// Pause/unpause media
					sound.pauseSound();
					break;
			}
		}
		else if(buttons & WPAD_BUTTON_2)
		{
			// Stop
			Sound sound;
			sound.stopSound();
		}
		else if(buttons & WPAD_BUTTON_RIGHT)
		{
			// forward
			Sound sound;		
			if(sound.getSoundState() != SOUND_UNLOADED) 
			{	
				FileList *fList = (FileList *)findWidget(WIDGET_FILELIST);
				
				// Go to next song
				if(fList)
				{
					fList->playFileRelative(1);
				}
			}
		}
		else if(buttons & WPAD_BUTTON_LEFT)
		{
			// back
			Sound sound;		
			if(sound.getSoundState() != SOUND_UNLOADED)
			{		
				FileList *fList = (FileList *)findWidget(WIDGET_FILELIST);
				
				// Go to previous song
				if(fList)
				{
					fList->playFileRelative(-1);
				}
			}
		}
		else if(buttons & WPAD_BUTTON_UP)
		{
			// random/sequential
			FileList *fList = (FileList *)findWidget(WIDGET_FILELIST);
			
			if(fList)
			{
				// Figure out what to toggle
				fList->setRandomMode(!fList->isRandomMode());
				
				if(fList->isRandomMode())
				{
					notice.setNotice("Random play");
				}
				else
				{
					notice.setNotice("Sequential play");
				}
			}
		}
	}
	
	// Draw scene
	setCursor(cursor.getCursorX(), cursor.getCursorY(), cursor.getCursorRot(), buttons, cursor.getButtonsUp());
	drawFrame();
	
	// Don't exit
	return shouldExit;
}


void Screen::addWidget(Widget *widget, int uid)
{
	// Create draw container
	DrawList *d = (DrawList *)malloc(sizeof(DrawList));
	
	// Set values
	d->widget = widget;
	
	if(uid != 0)
	{
		// Provided uid
		d->uid = uid;
	}
	else
	{
		// Built-in
		d->uid = uidUnique--;
	}
	
	// ALWAYS have unique extra uid
	d->uuid = uidUnique--;
	
	// Always added to the beginning of the list
	d->next = drawList;
	drawList = d;
}

Widget *Screen::findWidget(int uid)
{
	// Special case, no widgets
	if(!drawList)
	{
		// Bad widget
		return NULL;
	}
	
	// Iterate through and find the item
	DrawList *a = drawList;
	
	while(a)
	{
		if(a->uid == uid || a->uuid == uid)
		{
			// Found it!
			return a->widget;
		}
		
		// Go to next item
		a = a->next;
	}
	
	// Never found the item
	return NULL;
}

bool Screen::removeWidget(int uid)
{
	// Special case, no widgets
	if(!drawList)
	{
		// Can't delete
		return false;
	}
	
	DrawList *removedItem = NULL;
	
	// Special case, head is the deletable item
	if(drawList->uid == uid || drawList->uuid == uid)
	{
		// Remember item
		removedItem = drawList;
		
		// Remove item
		drawList = drawList->next;
	}
	else
	{
		// Normal find, Iterate through and find the item	
		DrawList *a = drawList;
		DrawList *b = NULL;
		
		while(a)
		{
			if(a->uid == uid || a->uuid == uid)
			{
				// Found it!
				removedItem = a;
				
				// Link nodes on either side
				b->next = a->next;
				
				// Exit the loop
				break;
			}
			
			// Go to next item
			b = a;
			a = a->next;
		}
	}
	
	if(removedItem)
	{
		// Recover object memory
		if(removedItem->widget)
		{
			delete removedItem->widget;
		}
		
		// Recover container memory
		free(removedItem);
		
		// We deleted
		return true;
	}
	
	// No item deleted
	return false;
}

void Screen::setCursor(int x, int y, f32 rot, u32 buttonsDown, u32 buttonsUp)
{
	// Set cursor location
	cursorSprite.SetPosition(x - offset.x, y - offset.y);
	
	// Pass on movement to widgets
	DrawList *d = drawList;
	
	// Remember a dragging widget
	DrawList *drag = NULL;
	
	// Find a dragging widget
	while(d)
	{
		// Check to see if it is visible
		if(d->widget->isDragging())
		{
			// Remember widget
			drag = d;
			break;
		}
		
		// Go to next widget
		d = d->next;
	}
	
	// Reset linked list
	d = drawList;
	
	while(d)
	{
		// Figure out if widget is visible
		if(d->widget->getVisible() && d->widget->getTouchable())
		{
			// Get widget dimensions
			Rect wRect = d->widget->getDimensions();
			
			// Figure out if our cursor is inside dimensions
			if(drag == d || (drag == NULL && (x >= wRect.x && x <= (wRect.x + wRect.width)) && (y >= wRect.y && y <= (wRect.y + wRect.height))))
			{
				// It does, figure out if this widget is enabled
				if(d->widget->getEnabled())
				{
					// Send cursorout command to old widget if needed
					if(lastUid && lastUid != d->uuid)
					{
						// Grab the widget
						Widget *w = findWidget(lastUid);
						
						if(w)
						{
							// Update it so it knows the cursor left its bounds
							w->cursorOut();
						}
					}
					
					// Remember that this was the last operated on
					lastUid = d->uuid;
					
					// Figure out if we need to send a down, move or up event
					bool needsMove = true;
					
					// Down event check
					if(buttonsDown & WPAD_BUTTON_A)
					{
						// Don't need to send a move action
						needsMove = false;
						
						// Send down action
						d->widget->cursorDown(x - wRect.x, y - wRect.y, BUTTON_PRIMARY);
						
						// Remember this for focusing
						focusUid = d->uuid;
					}
					if(buttonsDown & WPAD_BUTTON_B)
					{
						// Don't need to send a move action
						needsMove = false;
						
						// Send down action
						d->widget->cursorDown(x - wRect.x, y - wRect.y, BUTTON_SECONDARY);
						
						// Remember this for focusing
						focusUid = d->uuid;
					}
					if(buttonsDown & WPAD_BUTTON_PLUS)
					{
						// Don't need to send a move action
						needsMove = false;
						
						// Send down action
						d->widget->cursorDown(x - wRect.x, y - wRect.y, BUTTON_PLUS);
						
						// Remember this for focusing
						focusUid = d->uuid;
					}
					if(buttonsDown & WPAD_BUTTON_MINUS)
					{
						// Don't need to send a move action
						needsMove = false;
						
						// Send down action
						d->widget->cursorDown(x - wRect.x, y - wRect.y, BUTTON_MINUS);
						
						// Remember this for focusing
						focusUid = d->uuid;
					}
					
					// Up event check
					if(buttonsUp & WPAD_BUTTON_A)
					{
						// Don't need to send a move action
						needsMove = false;
						
						// Send down action
						d->widget->cursorUp(x - wRect.x, y - wRect.y, BUTTON_PRIMARY);
					}
					if(buttonsUp & WPAD_BUTTON_B)
					{
						// Don't need to send a move action
						needsMove = false;
						
						// Send down action
						d->widget->cursorUp(x - wRect.x, y - wRect.y, BUTTON_SECONDARY);
					}
					// Up event check
					if(buttonsUp & WPAD_BUTTON_PLUS)
					{
						// Don't need to send a move action
						needsMove = false;
						
						// Send down action
						d->widget->cursorUp(x - wRect.x, y - wRect.y, BUTTON_PLUS);
					}
					if(buttonsUp & WPAD_BUTTON_MINUS)
					{
						// Don't need to send a move action
						needsMove = false;
						
						// Send down action
						d->widget->cursorUp(x - wRect.x, y - wRect.y, BUTTON_MINUS);
					}
					
					// Move event check
					if(needsMove)
					{
						// Send move event
						d->widget->cursorMove(x - wRect.x, y - wRect.y);
					}
				}
				else
				{
					// Send cursorout command to old widget if needed
					if(lastUid && lastUid != d->uuid)
					{
						// Grab the widget
						Widget *w = findWidget(lastUid);
						
						if(w)
						{
							// Update it so it knows the cursor left its bounds
							w->cursorOut();
						}
					}
					
					// Ensure that if we switch widgets by the next cursor update, we don't try to send events to this widget
					lastUid = 0;
				}
				
				// Don't hit any more widgets
				return;
			}
		}
		
		// Go to next widget
		d = d->next;
	}
	
	// Didn't hit any, send cursorout command to old widget if needed
	if(lastUid)
	{
		// Grab the widget
		Widget *w = findWidget(lastUid);
		
		if(w)
		{
			// Update it so it knows the cursor left its bounds
			w->cursorOut();
		}
	}
	
	// Don't send an update
	lastUid = 0;
}

void Screen::drawFrame()
{
	// Ensure we have a new range
	GX_InvalidateTexAll();
	
	// Move menu widget if needed
	if(menuOpen)
	{
		// Find it
		Widget *menuWidget = findWidget(WIDGET_MENU);
		
		if(menuWidget)
		{
			// Get location
			Rect tRect = menuWidget->getAbsoluteDimensions();
			
			// Figure out if it needs to move
			if(tRect.y != menuFinal && tRect.y > -1000)
			{
				// It does, figure amount
				int moveAmount = abs(menuFinal - tRect.y);
				
				if(moveAmount > 35)
				{
					// No faster than 50 pixels a screen update
					moveAmount = 35;
				}
				else
				{
					// Decelerate based on proximity
					moveAmount /= 4;
					
					// No slower than five pixels a screen update	
					if(moveAmount < 5) { moveAmount = 5; }
				}
				
				// Move in memory
				tRect.y += moveAmount * direction;
				
				if(tRect.y > menuFinal)
				{
					// Don't go past
					tRect.y = menuFinal;
				}
				else if(tRect.y <= -1000)
				{
					// Remember menu closing
					menuOpen = false;
				}
				
				// Move actual widget
				menuWidget->move(tRect.x, tRect.y);
			}
		}
	}
	
	// Draw widgets (reverse order)
	DrawList *end = NULL;
	
	while(1)
	{
		DrawList *d = drawList;
		
		if(d == end)
		{	
			// Finished!
			break;
		}
		
		// Find next widget
		while(d->next != end)
		{
			d = d->next;
		}
		
		// Check to see if it is visible
		if(d->widget->getVisible())
		{
			// Render widget
			d->widget->draw();
		}
		
		// Set end one back
		end = d;
	}
	
	// Draw cursor
	{
		// Create layer manager
		LayerManager manager(1);
		
		// Hook cursor
		manager.Append(&cursorSprite);
		
		// Draw cursor
		manager.Draw(settingsManager.getOffsetX(), settingsManager.getOffsetY());
	}
	
	// Draw entire GX region
	mainWindow.Flush();
}
