#include <guichan.hpp>
#include <SDL/SDL_ttf.h>
#include <guichan/sdl.hpp>
#include "sdltruetypefont.hpp"
#include "SelectorEntry.hpp"

#include "sysconfig.h"
#include "sysdeps.h"
#include "config.h"
#include "options.h"
#include "uae.h"
#include "gui.h"
#include "gui_handling.h"
#include "memory.h"
#include "autoconf.h"

bool gui_running = false;
static int last_active_panel = 1;


ConfigCategory categories[] = {
  { "Paths",            "data/paths.ico",     NULL, NULL, InitPanelPaths,     ExitPanelPaths,   RefreshPanelPaths },
  { "Configurations",   "data/file.ico",      NULL, NULL, InitPanelConfig,    ExitPanelConfig,  RefreshPanelConfig },
  { "CPU and FPU",      "data/cpu.ico",       NULL, NULL, InitPanelCPU,       ExitPanelCPU,     RefreshPanelCPU },
  { "Chipset",          "data/cpu.ico",       NULL, NULL, InitPanelChipset,   ExitPanelChipset, RefreshPanelChipset },
  { "ROM",              "data/chip.ico",      NULL, NULL, InitPanelROM,       ExitPanelROM,     RefreshPanelROM },
  { "RAM",              "data/chip.ico",      NULL, NULL, InitPanelRAM,       ExitPanelRAM,     RefreshPanelRAM },
  { "Floppy drives",    "data/35floppy.ico",  NULL, NULL, InitPanelFloppy,    ExitPanelFloppy,  RefreshPanelFloppy },
  { "Hard drives / CD", "data/drive.ico",     NULL, NULL, InitPanelHD,        ExitPanelHD,      RefreshPanelHD },
  { "Display",          "data/screen.ico",    NULL, NULL, InitPanelDisplay,   ExitPanelDisplay, RefreshPanelDisplay },
  { "Sound",            "data/sound.ico",     NULL, NULL, InitPanelSound,     ExitPanelSound,   RefreshPanelSound },
  { "Input",            "data/joystick.ico",  NULL, NULL, InitPanelInput,     ExitPanelInput,   RefreshPanelInput },
  { "Miscellaneous",    "data/misc.ico",      NULL, NULL, InitPanelMisc,      ExitPanelMisc,    RefreshPanelMisc },
  { "Savestates",       "data/savestate.png", NULL, NULL, InitPanelSavestate, ExitPanelSavestate, RefreshPanelSavestate },
  { NULL, NULL, NULL, NULL, NULL, NULL, NULL }
};
enum { PANEL_PATHS, PANEL_CONFIGURATIONS, PANEL_CPU, PANEL_CHIPSET, PANEL_ROM, PANEL_RAM,
       PANEL_FLOPPY, PANEL_HD, PANEL_DISPLAY, PANEL_SOUND, PANEL_INPUT, PANEL_MISC, PANEL_SAVESTATES, 
       NUM_PANELS };


gcn::Gui* uae_gui;
gcn::Color gui_baseCol;
gcn::Color gui_baseColLabel;
gcn::Color colSelectorInactive;
gcn::Color colSelectorActive;
gcn::Container* gui_top;
gcn::Container* selectors;
gcn::contrib::SDLTrueTypeFont* gui_font;
SDL_Surface* gui_screen;
gcn::SDLGraphics* gui_graphics;
gcn::SDLInput* gui_input;
gcn::SDLImageLoader* gui_imageLoader;

namespace widgets 
{
  // Main buttons
  gcn::Button* cmdQuit;
  gcn::Button* cmdReset;
  gcn::Button* cmdRestart;
  gcn::Button* cmdStart;
  gcn::Button* cmdShutdown;
}


/* Flag for changes in rtarea:
  Bit 0: any HD in config?
  Bit 1: force because add/remove HD was clicked
  Bit 2: socket_emu on
  Bit 3: mousehack on
  Bit 4: rtgmem on
  Bit 5: chipmem larger than 2MB
  
  gui_rtarea_flags_onenter is set before GUI is shown, bit 1 may change during GUI display.
*/
static int gui_rtarea_flags_onenter;

