/*
   spacemaze - Fly a space ship through a maze and collect all the items.
   Copyright (C) 2000, 2001, 2002 John Ericson

   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

   2000-03-25 High / John Ericson john.ericson@home.se
*/

#include <stdlib.h>
#include <string.h>

#include <SDL.h>
#include <SDL_image.h>

#include "bool.h"
#include "sprite.h"
#include "campaign.h"
#include "level.h"
#include "defines.h"
#include "getfile.h"
#include "loadimg.h"
#include "gfx.h"
#include "fpstimer.h"

#include "BFont.h"

#ifdef _WIN32
   #include "getopt.h"
#else /* UNIX */
   #include <getopt.h>
#endif

void LoadGraphics(void);

/* Different default places to search for levels */
const char *levels_dirs[6] = {
   "./",
   "./levels/",
   "/usr/share/games/spacemaze/levels/",
   "/usr/local/share/games/spacemaze/levels/",
   "/opt/spacemaze/levels/",
   NULL
};

const char *campaigns_dirs[6] = {
   "./",
   "./campaigns/",
   NULL
};


const char *gfx_dirs[3] = {
   "./",
   "./gfx/",
   NULL
};

/* Video buffer */
SDL_Surface *screen;

Uint32 videoflags = SDL_SWSURFACE|SDL_HWPALETTE;

/*
SDL_Surface *image_spaceship,
            *image_box;
*/

/* Structure to cache images */
typedef struct {
   int used;
   SDL_Surface *image;
} image_s;

image_s images[MAXIMAGES];

struct struct_ship {
   float fx,     /* Position */
         fy;
   int   x,
         y;
   int   w,    /* Size */
         h;
   float oldx,  /* Old position */
         oldy;
   float vx,    /* Velocity */
         vy;
   float gasx,  /* Gas */
         gasy;
   bool  dead;
   bool  notmoving;
   int   color;
   sprite sprite;
} ship;

static level_s level;
static campaign_s campaign;

unsigned int currentlevel = 0;

int borderwidth = 1;

//struct struct_drawrect drawrect[MAXDRAWRECT];

/* Draw level.
   If level piece is bigger than the screen, no drawing will be made.
*/
void drawlevel(void) {
   int i;
   SDL_Rect rect;

   for (i = 0; i <= level.size; i++) {
      if (level.landscape[i].visible) {

         rect.x = level.landscape[i].x;
         rect.y = level.landscape[i].y;
         rect.w = level.landscape[i].w;
         rect.h = level.landscape[i].h;

         SDL_FillRect(screen, &rect, level.landscape[i].color);
         inputdrawrect(&rect);
      }
   }
}

void drawship(void)
   { drawsprite(&ship.sprite); }

void eraseship(void)
   { erasesprite(&ship.sprite); }

/*
void drawbox(int n)
   { drawsprite(&level.item[n].sprite); }
*/
void erasebox(int n) {
   erasesprite(&level.item[n].sprite); }

/*
void drawboxes(void) {
   int i;

   for (i = 0; i <= level.numitems; i++)
      if (level.item[i].type == LEVEL_OBJ_BOX)
         if (level.item[i].visible)
            drawbox(i);
}
*/

/* Check if two rectangular objects intersect.
   For optimal collision detection, the smallest object 
   should be 1 and the larger 2.
*/
int detectcollision(int x1, int y1, int w1, int h1, int x2, int y2, int w2, int h2) {
   if ((x1 >= x2 && x1 < (x2 + w2)) ||
      ((x1 + w1) >= x2 && (x1 + w1) < (x2 + w2)))
      if ((y1 >= y2 && y1 < (y2 + h2)) ||
         ((y1 + h1) >= y2 && (y1 + h1) < (y2 + h2)))
         return 1;

   return 0;
}


