// ---------------------------------------------------------------------------
// 
//   Lily Engine alpha
// 
//   Copyright (c) 2010 Clifford Jolly
//     http://cliffjolly.com
//     cliff@cliffjolly.com
// 
// ---------------------------------------------------------------------------
// 
//   This program 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 2 of the License, or
//   (at your option) any later version.
//  
//   This program 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, write to the Free Software
//   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
//   USA
// -------------------------- END HEADER -------------------------------------

#include <string>
#include <vector>
#include <sstream>
#include <cstdlib>
using namespace std;

#include "../hacks/winhacks.h"
#include <SDL.h>

#include "../filesystem/filesystem.h"

#include "../string/string.h"

#include "../texturemanager/texturemanager.h"

#include "../uijunk/widget.h"
#include "../uijunk/widgetcontainer.h"
#include "../uijunk/widgetbutton.h"
#include "../uijunk/widgetalignedbox.h"
#include "../uijunk/widgetpaddedbox.h"
#include "../uijunk/widgettext.h"
#include "../uijunk/widgetscrollable.h"
#include "../uijunk/widgetslider.h"
#include "../uijunk/widgetradiogroup.h"
#include "../uijunk/widgetselectchild.h"

#include "editorui.h"
#include "world.h"
#include "input.h"
#include "engineui.h"
#include "config.h"
#include "input.h"

// Warning to anyone who views this file:

// The UI system can be a little wordy. Also, the system for waiting
// for keybinds is a little ugly with the static variables and
// stuff. None of this should be used outside of the very narrow use
// that it has already, and it was coded with that single use in mind.

// -------------------------------------------------------------------
//  Controls settings
// -------------------------------------------------------------------

static vector<string> controlNames;

void engineUISetControlActionName(int controlNumber, const std::string &name) {
    
    if(controlNumber > 256) return; // Anything bigger would be
                                    // ridiculous, but this number is
                                    // always here to change.

    while((int)controlNames.size() < controlNumber + 1) {
        controlNames.push_back("???");
    }

    controlNames[controlNumber] = name;

}

// -------------------------------------------------------------------
//  Random junk
// -------------------------------------------------------------------

static vector<Widget*> engineUIStack;

static bool gotQuit = false;

static bool waitingForKeybind = false;
static int  waitingKeybindNum = -1;
static bool waitingSecondaryBind = false;
static WidgetButton *waitingButton = NULL;

static void pumpFocusEvent(void) {

    WidgetEvent event;
    event.type = WIDGETEVENT_FOCUSNEXT;
    engineUIHandleEvent(&event, NULL);

}

static bool escapeGoBack(Widget *w, void *data) {

    const WidgetEvent_Keyboard *event = (WidgetEvent_Keyboard*)widgetGetEvent();
    
    if(event->keyCode == WIDGET_KEY_ESCAPE) {

        engineUIPopAndDestroy();

        // This assumes the keybind window can never have anything
        // above it.
        waitingForKeybind = false;
        waitingKeybindNum = -1;
        waitingButton = NULL;

        return true;
    }

    return false;
}

static bool clickGoBack(Widget *w, void *data) {
    engineUIPopAndDestroy();
    return false;
}

// Theme defaults

static void setEngineUISlider(WidgetSlider *slider) {
	slider->setThingyImage("editordata/transparentbutton1.ninepatch");
	slider->setBackgroundImage("editordata/transparentbutton1_md.ninepatch");
	slider->setMinWidth(16);
	slider->setMinHeight(16);
	slider->setThingySize(32);
}

static void setEngineUIButton(WidgetButton *button) {

	button->setBackgroundImage("editordata/transparentbutton1.ninepatch");
	button->setBackgroundDefault("editordata/transparentbutton1.ninepatch");
	button->setBackgroundActive("editordata/transparentbutton1_md.ninepatch");
	button->setBackgroundMouseOver("editordata/transparentbutton1_mo.ninepatch");

	//button->setBackgroundImage("editordata/1pxbox1.ninepatch");
	//button->setBackgroundDefault("editordata/1pxbox1.ninepatch");
	//button->setBackgroundMouseOver("editordata/1pxbox3.ninepatch");
	//button->setBackgroundActive("editordata/1pxbox2.ninepatch");
}