static int gui_create_rtarea_flag(struct uae_prefs *p)
{
  int flag = 0;
  
  if(count_HDs(p) > 0)
    flag |= 1;
  
	if (p->socket_emu)
		flag |= 4;

  if (p->input_tablet > 0)
    flag |= 8;

  if(p->rtgmem_size)
    flag |= 16;

  if (p->chipmem_size > 2 * 1024 * 1024)
    flag |= 32;

  return flag;
}

void gui_force_rtarea_hdchange(void)
{
  gui_rtarea_flags_onenter |= 2;
}

static void (*refreshFuncAfterDraw)(void) = NULL;

void RegisterRefreshFunc(void (*func)(void))
{
  refreshFuncAfterDraw = func;
}


namespace sdl
{
  void gui_init()
  {
    //-------------------------------------------------
    // Set layer for GUI screen
    //-------------------------------------------------
  	char tmp[20];
  	snprintf(tmp, 20, "%dx%d", GUI_WIDTH, GUI_HEIGHT);
  	setenv("SDL_OMAP_LAYER_SIZE", tmp, 1);
  	snprintf(tmp, 20, "0,0,0,0");
  	setenv("SDL_OMAP_BORDER_CUT", tmp, 1);

    //-------------------------------------------------
    // Create new screen for GUI
    //-------------------------------------------------
    #if defined (RASPBERRY)
    const SDL_VideoInfo* videoInfo = SDL_GetVideoInfo ();
    printf("Current resolution: %d x %d %d bpp\n",videoInfo->current_w, videoInfo->current_h, videoInfo->vfmt->BitsPerPixel);
    gui_screen = SDL_SetVideoMode(videoInfo->current_w,videoInfo->current_h,16,SDL_SWSURFACE |SDL_FULLSCREEN);
    #else
    gui_screen = SDL_SetVideoMode(GUI_WIDTH, GUI_HEIGHT, 16, SDL_SWSURFACE);
    #endif
    SDL_EnableUNICODE(1);
    SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
    SDL_ShowCursor(SDL_ENABLE);

    //-------------------------------------------------
    // Create helpers for guichan
    //-------------------------------------------------
    gui_imageLoader = new gcn::SDLImageLoader();
    gcn::Image::setImageLoader(gui_imageLoader);
    gui_graphics = new gcn::SDLGraphics();
    gui_graphics->setTarget(gui_screen);
    gui_input = new gcn::SDLInput();
    uae_gui = new gcn::Gui();
    uae_gui->setGraphics(gui_graphics);
    uae_gui->setInput(gui_input);
  }

  void gui_halt()
  {
    delete uae_gui;
    delete gui_imageLoader;
    delete gui_input;
    delete gui_graphics;
    
    SDL_FreeSurface(gui_screen);
    gui_screen = NULL;
  }

  void gui_run()
  {
    //-------------------------------------------------
    // The main loop
    //-------------------------------------------------
    while(gui_running)
    {
      //-------------------------------------------------
      // Check user input
      //-------------------------------------------------
      SDL_Event event;
      while(SDL_PollEvent(&event))
      {
    		if (event.type == SDL_QUIT)
    		{
          //-------------------------------------------------
          // Quit entire program via SQL-Quit
          //-------------------------------------------------
    			uae_quit();
    			gui_running = false;
    			break;
    		}

        else if (event.type == SDL_KEYDOWN)
        {
          gcn::FocusHandler* focusHdl;
          gcn::Widget* activeWidget;


          if (event.key.keysym.sym == currprefs.key_for_menu)
          {
            if(emulating && widgets::cmdStart->isEnabled())
            {
              //------------------------------------------------
              // Continue emulation
              //------------------------------------------------
              gui_running = false;
            }
            else
            {
              //------------------------------------------------
              // First start of emulator -> reset Amiga
              //------------------------------------------------
              uae_reset(0,1);
              gui_running = false;
            }
           } else
          switch(event.key.keysym.sym)
          {
            case SDLK_q:
              //-------------------------------------------------
              // Quit entire program via Q on keyboard
              //-------------------------------------------------
              focusHdl = gui_top->_getFocusHandler();
              activeWidget = focusHdl->getFocused();
              if(dynamic_cast<gcn::TextField*>(activeWidget) == NULL) {
          			// ...but only if we are not in a Textfield...
          			uae_quit();
          			gui_running = false;
          		}
        			break;

            case VK_ESCAPE:
            case VK_R:
              //-------------------------------------------------
              // Reset Amiga
              //-------------------------------------------------
              uae_reset(1,1);
              gui_running = false;
              break;

            case VK_X:
            case VK_A:
              //------------------------------------------------
              // Simulate press of enter when 'X' pressed
              //------------------------------------------------
              event.key.keysym.sym = SDLK_RETURN;
              gui_input->pushInput(event); // Fire key down
              event.type = SDL_KEYUP;  // and the key up
              break;

            case VK_UP:
              if(HandleNavigation(DIRECTION_UP))
                continue; // Don't change value when enter ComboBox -> don't send event to control
              break;
              
            case VK_DOWN:
              if(HandleNavigation(DIRECTION_DOWN))
                continue; // Don't change value when enter ComboBox -> don't send event to control
              break;
              
            case VK_LEFT:
              if(HandleNavigation(DIRECTION_LEFT))
                continue; // Don't change value when enter Slider -> don't send event to control
              break;
              
            case VK_RIGHT:
              if(HandleNavigation(DIRECTION_RIGHT))
                continue; // Don't change value when enter Slider -> don't send event to control
              break;
          }
        }

        //-------------------------------------------------
        // Send event to guichan-controls
        //-------------------------------------------------
        gui_input->pushInput(event);
      }

  		if(gui_rtarea_flags_onenter != gui_create_rtarea_flag(&changed_prefs))
        DisableResume();

      // Now we let the Gui object perform its logic.
      uae_gui->logic();
      // Now we let the Gui object draw itself.
      uae_gui->draw();
      // Finally we update the screen.
      wait_for_vsync();
      SDL_Flip(gui_screen);
      
      if(refreshFuncAfterDraw != NULL)
      {
        void (*currFunc)(void) = refreshFuncAfterDraw;
        refreshFuncAfterDraw = NULL;
        currFunc();
      }
    }
  }

}