/* Load level */
void loadlevel(const char *file, level_s *level) {

   char fullfile[512];
   SDL_Color colors[256];

   /* Check if player has played through the whole campaign */
   if (strcmp(file, "") == 0) {
      printf("You have played through the complete game now\n");
      exit(0);
   }

   if (strcmp(file, "") != 0) {

      /* Look through directories for the file to load */
      if (!getfile(levels_dirs, file, fullfile)) {
         fprintf(stderr, "Error couldn't find %s\n", file);
         exit(1);
      } else {
         printf("%s exists\n", fullfile);
      }

      /* Load level */
      if (LoadLevel(level, fullfile) != 0) {
         fprintf(stderr, "Error parsing XML file %s\n", fullfile);
         exit(1);
      }
      /* Check level version */
      if (!strcmp(level->version, "1.0") == 0) {
         fprintf(stderr, "Error unknown level version. This game can run version 1.0 levels\n");
         exit(1);
      }

      printf("Loaded level %s name %s.\n", file, level->name);
   } else {
      fprintf(stderr, "Error no map to open, please use -m\n");
      exit(1);
   }

   /* If the screen resolution differs set new videomode */
   if (screen == NULL ||
      level->sizew != screen->w ||
      level->sizeh != screen->h) {

      /* Set videomode */
      if ((screen = SDL_SetVideoMode(level->sizew, level->sizeh,
         8, videoflags)) == NULL) {
         fprintf(stderr, "Couldn't initialize video mode: %s\n", SDL_GetError());
         exit(1);
      }

      /* Report graphics settings */
      printf("Set %dx%dx%d video mode\n",
         screen->w, screen->h, screen->format->BitsPerPixel);

      /***************/
      /* Set palette */
      /***************/

      /* Hopefully the loading of pictures wont mess this palette up */
      colors[0].r =     colors[0].g =     colors[0].b = 0;
      colors[1].r = 200;colors[1].g = 0;  colors[1].b = 0;
      colors[2].r = 0;  colors[2].g = 0;  colors[2].b = 150;
      colors[3].r = 200;colors[3].g = 200;colors[3].b = 0;
      colors[4].r = 0;  colors[4].g = 200;colors[4].b = 0;

      SDL_SetColors(screen, colors, 0, 5); /* Sets a portion of the colormap
                                               for the given 8-bit surface.
                                               Last digit is how many colours to set.
                                            */
   }

}

/* Start level */
void initlevel() {

   SDL_Rect trect;
   char caption[255] = CAPTION;

   /* Set title and icon for the window */
   strcat(caption, " - ");
   strcat(caption, level.name);
   SDL_WM_SetCaption(caption, NULL);

   /* Initialize ship */
   //ship.x = &ship.sprite.x;
   //ship.y = &ship.sprite.y;


   // TODO When I assign these to xf? It crapes out the value in h
   // These should be assigned to fx.
   ship.x    = level.playerstart[level.numstartpos].x;
   ship.y    = level.playerstart[level.numstartpos].y;

   ship.fx = (float)ship.x;
   ship.fy = (float)ship.y;

   ship.sprite.x = ship.x;
   ship.sprite.y = ship.y;

   ship.w = ship.sprite.image->w;
   ship.h = ship.sprite.image->h;

   ship.oldx  = 100;
   ship.oldy  = 100;

   ship.vx    = 0;
   ship.vy    = 0;
   ship.gasx  = (float)0.1;
   ship.gasy  = (float)0.1;
   ship.dead  = false;
   ship.notmoving = true;
   //ship.color = 1;

   /* Clear all stacked drawings */
   cleardrawrect();

   /* Draw borders */

   // Draw boarder around the screen
   trect.x = 0;
   trect.y = 0;
   trect.w = screen->w;
   trect.h = screen->h;
   SDL_FillRect(screen, &trect, 4);

   trect.x = borderwidth;
   trect.y = borderwidth;
   trect.w = screen->w - 1 - borderwidth;
   trect.h = screen->h - 1 - borderwidth;
   SDL_FillRect(screen, &trect, 0);

   LoadGraphics();

   drawlevel();
   drawship();

   /* Locks the screen */
   /*if (SDL_MUSTLOCK(screen))
      SDL_LockSurface(screen);
   */
   //SDL_UpdateRect(screen, 0, 0, 0, 0);

   /* Unlocks the screen */
   /*if (SDL_MUSTLOCK(screen))
      SDL_UnlockSurface(screen);
   */

   /* Redraw */
   updatescreen();
}