static void setEngineUICheckbox(WidgetButton *button) {

    button->setBackgroundImage("editordata/checked.tga");
    button->setBackgroundDefault("editordata/unchecked.tga");
    button->setBackgroundActive("editordata/checked.tga");
    button->setBackgroundMouseOver("editordata/checking.tga");

    button->setLabel("");
    button->setToggleable(true);
    button->setMinWidth(16);
    button->setMinHeight(16);
    button->setLabelPadding(0);

}


static Widget *wrapInScrollView(Widget *w) {

    WidgetContainer *view = new WidgetContainer();
    view->setNumCols(2);
    view->setStretchableCol(0);
    view->setStretchableRow(0);

    WidgetScrollable *scrollable = new WidgetScrollable();
    scrollable->setChild(w);

    WidgetSlider *scrollbar = new WidgetSlider();
    setEngineUISlider(scrollbar);
    scrollbar->setSmallStep(16);
    scrollbar->setBigStep(128);
    scrollable->setVerticalScrollBar(scrollbar);

    view->addChild(scrollable);
    view->addChild(scrollbar);

    return view;
}

// -------------------------------------------------------------------
//  Game selection screen
// ------------------------------------------------------------------- 

static void makeGamePicker(Widget **rootWidget, WidgetContainer **container) {

    WidgetContainer *gameSelectionBox = new WidgetContainer();
    gameSelectionBox->setCellSpacing(8);

    WidgetText *infoText = new WidgetText();
    infoText->setText("Multiple game projects or mods detected. Please select project to start.");
    infoText->setWordWrap(true);

    WidgetContainer *stuffInWindow = new WidgetContainer();
    stuffInWindow->addChild(infoText);
    stuffInWindow->addChild(gameSelectionBox);
    stuffInWindow->setNumRows(2);

    WidgetPaddedBox *paddedBox = new WidgetPaddedBox();
    paddedBox->setChild(stuffInWindow);
    paddedBox->setBackgroundImage("editordata/np_ff3.ninepatch");
    paddedBox->setPadding(16);
    paddedBox->setMinWidth(256);

    WidgetAlignedBox *rootBox = new WidgetAlignedBox();
    rootBox->setChild(paddedBox);
    rootBox->setVAlign(0);
    rootBox->setHAlign(0);

    *rootWidget = rootBox;
    *container = gameSelectionBox;
}

static void setGamePickerButton(WidgetButton *button) {

	button->setBackgroundImage("editordata/transparentbutton1.ninepatch");
	button->setBackgroundDefault("editordata/transparentbutton1.ninepatch");
	button->setBackgroundActive("editordata/transparentbutton1_md.ninepatch");
	button->setBackgroundMouseOver("editordata/transparentbutton1_mo.ninepatch");

	//button->setBackgroundImage("editordata/1pxbox1.ninepatch");
	//button->setBackgroundDefault("editordata/1pxbox1.ninepatch");
	//button->setBackgroundMouseOver("editordata/1pxbox3.ninepatch");
	//button->setBackgroundActive("editordata/1pxbox2.ninepatch");

}

static bool pickGame(Widget *w, void *data) {
    
    WidgetButton *button = (WidgetButton*)w;

    engineUIPopAndDestroy();

    World *world = (World*)data;

    string gameName = button->getName();

    world->setGameName(gameName);
    world->runInitLua();

    // Rebuild the spawn list.
    EditorUI *editor = world->getEditor();
    editor->populateSpawnList();

    // Load configs.
    loadConfig(gameName, world);

    return false;
}