namespace widgets 
{
  class MainButtonActionListener : public gcn::ActionListener
  {
    public:
      void action(const gcn::ActionEvent& actionEvent)
      {
			if (actionEvent.getSource() == cmdShutdown)
			{
				// ------------------------------------------------
				// Shutdown the host (power off)
				// ------------------------------------------------
				uae_quit();
				gui_running = false;
				host_shutdown();
			}
			else 
	      if (actionEvent.getSource() == cmdQuit)
	      {
          //-------------------------------------------------
          // Quit entire program via click on Quit-button
          //-------------------------------------------------
	        uae_quit();
  			  gui_running = false;
  			}
  			else if(actionEvent.getSource() == cmdReset)
			  {
          //-------------------------------------------------
          // Reset Amiga via click on Reset-button
          //-------------------------------------------------
    			uae_reset(1, 1);
    			gui_running = false;
        }
  			else if(actionEvent.getSource() == cmdRestart)
			  {
          //-------------------------------------------------
          // Restart emulator
          //-------------------------------------------------
          char tmp[MAX_PATH];
          fetch_configurationpath (tmp, sizeof (tmp));
          if(strlen(last_loaded_config) > 0)
            strcat (tmp, last_loaded_config);
          else
          {
            strcat (tmp, OPTIONSFILENAME);
            strcat (tmp, ".uae");
          }
    			uae_restart(0, tmp);
    			gui_running = false;
			  }
			  else if(actionEvent.getSource() == cmdStart)
			  {
			    if(emulating && widgets::cmdStart->isEnabled())
		      {
            //------------------------------------------------
            // Continue emulation
            //------------------------------------------------
            gui_running = false;
		      }
          else
          {
            //------------------------------------------------
            // First start of emulator -> reset Amiga
            //------------------------------------------------
      			uae_reset(0, 1);
      			gui_running = false;
          }
        }
      }
  };
  MainButtonActionListener* mainButtonActionListener;


  class PanelFocusListener : public gcn::FocusListener
  {
    public:
      void focusGained(const gcn::Event& event)
      {
        int i;
        for(i=0; categories[i].category != NULL; ++i)
        {
          if(event.getSource() == categories[i].selector)
          {
            categories[i].selector->setActive(true);
            categories[i].panel->setVisible(true);
            last_active_panel = i;
          }
          else
          {
            categories[i].selector->setActive(false);
            categories[i].panel->setVisible(false);
          }
        }
      }
  };
  PanelFocusListener* panelFocusListener;