/* Check if the ship picks up any boxes
*/
void shipgrabboxes(void) {
   int i, i2, i3;
   SDL_Rect rect;
   bool alltaken = true;

   /* Look thru items */
   for (i = 0;i <= level.numitems;i++)
      /* If ship collides with an existing box */
      if (level.item[i].type == LEVEL_OBJ_BOX && level.item[i].visible)
         if (detectcollision(level.item[i].x, level.item[i].y,
            level.item[i].sprite.image->w, level.item[i].sprite.image->h,
            ship.x, ship.y, ship.w, ship.h)) {

            printf("Got box number %d\n", i);
            level.item[i].visible = false;

            /* If something should be activated */
            if (level.item[i].activate)
               /* Look thru landscape */
               for (i2 = 0; i2 <= level.size; i2++)
                  if (level.landscape[i2].activate == level.item[i].activate) {
                     /* Change visible and crashable */
                     if (level.landscape[i2].visible)
                        level.landscape[i2].visible = false;
                     else
                        level.landscape[i2].visible = true;

                     if (level.landscape[i2].crashable)
                        level.landscape[i2].crashable = false;
                     else
                        level.landscape[i2].crashable = true;

                     /* If landscape is changed to unvisible redraw that area */
                     if (!level.landscape[i2].visible) {

                        level.landscape[i2].visible = false;

                        rect.x = level.landscape[i2].x;
                        rect.y = level.landscape[i2].y;
                        rect.w = level.landscape[i2].w;
                        rect.h = level.landscape[i2].h;

                        /* Erase landscape */
                        SDL_FillRect(screen, &rect, 0);
                        inputdrawrect(&rect);
                       }
                  }
            /* Look if all items are taken */
            for (i3 = 0; i3 <= level.numitems; i3++)
               if (level.item[i3].type == LEVEL_OBJ_BOX)
                  if (level.item[i3].visible)
                     alltaken = false;

            erasebox(i); /* Erase the picked up box */
            drawlevel(); /* Redraw level */

            /* All items are taken */
            if (alltaken) {
               printf("Congratulations! You have picked up all the boxes\n");

               loadlevel(campaign.levels[currentlevel++].filename, &level);

               initlevel();
            }
         }
}

/* See if the ship collides with any wall
*/
int shipcrash(void) {
   int i;

   for (i = 0; i <= level.size; i++)
      if (level.landscape[i].crashable)
         if (detectcollision(ship.x, ship.y, ship.w, ship.h,
            level.landscape[i].x, level.landscape[i].y,
            level.landscape[i].w, level.landscape[i].h)) {

            printf("Ship crashed into a wall\n");
            ship.dead = true;
            return 1 ;
         }

   return 0;
}


void ImageCacheInit(image_s *images) {
   int i;

   for (i = 0; i < MAXIMAGES; i++, *images++) {
      images->used = 0;
   }
}


SDL_Surface *loadimgdata(const char *file, bool transparent) {

   SDL_Surface *rtnsurface;
   char fullfile[512];

   if (!getfile(gfx_dirs, file, fullfile)) {
      fprintf(stderr, "Error couldn't find %s\n", file);
      exit(1);
   } else {
      printf("%s exists\n", fullfile);
   }

   /* Load graphics */
   rtnsurface = loadimage(fullfile, transparent);
   if (rtnsurface == NULL) {
      fprintf(stderr, "Error couldn't load %s\n", fullfile);
      exit(1);
   }
   return rtnsurface;
}