static WidgetButton *makeGameButton(const std::string &gameName, World *gameWorld) {

    WidgetButton *button = new WidgetButton();
    button->setName(gameName);

    //button->setLabel(gameName);
    button->setNativeEventHandler("onClick", pickGame, gameWorld);
    setGamePickerButton(button);

    Widget *icon = new Widget();
    icon->setBackgroundImage("games/" + gameName + "/info/icon.tga");
    icon->setMinWidth(64);
    icon->setMinHeight(64);

    WidgetAlignedBox *iconBox = new WidgetAlignedBox();
    iconBox->setChild(icon);
    iconBox->setVAlign(-1);
    iconBox->setHAlign(0);

    WidgetText *desc = new WidgetText();
    desc->setWordWrap(true);

    int descLen = 0;
    char *descContents = FileSystem::loadFile("games/" + gameName + "/info/desc.txt", &descLen, true);
    if(descContents) {

        // Shave off whitespace from the end.
        int j = strlen(descContents) - 1;
        while(j && descContents[j]) {
            
            if(descContents[j] != '\n' &&
               descContents[j] != '\r' &&
               descContents[j] != ' ') {

                break;
            }

            j--;
        }

        descContents[j + 1] = 0;

        // Set the label to this description.
        desc->setText(descContents);
        delete[] descContents;

    } else {

        // No description? Settle for the game name.
        desc->setText(gameName);
    }

    WidgetContainer *buttonCols = new WidgetContainer();

    buttonCols->setMinWidth(256);
    buttonCols->setCellSpacing(8);
    buttonCols->setNumCols(2);
    buttonCols->setNumRows(1);
    buttonCols->setStretchableCol(1);
    
    buttonCols->addChild(iconBox);
    buttonCols->addChild(desc);

    button->replaceLabelWidget(buttonCols);

    return button;

}

void popupGameSelectScreen(World *world) {

    Widget *gameSelectRoot = NULL;
    WidgetContainer *gameSelectContainer = NULL;

    makeGamePicker(&gameSelectRoot, &gameSelectContainer);
    engineUIPush(gameSelectRoot);

    vector<string> gameDirs;      
    FileSystem::getSubdirectories("games", gameDirs);

    for(unsigned int i = 0; i < gameDirs.size(); i++) {

        if(gameDirs[i] != "common") {
            WidgetButton *gameButton = makeGameButton(gameDirs[i], world);
            gameSelectContainer->addChild(gameButton);
            gameSelectContainer->setNumRows(gameSelectContainer->getNumChildren());
            gameSelectContainer->setStretchableRow(-1);
        }

    }

    pumpFocusEvent();

}

// -------------------------------------------------------------------
//  "Are you sure?" quit dialog
// -------------------------------------------------------------------

static bool quitYesClicked(Widget *w, void *data) {
    gotQuit = true;
    engineUIPopAndDestroy();
    return false;
}

static void popupQuitDialog(void) {

    WidgetAlignedBox *rootBox = new WidgetAlignedBox();

    WidgetPaddedBox *menuWindow = new WidgetPaddedBox();
    menuWindow->setMinWidth(256);
    menuWindow->setPadding(16);
    menuWindow->setBackgroundImage("editordata/np_ff3.ninepatch");
    menuWindow->setNativeEventHandler("WIDGETEVENT_KEYDOWN", escapeGoBack, NULL);

    WidgetContainer *vertContainer = new WidgetContainer();
    vertContainer->setNumRows(2);
    vertContainer->setStretchableRow(0);
    vertContainer->setCellSpacing(16);

    WidgetContainer *buttonsContainer = new WidgetContainer();
    buttonsContainer->setNumCols(3);
    buttonsContainer->setStretchableCol(1);
    
    WidgetText *areYouSureText = new WidgetText();
    areYouSureText->setText("Are you sure you want to quit?");

    WidgetButton *okayButton = new WidgetButton();
    setEngineUIButton(okayButton);
    okayButton->setMinWidth(64);
    okayButton->setLabel("Yes");
    okayButton->setNativeEventHandler("onClick", quitYesClicked, NULL);
    
    WidgetButton *cancelButton = new WidgetButton();
    setEngineUIButton(cancelButton);
    cancelButton->setLabel("No");
    cancelButton->setMinWidth(64);
    cancelButton->setNativeEventHandler("onClick", clickGoBack, NULL);

    buttonsContainer->addChild(okayButton);
    buttonsContainer->addChild(NULL);
    buttonsContainer->addChild(cancelButton);
    vertContainer->addChild(areYouSureText);
    vertContainer->addChild(buttonsContainer);
    menuWindow->setChild(vertContainer);
    rootBox->setChild(menuWindow);
    engineUIPush(rootBox);

    pumpFocusEvent();
    
}

