#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <GL/gl.h>  // Header File For The OpenGL32 Library
#include <GL/glu.h> // Header File For The GLu32 Library
#include <SDL/SDL.h>
#include <SDL/SDL_timer.h>

#include "blobcollective.h"
#include "lp_vector2.h"
#include "lp_profiler.h"

#define APP_WIDTH 640
#define APP_HEIGHT 480

static void
sdl_init(int width, int height)
{
  if(SDL_Init(SDL_INIT_VIDEO) < 0)
  {
    fprintf(stderr, "Unable to initialize SDL: %s\n", SDL_GetError());
    exit(1);
  }

  if ( SDL_SetVideoMode(width, height, 0, SDL_OPENGL) == NULL )
  {
    fprintf(stderr, "Unable to create OpenGL screen: %s\n", SDL_GetError());
    SDL_Quit();
    exit(2);
  }

  SDL_WM_SetCaption("Blob Sallad", NULL);
  SDL_ShowCursor(1);
}

static void
gl_init(int width, int height)
{
  glShadeModel(GL_SMOOTH);

  glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
  glClearDepth(1.0);
  glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
  glEnable(GL_BLEND);
  glEnable(GL_LINE_SMOOTH);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}

typedef struct screenconfig_st
{
  int dirty;

  float midX, midY;
  float minX, maxX;
  float minY, maxY;
  float zoom_factor;

  // on screen coordinates used to set the glViewport
  int winWidth, winHeight;
} screenconfig_t;

static void
update_screenconfig(screenconfig_t* config)
{
  if(!config->dirty)
    return;

  config->dirty = 0;

  glViewport(0, 0, config->winWidth, config->winHeight);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();

  glOrtho(config->minX, config->maxX, config->minY, config->maxY, -1.0f, 1.0f);
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
}

static void
zoom_in(screenconfig_t* config)
{
  if(config->zoom_factor < 2.0f)
  {
    config->dirty = 1;
    config->zoom_factor /= 0.95f;
  }
}

static void
zoom_out(screenconfig_t* config)
{
  if(config->zoom_factor > 0.5f)
  {
    config->dirty = 1;
    config->zoom_factor *= 0.95f;
  }
}
/*
static void
pan(screenconfig_t* config, int mouseX, int mouseY, int timeDelta)
{
  if(mouseX < 5 && config->minX > -0.5f)
  {
    config->dirty = 1;
    config->minX -= 0.0005f + 0.0015f * (float) timeDelta;
    config->maxX -= 0.0005f + 0.0015f * (float) timeDelta;
  }
  else if(mouseX > (config->winWidth - 5) && config->minX < 1.5f)
  {
    config->dirty = 1;
    config->minX += 0.0005f + 0.0015f * (float) timeDelta;
    config->maxX += 0.0005f + 0.0015f * (float) timeDelta;
  }
}
*/

int
main(int argc, char *argv[])
{
  int running = 1;
  SDL_Event event;
  int mouseX, mouseY;
  screenconfig_t config;
  BlobCollective collective(0.5f, 0.5f, 0.4f);
  Env env(0.0f, (float)APP_WIDTH/(float)APP_HEIGHT, 0.0f, 1.0f);
  LP_Vector2 gravity(0.0f, -0.5f);

  sdl_init(APP_WIDTH, APP_HEIGHT);
  gl_init(APP_WIDTH, APP_HEIGHT);

  config.dirty = 1;
  config.minX = 0.0f;
  config.minY = 0.0f;
  config.maxX = (float)APP_WIDTH/(float)APP_HEIGHT;
  config.maxY = 1.0f;
  config.winWidth = APP_WIDTH;
  config.winHeight = APP_HEIGHT;
  config.zoom_factor = 1.0f;

  unsigned int prevTime = SDL_GetTicks();
  unsigned int currentTime = SDL_GetTicks();

  int startTime = SDL_GetTicks();
  float dtBase = 0.01f;
  float minTime = 5.0f;
  float punch = 9.0f;
  float gravVal = 0.25f;

  while(running)
  {
    prevTime = currentTime;
    currentTime = SDL_GetTicks();
    float dt = 0.05f;

    float timeDiff = (float)(currentTime - startTime);
    if(timeDiff < minTime)
      continue;

    // dt = 0.15 at 50 fps
    dt = 0.05f; //timeDiff * dtBase;
    if(dt > 0.15)
      dt = 0.15f;

    startTime = currentTime;

    // draw
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    collective.Draw();
    SDL_GL_SwapBuffers();

    update_screenconfig(&config);

    // physics
    collective.Move(dt);
    collective.Satisfy(env);

    collective.SetForce(gravity);

    // pan(&config, mouseX, mouseY, currentTime - prevTime);
  }

  SDL_Quit();

  return 0;
}