void LoadGraphics(void) {

   int i;

   // TODO level.numitems is wrong. It starts at 1, it should start at 0
   for (i = 0;i <= level.numitems; i++) {
      switch (level.item[i].type) {

         case LEVEL_OBJ_BOX:
            if (images[LEVEL_OBJ_BOX].used == 0) {
               images[LEVEL_OBJ_BOX].image = loadimgdata("box.png", false);
               if (images[LEVEL_OBJ_BOX].image != NULL)
                  printf("Loaded image box.png\n");
            } else
               printf("Reused image box.png\n");

            /* Assign image */
            level.item[i].sprite.image = images[level.item[i].type].image;

            break;
         case LEVEL_OBJ_LANDINGPLATE:
            if (images[LEVEL_OBJ_LANDINGPLATE].used == 0) {
               images[LEVEL_OBJ_LANDINGPLATE].image = loadimgdata("landingplate.png", false);
               if (images[LEVEL_OBJ_LANDINGPLATE].image != NULL)
                  printf("Loaded image landingplate.png\n");
            } else
               printf("Reused image landingplate.png\n");

            /* Assign image */
            level.item[i].sprite.image = images[level.item[i].type].image;

            /* Transform the coordinates so they are middle lower placed */
            level.item[i].x -= (int)(level.item[i].sprite.image->w / 2);
            level.item[i].y -= level.item[i].sprite.image->h - 1;

            break;
      }

      /* Increase the usage to show how much this file is needed */
      if (images[level.item[i].type].used != -1)
         images[level.item[i].type].used++;

      /* Assign cordinates to sprite */
      level.item[i].sprite.x = level.item[i].x;
      level.item[i].sprite.y = level.item[i].y;
   }
}


/* Draw text on the screen
*/
void DrawText(unsigned int x, unsigned int y, char *text, ...) {

   SDL_Rect rect;

   PrintString(screen, x, y, text);

   rect.x = x;
   rect.y = y;
   rect.w = TextWidth(text);
   rect.h = FontHeight(GetCurrentFont());

   inputdrawrect(&rect);
}

void drawlandscapeobjects(void) {

   int i;

   for (i = 0;i <= level.numitems; i++) {

      switch (level.item[i].type) {
         case LEVEL_OBJ_BOX:
            if (!level.item[i].visible) break;
         case LEVEL_OBJ_LANDINGPLATE:

            drawsprite(&level.item[i].sprite);
            break;
      }
   }
}

/* Show how to use the program
*/
void usage(void) {
   printf("spacemaze 0.9 Copyright (C) 2000 John Ericson john.ericson@home.se\n");
   printf("Usage: spacemaze [OPTIONS]...\n");
   printf("\n");
   printf("  -h    display this help\n");
   printf("  -f    use fullscreen\n");
   printf("  -m .. map to open\n");
   printf("  -c    display mouse cursor inside application window\n");
}