// -------------------------------------------------------------------
//  Readme window
// -------------------------------------------------------------------

static void popupReadme(void) {

    WidgetAlignedBox *rootBox = new WidgetAlignedBox();

    WidgetPaddedBox *menuWindow = new WidgetPaddedBox();
    menuWindow->setMinWidth(256);
    menuWindow->setPadding(16);
    menuWindow->setBackgroundImage("editordata/np_ff3.ninepatch");
    menuWindow->setNativeEventHandler("WIDGETEVENT_KEYDOWN", escapeGoBack, NULL);

    WidgetContainer *rows = new WidgetContainer();
    rows->setStretchableRow(0);
    rows->setMinWidth(512);
    rows->setMinHeight(512);

    WidgetContainer *textView = new WidgetContainer();
    textView->setNumCols(2);
    textView->setStretchableCol(0);
    textView->setStretchableRow(0);

    WidgetScrollable *scrollable = new WidgetScrollable();

    WidgetSlider *scrollbar = new WidgetSlider();
    setEngineUISlider(scrollbar);
    scrollbar->setSmallStep(32);
    scrollbar->setBigStep(256);
    scrollable->setVerticalScrollBar(scrollbar);
    
    WidgetText *readmeText = new WidgetText();

    int readmeLen = 0;
    char *readmeFileData = FileSystem::loadFile("README.txt", &readmeLen, true);
    if(readmeFileData) {
        readmeText->setText(readmeFileData);
        delete[] readmeFileData;
    } else {
        readmeText->setText("An error occured loading the README.txt file.");
    }

    WidgetButton *closeButton = new WidgetButton();
    setEngineUIButton(closeButton);
    closeButton->setLabel("Close");
    closeButton->setNativeEventHandler("onClick", clickGoBack, NULL);

    textView->addChild(scrollable);
    textView->addChild(scrollbar);
    scrollable->setChild(readmeText);
    rows->addChild(textView);
    rows->addChild(closeButton);
    rows->setNumRows(rows->getNumChildren());
    menuWindow->setChild(rows);
    rootBox->setChild(menuWindow);

    engineUIPush(rootBox);
    pumpFocusEvent();
}

// -------------------------------------------------------------------
//  Options screen
// ------------------------------------------------------------------- 

static bool keybindButtonClicked(Widget *w, void *data) {

    // Pop up a new window telling the user to press something, clear
    // the bind, or just cancel.

    WidgetAlignedBox *rootBox = new WidgetAlignedBox();

    WidgetPaddedBox *menuWindow = new WidgetPaddedBox();
    menuWindow->setPadding(16);
    menuWindow->setBackgroundImage("editordata/np_ff3.ninepatch");
    menuWindow->setNativeEventHandler("WIDGETEVENT_KEYDOWN", escapeGoBack, NULL);
    rootBox->setChild(menuWindow);

    WidgetText *infoText = new WidgetText();
    infoText->setText("Press a key to assign to this bind, or escape to cancel.\nPress backspace to clear the bind.");
    menuWindow->setChild(infoText);

    engineUIPush(rootBox);

    // Which bind it is (number and primary/secondary) is stored in
    // the Widget's name.
    string widgetName = w->getName();
    vector<string> nameParts;
    stringTokenize(widgetName, "_", nameParts, true);

    waitingButton = dynamic_cast<WidgetButton*>(w);

    if(nameParts.size() > 1) {

        // Set up static variables so we know what to set when the
        // player finally does pick something.

        waitingForKeybind = true;
        waitingKeybindNum = atoi(nameParts[0].c_str());

        if(nameParts[1] == "Secondary") {
            waitingSecondaryBind = true;
        } else {
            waitingSecondaryBind = false;
        }

    }

    return false;

}