  void gui_init()
  {
    int i;
    int yPos;

    //-------------------------------------------------
    // Define base colors
    //-------------------------------------------------
    gui_baseCol.r = 192;
    gui_baseCol.g = 192;
    gui_baseCol.b = 208;
    gui_baseColLabel.r = gui_baseCol.r;
    gui_baseColLabel.g = gui_baseCol.g;
    gui_baseColLabel.b = gui_baseCol.b;
    gui_baseColLabel.a = 192;
    colSelectorInactive.r = 255;
    colSelectorInactive.g = 255;
    colSelectorInactive.b = 255;
    colSelectorActive.r = 192;
    colSelectorActive.g = 192;
    colSelectorActive.b = 255;

    //-------------------------------------------------
    // Create container for main page
    //-------------------------------------------------
    gui_top = new gcn::Container();
    //gui_top->setDimension(gcn::Rectangle(0, 0, GUI_WIDTH, GUI_HEIGHT));
    gui_top->setDimension(gcn::Rectangle((gui_screen->w - GUI_WIDTH) / 2, (gui_screen->h - GUI_HEIGHT) / 2, GUI_WIDTH, GUI_HEIGHT));
    gui_top->setBaseColor(gui_baseCol);
    uae_gui->setTop(gui_top);

    //-------------------------------------------------
    // Initialize fonts
    //-------------------------------------------------
	  TTF_Init();
	  gui_font = new gcn::contrib::SDLTrueTypeFont("data/FreeSans.ttf", 14);
    gcn::Widget::setGlobalFont(gui_font);
    
  	//--------------------------------------------------
  	// Create main buttons
  	//--------------------------------------------------
    mainButtonActionListener = new MainButtonActionListener();

  	cmdQuit = new gcn::Button("Quit");
  	cmdQuit->setSize(BUTTON_WIDTH, BUTTON_HEIGHT);
    cmdQuit->setBaseColor(gui_baseCol);
    cmdQuit->setId("Quit");
    cmdQuit->addActionListener(mainButtonActionListener);

	cmdShutdown = new gcn::Button("Shutdown");
	cmdShutdown->setSize(BUTTON_WIDTH, BUTTON_HEIGHT);
	cmdShutdown->setBaseColor(gui_baseCol);
	cmdShutdown->setId("Shutdown");
	cmdShutdown->addActionListener(mainButtonActionListener);

   	cmdReset = new gcn::Button("Reset");
  	cmdReset->setSize(BUTTON_WIDTH, BUTTON_HEIGHT);
    cmdReset->setBaseColor(gui_baseCol);
  	cmdReset->setId("Reset");
    cmdReset->addActionListener(mainButtonActionListener);

   	cmdRestart = new gcn::Button("Restart");
  	cmdRestart->setSize(BUTTON_WIDTH, BUTTON_HEIGHT);
    cmdRestart->setBaseColor(gui_baseCol);
  	cmdRestart->setId("Restart");
    cmdRestart->addActionListener(mainButtonActionListener);

  	cmdStart = new gcn::Button("Start");
  	if(emulating)
  	  cmdStart->setCaption("Resume");
  	cmdStart->setSize(BUTTON_WIDTH, BUTTON_HEIGHT);
    cmdStart->setBaseColor(gui_baseCol);
  	cmdStart->setId("Start");
    cmdStart->addActionListener(mainButtonActionListener);

  	//--------------------------------------------------
    // Create selector entries
  	//--------------------------------------------------
  	int workAreaHeight = GUI_HEIGHT - 2 * DISTANCE_BORDER - BUTTON_HEIGHT - DISTANCE_NEXT_Y;
    selectors = new gcn::Container();
    selectors->setSize(150, workAreaHeight - 2);
    selectors->setBaseColor(colSelectorInactive);
    selectors->setFrameSize(1);
  	int panelStartX = DISTANCE_BORDER + selectors->getWidth() + 2 + 11;

  	panelFocusListener = new PanelFocusListener();
    for(i=0; categories[i].category != NULL; ++i)
    {
      categories[i].selector = new gcn::SelectorEntry(categories[i].category, categories[i].imagepath);
      categories[i].selector->setActiveColor(colSelectorActive);
      categories[i].selector->setInactiveColor(colSelectorInactive);
      categories[i].selector->setSize(150, 24);
      categories[i].selector->addFocusListener(panelFocusListener);
      
      categories[i].panel = new gcn::Container();
      categories[i].panel->setId(categories[i].category);
      categories[i].panel->setSize(GUI_WIDTH - panelStartX - DISTANCE_BORDER - 1, workAreaHeight - 2);
      categories[i].panel->setBaseColor(gui_baseCol);
      categories[i].panel->setFrameSize(1);
      categories[i].panel->setVisible(false);
    }

  	//--------------------------------------------------
  	// Initialize panels
  	//--------------------------------------------------
    for(i=0; categories[i].category != NULL; ++i)
    {
      if(categories[i].InitFunc != NULL)
        (*categories[i].InitFunc) (categories[i]);
    }

  	//--------------------------------------------------
    // Place everything on main form
  	//--------------------------------------------------
    gui_top->add(cmdReset, DISTANCE_BORDER, GUI_HEIGHT - DISTANCE_BORDER - BUTTON_HEIGHT);
    gui_top->add(cmdQuit, DISTANCE_BORDER + BUTTON_WIDTH + DISTANCE_NEXT_X, GUI_HEIGHT - DISTANCE_BORDER - BUTTON_HEIGHT);
    gui_top->add(cmdShutdown, DISTANCE_BORDER + 2 * BUTTON_WIDTH + 2 * DISTANCE_NEXT_X, GUI_HEIGHT - DISTANCE_BORDER - BUTTON_HEIGHT);
    gui_top->add(cmdStart, GUI_WIDTH - DISTANCE_BORDER - BUTTON_WIDTH, GUI_HEIGHT - DISTANCE_BORDER - BUTTON_HEIGHT);

    gui_top->add(selectors, DISTANCE_BORDER + 1, DISTANCE_BORDER + 1);
    for(i=0, yPos=0; categories[i].category != NULL; ++i, yPos += 24)
    {
      selectors->add(categories[i].selector,  0,  yPos);
      gui_top->add(categories[i].panel, panelStartX, DISTANCE_BORDER + 1);
    }

  	//--------------------------------------------------
  	// Activate last active panel
  	//--------------------------------------------------
  	categories[last_active_panel].selector->requestFocus();
  }