/* Main program
*/
int main(int argc, char *argv[]) {
   SDL_Event event;

   Uint8 *keys;

   //int key;
   //int numkeys;

   bool done;
   int i;
   //int readx,
   //    ready;

   int c;
   char leveltoload[255] = DEFAULTLEVEL;
   char campaigntoload[255] = DEFAULTCAMPAIGN;
   int drawcursor = 0;

   char file[512]; /* Stores complete path */


   BFont_Info *Font1=NULL;


   /**************************************/
   /* Process the command line arguments */
   /**************************************/

   while (1) {
      c = getopt(argc, argv, "fm:hck");

      /* Missing argument on an option that requires a argument */
      if (c == '?') exit(1);
      /* End of command line */
      if (c == EOF) break;

      switch(c) {
         case 'f':
            videoflags |= SDL_FULLSCREEN;
            break;
         case 'm':
            strcpy(leveltoload, optarg);
            break;
         case 'h':
            usage();
            exit(1);
            break;
         case 'c':
            drawcursor = 1;
            break;
         case 'k':
            strcpy(campaigntoload, optarg);
            break;
         default:
            fprintf(stderr, "Error getopt returned character code 0%o\n", c);
      }
   }

   /***********************/
   /* Initialize hardware */
   /***********************/

   /* Initialize SDL */
   if (SDL_Init(SDL_INIT_VIDEO) < 0) {
      fprintf(stderr, "Couldn't initialize SDL: %s\n",SDL_GetError());
      exit(1);
   }


   /* Initialize graphich system */
   initdrawrect(MAXDRAWRECT);
   cleardrawrect();

   /***************/
   /* Load levels */
   /***************/

   /* Check if used has specified a specific level */
   if (strcmp(leveltoload, "") == 0) {

      /* Load a campaign */

      /* Initialize campaign */
      if (strcmp(campaigntoload, "") != 0) {

         /* Look through directories for the file to load */
         if (!getfile(campaigns_dirs, campaigntoload, file)) {
            fprintf(stderr, "Error couldn't find campaign %s\n", campaigntoload);
            exit(1);
         } else {
            printf("%s exists\n", file);
         }

         /* Load campaign */
         if (LoadCampaign(&campaign, file) != 0) {
            fprintf(stderr, "Error parsing XML file %s\n", file);
            exit(1);
         }

         /* Check campaign version */
         if (!strcmp(campaign.version, "1.0") == 0) {
            fprintf(stderr, "Error unknown campaign version. This game can run version 1.0 campaigns\n");
            exit(1);
         }

         printf("Loaded campaign %s name %s.\n", campaigntoload, campaign.name);
      } else {
         fprintf(stderr, "Error no campaign to open, please use -k\n");
         exit(1);
      }

   } else {

      /* Load a level */

      /* Setup a dummy campaign for our custom level */
      strcpy(campaign.name, "custom level");
      campaign.numlevels = 1;
      strcpy(campaign.levels->filename, leveltoload);
   }

   /* Initialize level */
   loadlevel(campaign.levels[currentlevel++].filename, &level);

   /* Initialize image cache */
   ImageCacheInit((image_s *)&images);

   /*****************/
   /* Load graphics */
   /*****************/

   /* Ship */
   images[OBJ_SPACESHIP].image = loadimgdata("spaceship.png", false);
   images[OBJ_SPACESHIP].used = -1;

   ship.sprite.image = images[OBJ_SPACESHIP].image;


   //LoadGraphics();


   /**************/
   /* Load Fonts */
   /**************/

   /* Look through directories for the level to load */
   if (!getfile(gfx_dirs, "topaz.png", file)) {
      fprintf(stderr, "Error couldn't find font topaz.png\n");
      exit(1);
   } else {
      printf("%s exists\n", file);
   }

   /* Load Font */
   Font1 = LoadFont(file);
   if (Font1 == NULL) {
      fprintf(stderr, "Couldnt load %s\n", file);
      exit(1);
    }


   /* Set the default font */
    SetCurrentFont(Font1);

   /*************************/
   /* Misc program settings */
   /*************************/

   SDL_ShowCursor(drawcursor); /* Don't draw mouse cursor inside application window */

   /************************************/
   /* Set keyboard delays for gameplay */
   /************************************/

   /* Set keyboard delays.
      Delay for when to start sending repeating KEYDOWN's 
      and the interval between them */
   SDL_EnableKeyRepeat(1000, 200);

   keys = SDL_GetKeyState(NULL);

   /*******************/
   /* The game begins */
   /*******************/

   initlevel();


   /* Main loop */
   done = false;

   while (!done) {

      /* Polls for currently pending events */
      if (SDL_PollEvent(&event)) {

         switch (event.type) {

         case SDL_KEYDOWN:
         case SDL_KEYUP:

            keys = SDL_GetKeyState(NULL);

            break;

         case SDL_QUIT:             /* Quit request */
            printf("Quit requested, quitting.\n");
            exit(0);
            break;
         }
      }

      if (keys[SDLK_ESCAPE] == SDL_PRESSED) {
         printf("Quit requested, quitting.\n");
         exit(0);
      }
      if (keys[SDLK_UP] == SDL_PRESSED) {
         ship.vy = ship.vy - ship.gasy;
         ship.notmoving = false;
      }
      if (keys[SDLK_DOWN] == SDL_PRESSED) {
         ship.vy = ship.vy + ship.gasy;
         ship.notmoving = false;
      }
      if (keys[SDLK_LEFT] == SDL_PRESSED) {
         ship.vx = ship.vx - ship.gasx;
         ship.notmoving = false;
      }
      if (keys[SDLK_RIGHT] == SDL_PRESSED) {
         ship.vx = ship.vx + ship.gasx;
         ship.notmoving = false;
      }

      waitframe();

      if (ship.dead)
         ship.vx = ship.vy = 0;

      /* Save old positions */
      ship.oldx = (float)ship.x;
      ship.oldy = (float)ship.y;


      /* Locks the screen */
      if (SDL_MUSTLOCK(screen))
         if (SDL_LockSurface(screen) < 0)
            continue;

      /*
      IMPORTANT!

      No operating system or library calls should be made here
      since critical system locks may be held during this time.    
      */


      /* Move the ship */
      if (!ship.notmoving) {

         eraseship();

         ship.fx += ship.vx;
         ship.fy += ship.vy;
         ship.x = (int)ship.fx;
         ship.y = (int)ship.fy;

         /* Add gravity */
         ship.vy += level.gravity;

         /* Look if the spaceship hit any wall */
         if ((ship.x + (int)ship.w) >= (screen->w - borderwidth)) {
            ship.vx = ship.vx * -1;
            ship.fx =  (float)(screen->w - borderwidth - (int)ship.w);
         } else if (ship.x <= (borderwidth)) {
            ship.vx = ship.vx * -1;
            ship.fx = (float)borderwidth + 1;
         }
         if ((ship.y + (int)ship.h) >= (screen->h - borderwidth)) {
            ship.vy = ship.vy * -1;
            ship.fy =  (float)(screen->h - borderwidth - (int)ship.h);
         } else if (ship.y <= (borderwidth)) {
            ship.vy = ship.vy * -1;
            ship.fy = (float)borderwidth + 1;
         }


         ship.x = (int)ship.fx;
         ship.y = (int)ship.fy;
         ship.sprite.x = ship.x;
         ship.sprite.y = ship.y;

         /* Look if the spaceship has landed on any landingplate */
         for (i = 0;i <= level.numitems; i++) {
            if (level.item[i].type == LEVEL_OBJ_LANDINGPLATE) {
               if (detectcollision(ship.x, ship.y+ship.h, 0, 0,
                  level.item[i].x, level.item[i].y,
                  level.item[i].sprite.image->w, level.item[i].sprite.image->h) &&
                  detectcollision(ship.x+ship.w, ship.y+ship.h, 0, 0,
                  level.item[i].x, level.item[i].y,
                  level.item[i].sprite.image->w, level.item[i].sprite.image->h)) {

                  /* Ship landed successfully */
                  ship.notmoving = true;
                  ship.vx = 0;
                  ship.vy = 0;
                  ship.fy = (float)level.item[i].y - ship.h;

                  DrawText(10, 150, "You have landed");
               }
            }
         }

         ship.x = (int)ship.fx;
         ship.y = (int)ship.fy;
         ship.sprite.x = ship.x;
         ship.sprite.y = ship.y;

         if (shipcrash())
            done = true;

         shipgrabboxes();

         drawship();
      }

      /* Dont let the ship get out of the screen */
      /*
      if (ship.x < 0) ship.x = 0;
      if (ship.y < 0) ship.y = 0;
      if (ship.x > (screen->w - ship.w)) ship.x = (screen->w - ship.w);
      if (ship.y > (screen->h - ship.h)) ship.y = (screen->h - ship.h);
      */

      //drawboxes();
      drawlandscapeobjects();

      //DrawText(10, 10, "This is a test");

      /* Unlocks the screen */
      if (SDL_MUSTLOCK(screen))
         SDL_UnlockSurface(screen);

      /* Redraw */
      updatescreen();

   } /* while */


   /*******************/
   /* Clear things up */
   /*******************/

   FreeFont(Font1);

   /********/
   /* Quit */
   /********/

   return 0; /* Success */
}