#include <unistd.h>
#include <stdlib.h>
#include "entity.h"
#include "SDL.h"
#include <glib.h>


#include "sdl-common.h"

static int window_created = FALSE;

static gint sdl_refresh (ENode * node, EBuf * attr, EBuf * value);


/* Normalizes a rectangle so it is contained within the screen.
 * If a rectangle is outside the screen it will be shrunk to fit
 * inside the screen. */
inline void normalize_rect(SDL_Surface * screen, SDL_Rect * rect)
{
  if (0 > rect->x)
    {
      rect->x = 0;
    }

  if (rect->x > screen->w)
    {
      rect->x = screen->w;
    }

  if ((rect->x + rect->w) > screen->w)
    {
      rect->w = screen->w - rect->x;
    }

  if (0 > rect->y )
    {
      rect->y = 0;
    }

  if (rect->y > screen->h )
    {
      rect->y = screen->h;
    }
  if ((rect->y + rect->h) > screen->h)
    {
      rect->h = screen->h - rect->y;
    }
}



/* A traditional sdl mainloop for those who want it. */
static gint
sdl_traditional_mainloop (gpointer data)
{
  SDL_Event event;
  Uint8 *keys;

  sdl_refresh (data, NULL, NULL);

  SDL_PollEvent (&event);

  if ( event.type == SDL_QUIT )
    {
      /* Death to us here. */
      SDL_Quit ();
      exit(0);
      /* ondelete happens .*/
    }

  keys = SDL_GetKeyState(NULL);

  if ( keys[SDLK_ESCAPE] == SDL_PRESSED ) {
    SDL_Quit();
    exit(0);
  }


  return TRUE;
}


/**sdl_refresh
 * Does the actual drawing.
 */
static gint
sdl_refresh (ENode * node, EBuf * attr, EBuf * value)
{
  SDL_Surface *screen = NULL;
  sdl_item * item = NULL;
  GPtrArray * item_list = NULL;
  static SDL_Rect rects[2048];
  int refresh_counter = 0;
  int array_length = 0;
  int counter = 0;
 
  /* Get our SDL surface */
  screen = enode_get_kv (node, "surface");

  /* Get item list. */ 
  item_list = enode_get_kv (node, "item_list");

  if (NULL != item_list)
    {
      array_length = item_list->len;
    }

  /* If we can get a valid screen, attempt to render to it. */
  if (NULL != screen)
    {
      if ( SDL_MUSTLOCK (screen) )
        {
          if ( SDL_LockSurface (screen) < 0 )
            {
              /* bad things are happening. */
              return FALSE;
            }
        }

      /* Step through our refresh needed items. */
      for (counter = 0 ; counter <array_length; counter++)
        {      
          /* Render. */
          item = g_ptr_array_index (item_list, counter);

          if (TRUE == item->needs_refresh)
            {
              switch (item->type)
                {
                  case SDL_SPRITE:
                  sdl_sprite_render (&rects[refresh_counter], screen, item);
                  break;
                  case SDL_RECTANGLE:
                  sdl_rectangle_render (&rects[refresh_counter], screen, item);
                  break;
                }

              normalize_rect(screen, &rects[refresh_counter]);
              refresh_counter++;
            }
        }

      /* Update SDL Window. */
      if ( SDL_MUSTLOCK (screen) )
        {
          SDL_UnlockSurface (screen);
        }

      if (0 < refresh_counter) /* Have something to update. */
        {
          /* Update the screen. */
          SDL_UpdateRects (screen, refresh_counter, &(rects[0]));
        }
      
      return TRUE;
    }

  /* No surface to write to. */
  return FALSE;
}


/**sdl_abort_check
 * Does the SDL event handling for abort checking. 
 */
gint sdl_abort_check (gpointer data)
{
  SDL_Event event;
  Uint8 *keys;

  SDL_PollEvent (&event);

  if ( event.type == SDL_QUIT )
    {
      /* Death to us here. */ 
      SDL_Quit ();
      exit(0);
      /* ondelete happens .*/
    }

  keys = SDL_GetKeyState(NULL);

  if ( keys[SDLK_ESCAPE] == SDL_PRESSED ) {
    SDL_Quit();
    exit(0);
  }

  return TRUE;

}

static void
sdl_window_destroy (ENode * node)
{
}