static Widget *createKeybindsScreen(void) {
    
    WidgetContainer *container = new WidgetContainer();
    container->setNumCols(3);
    container->setCellSpacing(4);

    WidgetText *nameColHeader = new WidgetText();
    nameColHeader->setText("Action");
    container->addChild(nameColHeader);
    
    // "Keyboard" and "Joystick" have been replaced with "Primary" and
    // "Secondary".

    WidgetText *keyColHeader = new WidgetText();
    keyColHeader->setText("Primary");
    container->addChild(keyColHeader);

    WidgetText *joyColHeader = new WidgetText();
    joyColHeader->setText("Secondary");
    container->addChild(joyColHeader);

    int bindLabelPadding = 16;

    float bindMinWidth = 192;

    for(unsigned int i = 0; i < controlNames.size(); i++) {

        InputBindDef def;


        // Action name

        WidgetAlignedBox *nameBox = new WidgetAlignedBox();
        nameBox->setHAlign(-1);
        container->addChild(nameBox);

        WidgetText *name = new WidgetText();
        name->setText(controlNames[i]);
        nameBox->setChild(name);


        // Primary column

        WidgetButton *keyBindButton = new WidgetButton();
        setEngineUIButton(keyBindButton);
        keyBindButton->setMinWidth(bindMinWidth);
        keyBindButton->setMinHeight(4);
        keyBindButton->setLabelPadding(bindLabelPadding);

        keyBindButton->setNativeEventHandler("onClick", keybindButtonClicked, NULL);

        ostringstream widgetNameStr1;
        widgetNameStr1 << i << "_" << "Primary";
        keyBindButton->setName(widgetNameStr1.str());

        container->addChild(keyBindButton);

        inputGetKeybind(i, false, &def);
        keyBindButton->setLabel(inputGetNameForBind(&def));


        // Secondary column

        WidgetButton *joyBindButton = new WidgetButton();
        setEngineUIButton(joyBindButton);
        joyBindButton->setMinWidth(bindMinWidth);
        joyBindButton->setMinHeight(4);
        joyBindButton->setLabelPadding(bindLabelPadding);

        ostringstream widgetNameStr2;
        widgetNameStr2 << i << "_" << "Secondary";
        joyBindButton->setName(widgetNameStr2.str());

        container->addChild(joyBindButton);

        joyBindButton->setNativeEventHandler("onClick", keybindButtonClicked, NULL);

        inputGetKeybind(i, true, &def);
        joyBindButton->setLabel(inputGetNameForBind(&def));

    }

    container->setNumRows(container->getNumChildren() / 3);
    container->setStretchableRow(-1);

    return container;

}

// Audio control callbacks.

static bool setSFXVolume(Widget *w, void *data) {
    
    World *world = (World*)data;
    WidgetSlider *slider = dynamic_cast<WidgetSlider*>(w);
    
    if(slider && world) {
        world->soundResourceManager.setSoundVolume(slider->getValue());
    }

    return false;
}

static bool setMusicVolume(Widget *w, void *data) {
    
    World *world = (World*)data;
    WidgetSlider *slider = dynamic_cast<WidgetSlider*>(w);
    
    if(slider && world) {
        world->soundResourceManager.setMusicVolume(slider->getValue());
    }

    return false;
}