  void gui_halt()
  {
    int i;

    for(i=0; categories[i].category != NULL; ++i)
    {
      if(categories[i].ExitFunc != NULL)
        (*categories[i].ExitFunc) ();
    }

    for(i=0; categories[i].category != NULL; ++i)
      delete categories[i].selector;
    delete panelFocusListener;
    delete selectors;

    delete cmdQuit;
	delete cmdShutdown;
    delete cmdReset;
    delete cmdRestart;
    delete cmdStart;
   
    delete mainButtonActionListener;
    
    delete gui_font;
    delete gui_top;
  }
}


void RefreshAllPanels(void)
{
  int i;
  
  for(i=0; categories[i].category != NULL; ++i)
  {
    if(categories[i].RefreshFunc != NULL)
      (*categories[i].RefreshFunc) ();
  }
}


void DisableResume(void)
{
	if(emulating)
  {
    widgets::cmdStart->setEnabled(false);
    gcn::Color backCol;
    backCol.r = 128;
    backCol.g = 128;
    backCol.b = 128;
    widgets::cmdStart->setForegroundColor(backCol);
  }
}


void run_gui(void)
{
  gui_running = true;
  gui_rtarea_flags_onenter = gui_create_rtarea_flag(&currprefs);

  try
  {
    sdl::gui_init();
    widgets::gui_init();
    sdl::gui_run();
    widgets::gui_halt();
    sdl::gui_halt();
  }
  // Catch all Guichan exceptions.
  catch (gcn::Exception e)
  {
    std::cout << e.getMessage() << std::endl;
    uae_quit();
  }
  // Catch all Std exceptions.
  catch (std::exception e)
  {
    std::cout << "Std exception: " << e.what() << std::endl;
    uae_quit();
  }
  // Catch all unknown exceptions.
  catch (...)
  {
    std::cout << "Unknown exception" << std::endl;
    uae_quit();
  }
  if(quit_program > UAE_QUIT || quit_program < -UAE_QUIT)
  {
  	//--------------------------------------------------
    // Prepare everything for Reset of Amiga
  	//--------------------------------------------------
		currprefs.nr_floppies = changed_prefs.nr_floppies;
		
		if(gui_rtarea_flags_onenter != gui_create_rtarea_flag(&changed_prefs))
	    quit_program = -UAE_RESET_HARD; // Hardreset required...
  }
}