static void
rendsdl_window_render (ENode * node)
{
  gchar * width_str = NULL;
  gchar * height_str = NULL;
  gchar * refresh_interval = NULL;
  int width = 0;
  int height = 0;
  int bitmask = 0;
  char * is_true = NULL;
  SDL_Surface *screen = NULL;
  GPtrArray * garray = NULL;

  if (window_created == FALSE) /* No windows created yet. */
    {
      /* Get the size of the window. */
      width_str = enode_attrib_str (node, "width", NULL);
      height_str = enode_attrib_str (node, "height", NULL);

      if (NULL == width_str || NULL == height_str )
        {
          return;
        }

      width = atoi (width_str);
      height = atoi (height_str);


      is_true = enode_attrib_str (node, "fullscreen", NULL);
      if ((NULL != is_true) && (0 == strcmp ("true", is_true)))
         {
            bitmask = SDL_FULLSCREEN;
         }

      /* Store the enode. */
      enode_set_kv (node, "node", node);

      /* Initialize the display . */
      screen = SDL_SetVideoMode (width, height, 0, SDL_HWSURFACE|bitmask);

      if (NULL != screen)/* Store the surface. */
        {
          enode_set_kv (node, "surface", screen);
        }

      /* Setup our item array. */
      garray = g_ptr_array_new ();
      if (NULL != garray)
        {
          enode_set_kv (node, "item_list", garray);
        }


      /* Looking for a refresh interval which mean traditional mainloop. */
      refresh_interval = enode_attrib_str (node, "refresh", NULL);

      /* If they want a traditional mainloop. */
      if (NULL != refresh_interval)
        {
          /* So events won't be lost. */
          g_timeout_add (atoi (refresh_interval), 
		sdl_traditional_mainloop, node);
        }
      else
        {
          /* Setup the saftey abort callback. */ 
          g_idle_add (sdl_abort_check, node);
        }

      /* Only allow one SDL window per program. */
      window_created = TRUE;
    }
}



/* SDL renderer initalization.*/
void
renderer_init (RendererFlags flags)
{
  Element *element;
  ElementAttr *e_attr;

  if (flags & RENDERER_INIT)
    {
      /* Initialize the SDL library */
      if ( SDL_Init (SDL_INIT_VIDEO | SDL_INIT_TIMER) < 0 )
        {
          /* Warn here. */
          fprintf(stderr, "SDL init failed\n");
          fprintf(stderr, "SDL error:%s\n", SDL_GetError());
        }
    }

  if (flags & RENDERER_REGISTER)
    {
      /*mainloop_register ("sdl_poll_events", sdl_poll_events, NULL, NULL);*/

      /* Register sdl-window. */
      element = g_new0 (Element, 1);
      element->render_func = rendsdl_window_render;
      element->destroy_func = sdl_window_destroy;
      element->parent_func = NULL;
      element->tag = "sdl-window";
      element->description = "Create a new SDL window.";
      element_register (element);

      e_attr = g_new0 (ElementAttr, 1);
      e_attr->attribute = "fullscreen";
      e_attr->description = "Fullscreen boolean";
      e_attr->value_desc = "boolean";
      e_attr->possible_values = "true,false";
      e_attr->set_attr_func = NULL;
      element_register_attrib (element, e_attr);

      e_attr = g_new0 (ElementAttr, 1);
      e_attr->attribute = "width";
      e_attr->description = "Width of the window";
      e_attr->value_desc = "integer";
      e_attr->possible_values = "-1,*";
      e_attr->set_attr_func = NULL;
      element_register_attrib (element, e_attr);

      e_attr = g_new0 (ElementAttr, 1);
      e_attr->attribute = "height";
      e_attr->description = "Height of the window.";
      e_attr->value_desc = "integer";
      e_attr->possible_values = "-1,*";
      e_attr->set_attr_func = NULL;
      element_register_attrib (element, e_attr);

      e_attr = g_new0 (ElementAttr, 1);
      e_attr->attribute = "refresh";
      e_attr->description = "Refresh interval in milliseconds.";
      e_attr->value_desc = "integer";
      e_attr->possible_values = "-1,*";
      e_attr->set_attr_func = NULL;
      element_register_attrib (element, e_attr);

      e_attr = g_new0 (ElementAttr, 1);
      e_attr->attribute = "_refresh";
      e_attr->description = "A force refresh";
      e_attr->value_desc = "integer";
      e_attr->possible_values = "*";
      e_attr->set_attr_func = sdl_refresh;
      element_register_attrib (element, e_attr);
      /* End sdl-window register. */

      /* Register other renders here. */
      sdl_sprite_register();
      sdl_rectangle_register();
      sdl_mouse_register();
      sdl_keys_register();
  }
}


syntax highlighted by Code2HTML, v. 0.9.1