static Widget *createAudioScreen(World *world) {

    WidgetContainer *container = new WidgetContainer();
    container->setNumCols(2);
    container->setCellSpacing(4);
    container->setStretchableCol(0);
    container->setStretchableRow(-1);

    WidgetText *soundEffectsVolumeText = new WidgetText();
    soundEffectsVolumeText->setText("Sound effects volume");

    WidgetText *musicVolumeText = new WidgetText();
    musicVolumeText->setText("Music volume");

    // Sound effects volume slider.
    WidgetSlider *sfxSlider = new WidgetSlider();
    sfxSlider->setVertical(false);
    setEngineUISlider(sfxSlider);
    sfxSlider->setMinWidth(128);
    sfxSlider->setValue(world->soundResourceManager.getSoundVolume());
    sfxSlider->setNativeEventHandler("onValueChanged", setSFXVolume, world);

    // Music volume slider.
    WidgetSlider *musicSlider = new WidgetSlider();
    musicSlider->setVertical(false);
    setEngineUISlider(musicSlider);
    musicSlider->setMinWidth(128);
    musicSlider->setValue(world->soundResourceManager.getMusicVolume());
    musicSlider->setNativeEventHandler("onValueChanged", setMusicVolume, world);

    container->addChild(soundEffectsVolumeText);
    container->addChild(sfxSlider);
    container->addChild(musicVolumeText);
    container->addChild(musicSlider);

    container->setNumRows(container->getNumChildren() / container->getNumCols());

    return container;

}

static bool shaderBoxClicked(Widget *w, void *data) {
    WidgetButton *box = dynamic_cast<WidgetButton*>(w);
    
    bool shadersEnabled = box->getToggleValue();

    TextureManager *manager = TextureManager::get();
    manager->setShadersEnabled(shadersEnabled);
    manager->clearAllTextures();

    return false;
}

static bool lightingBoxClicked(Widget *w, void *data) {
    WidgetButton *box = dynamic_cast<WidgetButton*>(w);
    World *world = (World*)data;
    bool lightingEnabled = box->getToggleValue();

    world->setLighting(lightingEnabled);

    return false;
}

static Widget *createVideoScreen(World *world) {

    WidgetContainer *container = new WidgetContainer();
    container->setNumCols(2);
    container->setCellSpacing(4);
    container->setStretchableCol(0);
    container->setStretchableRow(-1);

    bool lightingEnabled = world->getLighting();

    TextureManager *manager = TextureManager::get();
    bool shadersEnabled = manager->getShadersEnabled();

    WidgetText *shaderLabel = new WidgetText();
    shaderLabel->setText("Shaders");

    WidgetButton *shaderCheckBox = new WidgetButton();
    setEngineUICheckbox(shaderCheckBox);
    shaderCheckBox->setToggleValue(shadersEnabled);
    shaderCheckBox->setNativeEventHandler("onClick", shaderBoxClicked, NULL);

    WidgetText *lightingLabel = new WidgetText();
    lightingLabel->setText("Lighting");

    WidgetButton *lightingCheckBox = new WidgetButton();
    setEngineUICheckbox(lightingCheckBox);
    lightingCheckBox->setToggleValue(lightingEnabled);
    lightingCheckBox->setNativeEventHandler("onClick", lightingBoxClicked, world);

    container->addChild(lightingLabel);
    container->addChild(lightingCheckBox);

    container->addChild(shaderLabel);
    container->addChild(shaderCheckBox);

    container->setNumRows(container->getNumChildren() / 2);

    return container;

}

void popupOptionsScreen(World *world) {

    WidgetAlignedBox *rootBox = new WidgetAlignedBox();

    WidgetPaddedBox *menuWindow = new WidgetPaddedBox();
    menuWindow->setMinWidth(256);
    menuWindow->setMinHeight(384);
    menuWindow->setPadding(16);
    menuWindow->setBackgroundImage("editordata/np_ff3.ninepatch");
    menuWindow->setNativeEventHandler("WIDGETEVENT_KEYDOWN", escapeGoBack, NULL);
    rootBox->setChild(menuWindow);

    WidgetContainer *rows = new WidgetContainer();
    menuWindow->setChild(rows);
    rows->setNumRows(3);
    rows->setStretchableRow(1);

    WidgetRadioGroup *group = new WidgetRadioGroup();
    rows->addChild(group);

    WidgetContainer *tabButtonsContainer = new WidgetContainer();
    group->setChild(tabButtonsContainer);

    // Tab buttons for the options screen

    WidgetButton *keybindsButton = new WidgetButton();
    setEngineUIButton(keybindsButton);
    keybindsButton->setLabel("Controls");
    tabButtonsContainer->addChild(keybindsButton);
    keybindsButton->setToggleable(true);
    keybindsButton->setIsRadio(true);

    WidgetButton *videoButton = new WidgetButton();
    setEngineUIButton(videoButton);
    videoButton->setLabel("Video");
    tabButtonsContainer->addChild(videoButton);
    videoButton->setToggleable(true);
    videoButton->setIsRadio(true);

    WidgetButton *soundButton = new WidgetButton();
    setEngineUIButton(soundButton);
    soundButton->setLabel("Audio");
    tabButtonsContainer->addChild(soundButton);
    soundButton->setToggleable(true);
    soundButton->setIsRadio(true);

    WidgetButton *miscButton = new WidgetButton();
    setEngineUIButton(miscButton);
    miscButton->setLabel("Misc");
    tabButtonsContainer->addChild(miscButton);
    miscButton->setToggleable(true);
    miscButton->setIsRadio(true);

    // Done adding tab buttons

    tabButtonsContainer->setNumCols(tabButtonsContainer->getNumChildren() + 1);
    tabButtonsContainer->setStretchableCol(tabButtonsContainer->getNumChildren());
    keybindsButton->setToggleValue(true);

    // Actual tab pane.

    WidgetSelectChild *tabPane = new WidgetSelectChild();
    tabPane->setMinWidth(256);
    tabPane->setMinHeight(256);

    WidgetPaddedBox *paddedBox = new WidgetPaddedBox();
    paddedBox->setChild(tabPane);

    Widget *wrappedView = wrapInScrollView(paddedBox);
    rows->addChild(wrappedView);

    // Individual tabs.

    Widget *keybindsScreen = createKeybindsScreen();
    tabPane->addChild(keybindsScreen);
    tabPane->mapRadioToChild(keybindsButton, keybindsScreen);

    Widget *videoScreen = createVideoScreen(world);
    tabPane->addChild(videoScreen);
    tabPane->mapRadioToChild(videoButton, videoScreen);

    Widget *soundScreen = createAudioScreen(world);
    tabPane->addChild(soundScreen);
    tabPane->mapRadioToChild(soundButton, soundScreen);

    Widget *miscScreen = new Widget();
    tabPane->addChild(miscScreen);
    tabPane->mapRadioToChild(miscButton, miscScreen);
    
    // Bottom row is just the close button.

    WidgetButton *closeButton = new WidgetButton();
    setEngineUIButton(closeButton);
    closeButton->setLabel("Close");
    closeButton->setNativeEventHandler("onClick", clickGoBack, NULL);
    rows->addChild(closeButton);

    engineUIPush(rootBox);
    pumpFocusEvent();
}

// -------------------------------------------------------------------
//  Main menu
// ------------------------------------------------------------------- 

static bool quitButtonClick(Widget *w, void *data) {
    
    popupQuitDialog();
    return false;
}

static bool readmeButtonClick(Widget *w, void *data) {

    popupReadme();
    return false;
}

static bool optionsButtonClick(Widget *w, void *data) {

    popupOptionsScreen((World*)data);
    return false;
}

void popupEngineMainMenu(World *world) {

    WidgetAlignedBox *rootBox = new WidgetAlignedBox();

    WidgetPaddedBox *menuWindow = new WidgetPaddedBox();
    menuWindow->setMinWidth(256);
    menuWindow->setPadding(16);
    menuWindow->setBackgroundImage("editordata/np_ff3.ninepatch");
    menuWindow->setNativeEventHandler("WIDGETEVENT_KEYDOWN", escapeGoBack, NULL);

    WidgetContainer *menuContainer = new WidgetContainer();

    WidgetButton *optionsButton = new WidgetButton();
    setEngineUIButton(optionsButton);
    optionsButton->setLabel("Options");
    optionsButton->setNativeEventHandler("onClick", optionsButtonClick, world);

    WidgetButton *readmeButton = new WidgetButton();
    setEngineUIButton(readmeButton);
    readmeButton->setLabel("Readme");
    readmeButton->setNativeEventHandler("onClick", readmeButtonClick, NULL);

    WidgetButton *quitButton = new WidgetButton();
    setEngineUIButton(quitButton);
    quitButton->setLabel("Quit");
    quitButton->setNativeEventHandler("onClick", quitButtonClick, NULL);

    menuContainer->addChild(optionsButton);
    menuContainer->addChild(readmeButton);
    menuContainer->addChild(quitButton);
    menuContainer->setNumRows(menuContainer->getNumChildren());

    menuWindow->setChild(menuContainer);
    rootBox->setChild(menuWindow);
    engineUIPush(rootBox);

    pumpFocusEvent();
}

// -------------------------------------------------------------------
//  Utility stuff
// ------------------------------------------------------------------- 

void engineUIPush(Widget *w) {
    engineUIStack.push_back(w);
}

void engineUIPop(void) {

    assert(engineUIStack.size());

    engineUIStack.erase(engineUIStack.end() - 1);
}

void engineUIPopAndDestroy(void) {

    assert(engineUIStack.size());

    Widget *w = engineUIStack[engineUIStack.size() - 1];

    widgetSafeDelete(w);
    engineUIPop();
}

bool engineUIHandleEvent(WidgetEvent *event, World *world) {

    if(engineUIStack.size()) {
        
        engineUIStack[engineUIStack.size() - 1]->handleEvent(event);
        return true;
    }

    if(event->type == WIDGETEVENT_KEYDOWN) {

        WidgetEvent_Keyboard *keyEvent = (WidgetEvent_Keyboard*)event;
        
        // Escape key - bring up the main menu.
        if(keyEvent->keyCode == WIDGET_KEY_ESCAPE) {
            popupEngineMainMenu(world);
            return true;
        }
    }

    return false;
}

void engineUIDraw(int width, int height) {

    if(!engineUIStack.size()) return;

    for(unsigned int i = 0; i < engineUIStack.size(); i++) {

        Widget *root = engineUIStack[i];

        root->setWidth(width);
        root->setHeight(height);
        root->draw();

    }
}

bool engineUIOpen(void) {
    return engineUIStack.size();
}

bool engineUIGotQuit(void) {
    return gotQuit;
}

void engineUIUpdate(void) {

    if(waitingForKeybind) {

        SDL_PumpEvents();

        InputBindDef def;
        inputClearBindDef(&def);

        // Start by checking keyboard events.
        Uint8 *keyState = SDL_GetKeyState(NULL);

        bool gotClearSignal = false;

        for(int i = 0; i < SDLK_LAST; i++) {
            
            // Skip these...
            if(i == SDLK_NUMLOCK   ||
               i == SDLK_CAPSLOCK  ||
               i == SDLK_SCROLLOCK ||
               i == SDLK_ESCAPE) {
                continue;
            }

            if(keyState[i]) {

                if(i == SDLK_BACKSPACE) {
                 
                    // Got the clear signal.
                    gotClearSignal = true;
                    def.sdlKey = -1;
   
                } else {

                    // Set the key.
                    def.sdlKey = i;
                }
            }
        }

        if(def.sdlKey == -1 && !gotClearSignal) {

            // Check joysticks.
            inputSetDefBasedOnWhateverJoysticksAreDoing(&def);

        }

        if(gotClearSignal || def.sdlKey != -1 || def.joyNum != -1) {

            engineUIPopAndDestroy();
            waitingForKeybind = false;

            if(waitingButton) {

                inputSetKeybind(waitingKeybindNum, waitingSecondaryBind, &def);

                waitingButton->setLabel(inputGetNameForBind(&def));
            }

        }

    }

}



