/**\file n_iso_engine.c
 *  Map loading, saving, with objects, animations, ...
 *\author Castagnier Mickael
 *\version 1.0
 *\date 04/07/05
 */

#include "n_iso_engine.h"

#ifndef NOISOENGINE

/*!\fn create_empty_map( MAP **map , char *name , int XSIZE , int YSIZE , int TILEW , int TILEH , int nbmaxobjects , int nbmaxgroup , int nbmaxanims , int nbtiles , int nbanims )
 *\brief Create an empty map
 *\param map the map pointer to point on the allocated one
 *\param name Name of the map (used for linking between two map )
 *\param XSIZE the X size of the map (in tile)
 *\param YSIZE the Y size of the map (in tile)
 *\param TILEW the Width of a tile
 *\param TILEH the Heigh of a tile
 *\param nbmaxobjects the maximum number of objects in the map
 *\param nbmaxgroup the maximum number of active objects in the map
 *\param nbmaxanims the maximum number of aniations in the map
 *\param nbtiles the maximum number of tiles in the map
 *\param nbanims the maximum number of animations in the map
 *\return TRUE on success FALSE on failure
 */
int create_empty_map( MAP **map , char *name , int XSIZE , int YSIZE , int TILEW , int TILEH , int nbmaxobjects , int nbmaxgroup , int nbmaxanims , int nbtiles , int nbanims )
{
   int x, y;

   /* only here for warning:unused... avoiding, will be removed further */
   nbmaxobjects = nbmaxgroup = nbmaxanims = nbtiles = nbanims = 1 ;
   nbmaxgroup = nbmaxobjects ;

   /* allocating map */
   Malloc( ( *map ) , MAP , 1 );

   if ( !( *map ) )
      return FALSE;

   /* allocating grid */
   Malloc( ( *map ) -> grid , CELL , XSIZE * YSIZE );

   /* allocating name */
   Malloc( ( *map ) -> name , char , ustrsizez( name ) );

   ustrcpy( ( *map ) -> name , name );

   if ( !( *map ) -> grid || !( *map ) -> name )
      return FALSE;

   ( *map ) -> XSIZE = XSIZE ;
   ( *map ) -> YSIZE = YSIZE ;

   for ( x = 0 ; x < XSIZE ; x++ ) {
      for ( y = 0 ; y < YSIZE ; y++ ) {
         set_value( ( *map ) , N_TILE , x , y , FALSE );
         set_value( ( *map ) , N_ABILITY , x , y , FALSE );
         set_value( ( *map ) , N_MUSIC , x , y , FALSE );
         set_value( ( *map ) , N_OBJECT , x , y , FALSE );
      }
   }

   ( *map ) -> TILEW = TILEW;
   ( *map ) -> TILEH = TILEH;
   ( *map ) -> bgcolor = makeacol( 0 , 0 , 0 , 255 );
   ( *map ) -> wirecolor = makeacol( 255 , 255 , 255 , 255 );

   /* Drawing the mousemap */

   ( *map ) -> mousemap = create_bitmap( ( *map ) -> TILEW , ( *map ) -> TILEH );
   ( *map ) -> colortile = create_bitmap( ( *map ) -> TILEW , ( *map ) -> TILEH );

   clear_to_color( ( *map ) -> mousemap , makecol( 255 , 255 , 255 ) );
   clear_to_color( ( *map ) -> colortile , ( *map ) -> bgcolor );

   /* drawing mousemap */
   line( ( *map ) -> mousemap , 0 , ( TILEH >> 1 ) - 2 , ( TILEW >> 1 ) - 3 , 0 , makecol( 255, 0, 0 ) );
   floodfill( ( *map ) -> mousemap , 1 , 1 , makecol( 255, 0, 0 ) );

   line( ( *map ) -> mousemap , ( TILEW >> 1 ) - 3 , TILEH - 1 , 0 , ( TILEH >> 1 ) + 1 , makecol( 0 , 255 , 0 ) );
   floodfill( ( *map ) -> mousemap , 1 , TILEH - 2 , makecol( 0, 255, 0 ) );

   line( ( *map ) -> mousemap , ( TILEW >> 1 ) + 2 , TILEH - 1 , TILEW - 1 , ( TILEH >> 1 ) + 1 , makecol( 0, 0, 255 ) );
   floodfill( ( *map ) -> mousemap , TILEW - 2 , TILEH - 1 , makecol( 0, 0, 255 ) );

   line( ( *map ) -> mousemap , ( TILEW >> 1 ) + 2 , 0 , TILEW - 1 , ( TILEH >> 1 ) - 2 , makecol( 255, 255 , 0 ) );
   floodfill( ( *map ) -> mousemap , TILEW - 1 , 0 , makecol( 255, 255, 0 ) );

   /* drawing colortile */
   line( ( *map ) -> colortile , 0 , ( TILEH >> 1 ) - 2 , ( TILEW >> 1 ) - 3 , 0 , makecol( 255 , 0 , 255 ) );
   floodfill( ( *map ) -> colortile , 1 , 1 , makecol( 255 , 0 , 255 ) );

   line( ( *map ) -> colortile , ( TILEW >> 1 ) - 3, TILEH - 1 , 0 , ( TILEH >> 1 ) + 1 , makecol( 255 , 0 , 255 ) );
   floodfill( ( *map ) -> colortile , 1 , TILEH - 2 , makecol( 255 , 0 , 255 ) );

   line( ( *map ) -> colortile , ( TILEW >> 1 ) + 2 , TILEH - 1 , TILEW - 1 , ( TILEH >> 1 ) + 1 , makecol( 255 , 0 , 255 ) );
   floodfill( ( *map ) -> colortile , TILEW - 2 , TILEH - 1 , makecol( 255 , 0 , 255 ) );

   line( ( *map ) -> colortile , ( TILEW >> 1 ) + 2 , 0 , TILEW - 1 , ( TILEH >> 1 ) - 2 , makecol( 255 , 0 , 255 ) );
   floodfill( ( *map ) -> colortile , TILEW - 1 , 0 , makecol( 255 , 0 , 255 ) );

   ( *map ) -> ptanchorX = ( *map ) -> X = 0 ;
   ( *map ) -> ptanchorY = ( *map ) -> Y = 0 ;

   return TRUE;
} /* create_empty_map( ... ) */



/*!\fn set_value( MAP *map , int type , int x , int y , int value )
 *\brief Set the value of a cell in a map
 *\param map A MAP *map to 'touch'
 *\param type It is the type of value to change , it can be N_TILE,N_ABILITY,N_MUSIC,N_OBJECT
 *\param x X coordinate of the cell
 *\param y Y coordinate of the cell
 *\param value Value to give to the cell
 *\return TRUE or FALSE
 */
int set_value( MAP *map , int type , int x , int y , int value )
{
   if ( !map || x >= map -> XSIZE || y >= map -> YSIZE )
      return FALSE;

   if ( type == N_TILE ) {

      map -> grid[ x + y * map -> XSIZE ] . tilenumber = value ;

      return TRUE;
   }

   if ( type == N_ABILITY ) {

      map -> grid[ x + y * map -> XSIZE ] . ability = value ;

      return TRUE;
   }

   if ( type == N_MUSIC ) {

      map -> grid[ x + y * map -> XSIZE ] . music = value ;

      return TRUE;
   }

   if ( type == N_OBJECT ) {

      map -> grid[ x + y * map -> XSIZE ] . objectnumber = value ;

      return TRUE;
   }


   return FALSE;
} /* set_value( ... ) */



/*!\fn get_value( MAP *map , int type , int x , int y )
 *\brief Get a the tilenumber of a cell item
 *\param map A MAP *map to 'touch'
 *\param type It is the type of value to change , it can be N_TILE,N_ABILITY,N_MUSIC,N_OBJECT
 *\param x X coordinate of the cell
 *\param y Y coordinate of the cell
 *\return The specified type value of the cell, or FALSE if unavailable
 */
int get_value( MAP *map , int type , int x , int y )
{

   if ( !map || x >= map -> XSIZE || y >= map -> YSIZE )
      return FALSE;


   if ( type == N_TILE )
      return map -> grid[ x + y * map -> XSIZE ] . tilenumber ;

   if ( type == N_OBJECT )
      return map -> grid[ x + y * map -> XSIZE ] . objectnumber ;

   if ( type == N_MUSIC )
      return map -> grid[ x + y * map -> XSIZE ] . music ;

   if ( type == N_ABILITY )
      return map -> grid[ x + y * map -> XSIZE ] . ability ;

   return FALSE;

} /* get_value( ... ) */



/*!\fn ScreenToMap( int mx, int my, int *Tilex,int *Tiley,BITMAP *mousemap)
 *\brief Convert screen coordinate to map coordinate
 *\param mx The 'x' coordinate in pixel
 *\param my The 'y' coordinate in pixel
 *\param Tilex Output for the generated Tilex
 *\param Tiley Output for the generated Tiley
 *\param mousemap A mousemap with colors for one tile
 *\return TRUE
 */
int ScreenToMap( int mx, int my, int *Tilex, int *Tiley, BITMAP *mousemap )
{

   /* SCREEN TO MAP VARIABLES */
   int RegionX, RegionY,
       RegionDX = 0, RegionDY = 0,
       MouseMapX, MouseMapY,
       MouseMapWidth, MouseMapHeight, c;

   MouseMapWidth = mousemap -> w;
   MouseMapHeight = mousemap -> h;

   /* First step find out where we are on the mousemap */
   RegionX = ( mx / MouseMapWidth );
   RegionY = ( my / MouseMapHeight ) << 1; /* The multiplying by two is very important */


   /* Second Step: Find out WHERE in the mousemap our mouse is, by finding MouseMapX and MouseMapY. */

   MouseMapX = mx % MouseMapWidth;
   MouseMapY = my % MouseMapHeight;

   /* third step: Find the color in the mouse map */
   c = getpixel( mousemap, MouseMapX, MouseMapY );

   if ( c == makecol( 255, 0, 0 ) ) {
      RegionDX = -1;
      RegionDY = -1;
   }

   if ( c == makecol( 255, 255, 0 ) ) {
      RegionDX = 0;
      RegionDY = -1;
   }

   if ( c == makecol( 255, 255, 255 ) ) {
      RegionDX = 0;
      RegionDY = 0;
   }

   if ( c == makecol( 0, 255, 0 ) ) {
      RegionDX = -1;
      RegionDY = 1;
   }

   if ( c == makecol( 0, 0, 255 ) ) {
      RegionDX = 0;
      RegionDY = 1;
   }

   *Tilex = ( RegionDX + RegionX );
   *Tiley = ( RegionDY + RegionY );


   return TRUE;
} /* ScreenToMap( ... ) */



/*!\fn camera_to_scr( MAP **map , int x , int y )
 *\brief Center Map on given screen coordinate
 *\param map A MAP *map to center on x,y
 *\param x the X coordinate (in pixel) to center on;
 *\param y the Y coordintae (in pixel) to center on;
 *\return TRUE
 */
int camera_to_scr( MAP **map , int x , int y )
{
   if ( x < 0 )
      x = 0;

   if ( y < 0 )
      y = 0;

   ( *map ) -> X = x % ( *map ) ->TILEW;
   ( *map ) -> Y = y % ( *map ) ->TILEH;
   ( *map ) -> ptanchorY = y / ( ( *map ) ->TILEH >> 1 );
   ( *map ) -> ptanchorY = ( *map ) -> ptanchorY >> 1;
   ( *map ) -> ptanchorY = ( *map ) -> ptanchorY << 1;
   ( *map ) -> ptanchorX = ( x + ( ( *map ) -> ptanchorY & 1 ) * ( ( *map ) ->TILEW >> 1 ) ) / ( *map ) ->TILEW;

   return TRUE;
} /* camera_to_scr( ... )*/



/*!\fn camera_to_map( MAP **map , int tx , int ty , int x , int y )
 *\brief Center Map on given map coordinate, with x & y offset
 *\param map A MAP *map to center on tx+x , ty+y
 *\param tx TILEX offset in tile
 *\param ty TILEY offset in tile
 *\param x X offset in pixel
 *\param y Y Offset in pixel
 *\return TRUE
 */
int camera_to_map( MAP **map , int tx , int ty , int x , int y )
{

   ( *map ) -> ptanchorX = tx;
   ( *map ) -> ptanchorY = ty;
   ( *map ) -> X = x;
   ( *map ) -> Y = y;

   return TRUE;
} /* camera_to_map(...) */



/*!\fn draw_map( MAP *map , BITMAP *bmp , int destx , int desty , int mode)
 *\brief Draw a MAP *map on the BITMAP *bmp.
 *\param map The Map to draw
 *\param bmp The screen destination
 *\param destx X offset on bmp
 *\param desty Y offste on bmp
 *\param mode Drawing mode
 *\return TRUE
 */
int draw_map( MAP *map , BITMAP *bmp , int destx , int desty , int mode )
{
   int a , b ,           /* iterators */
       x , y ,           /* temp store of coordinate      */
       mvx,mvy ,           /* precomputed offset */
       tw , th,          /* number of tile for one screen */
       TW2,TH2;          /* size of TILE_W /2 and TILE_H/2 */

   /* computing tw&th */
   tw = 1 + ( ( bmp -> w - destx ) / map -> TILEW ) ;
   th = 3 + ( ( bmp -> h - desty ) / ( map -> TILEH >> 1 ) ) ;

   TW2 = map -> TILEW>>1 ;
   TH2 = map -> TILEH>>1 ;

   mvx = destx - TW2 - map -> X;
   mvy = desty - TH2 - map -> Y;

   for ( a = 0 ; a <= tw ; a++ ) {
      for ( b = 0 ; b <= th ; b++ ) {


         x = ( a * map -> TILEW + ( ( b & 1 ) * TW2 ) ) - ( map -> TILEW >> 1 ) + mvx;
         y = ( b * TH2 ) + mvy;

         if ( ( a + map -> ptanchorX ) < map->XSIZE && ( b + map -> ptanchorY ) < map->YSIZE ) {

            if ( map -> grid [ a + map -> ptanchorX + ( b + map -> ptanchorY ) * map->XSIZE ] . tilenumber == FALSE ) {

               masked_blit( map -> colortile , bmp , 0 , 0 , x , y , map -> colortile -> w , map -> colortile -> h );

            }

            else {

               /* TODO HERE : add sprite blit */
               triangle( bmp , x , y + ( map -> TILEH >> 1 ), x + map-> TILEW , y + ( map->TILEH >> 1 ), x + ( map -> TILEW >> 1 ) , y , makecol( 255, 255, 0 ) );
               triangle( bmp , x , y + ( map -> TILEH >> 1 ), x + map-> TILEW , y + ( map->TILEH >> 1 ), x + ( map -> TILEW >> 1 ) , y + map -> TILEH, makecol( 255, 0, 255 ) );

            } /* if( map -> grid... ) ) */

            if ( mode == 1 ) {

               line( bmp , x + ( map -> TILEW >> 1 ) - 2 , y , x , y + ( map -> TILEH >> 1 ) - 1 , map -> wirecolor );
               line( bmp , x + ( map -> TILEW >> 1 ) - 2 , y + map -> TILEH - 1 , x , y + ( map -> TILEH >> 1 ) , map -> wirecolor );
               line( bmp , x + ( map -> TILEW >> 1 ) + 1 , y + map -> TILEH - 1 , x + map -> TILEW - 1 , y + ( map -> TILEH >> 1 ) , map -> wirecolor );
               line( bmp , x + ( map -> TILEW >> 1 ) + 1 , y , x + map -> TILEW - 1 , y + ( map -> TILEH >> 1 ) - 1 , map -> wirecolor );

            }

            /* TODO HERE : check if object,
               check if it is already in the library
               if yes refresh its ttl
               if no add it with the defaut ttl
               */


         } /* if( (a + ...) ) */

      } /* for( a... ) */
   } /* for( b... ) */

   /* TODO HERE:

      sort the object list by X and by Y
      blit all the sprites of the active list
      */

   return TRUE;

} /* draw_map( ... ) */



/*!\fn load_map( MAP **map , char *filename )
 *\brief Load the map from filename
 *\param map A MAP *pointer to point on the loaded map
 *\param filename The filename of the map to load
 *\return TRUE or FALSE
 */
int load_map( MAP **map , char *filename )
{
   PACKFILE *loaded;

   char temp_name[1024];
   MAP temp_map;


   int it = 0 , it1 = 0 ;

   loaded = pack_fopen( filename , "rb" );

   if( map )
      free_map( &(*map) );
   if( !(*map) )
      Malloc( (*map) , MAP , 1 );

   /* writting name */
   it = pack_igetl( loaded );

   pack_fread( temp_name , it , loaded );

   /* writting states */
   temp_map . XSIZE  = pack_igetl( loaded );
   temp_map . YSIZE  = pack_igetl( loaded );
   temp_map . TILEW  = pack_igetl( loaded );
   temp_map . TILEH = pack_igetl( loaded );
   temp_map . ptanchorX = pack_igetl( loaded );
   temp_map . ptanchorY = pack_igetl( loaded );
   temp_map . X = pack_igetl( loaded );
   temp_map . Y = pack_igetl( loaded );
   temp_map . bgcolor = pack_igetl( loaded );
   temp_map . wirecolor = pack_igetl( loaded );

   create_empty_map( &( *map ) , temp_name ,
         temp_map . XSIZE , temp_map . YSIZE ,
         temp_map . TILEW , temp_map . TILEH ,
         2000 , 2000 , 2000 , 2000 ,2000 );
   (*map ) -> XSIZE = temp_map . XSIZE ;
   (*map ) -> YSIZE = temp_map . YSIZE ;
   (*map ) -> TILEW = temp_map . TILEW ;
   (*map ) -> TILEH = temp_map . TILEH ;
   (*map ) -> ptanchorX = temp_map . ptanchorX ;
   (*map ) -> ptanchorY = temp_map . ptanchorY ;
   (*map ) -> X  = temp_map .  X ;
   (*map ) -> Y  = temp_map . Y   ;
   (*map ) ->bgcolor  = temp_map . bgcolor ;
   (*map ) ->wirecolor = temp_map . wirecolor ;


   /* saving the grid */
   for( it = 0 ; it < (*map ) -> XSIZE ; it ++ ) {
      for( it1 = 0 ; it1 < (*map ) -> YSIZE ; it1 ++ ){
         (*map ) -> grid[ it + it1 * (*map ) -> XSIZE ] . tilenumber  = pack_igetl( loaded );
         (*map ) -> grid[ it + it1 * (*map ) -> XSIZE ] . ability  = pack_igetl( loaded );
         (*map ) -> grid[ it + it1 * (*map ) -> XSIZE ] . objectnumber  = pack_igetl( loaded );
         (*map ) -> grid[ it + it1 * (*map ) -> XSIZE ] . music  = pack_igetl( loaded );
      }
   }

   /*load_animlib( loaded , & (*map ) -> libtiles );*/
   /*load_animlib( loaded , & (*map ) -> libanims );*/

   pack_fclose( loaded );


   return TRUE;
} /* load_map( ... ) */



/*!\fn save_map( MAP *map , char *filename )
 *\brief Save the map to filename
 *\param map A MAP *pointer
 *\param filename The filename of the file where to save the map
 *\return TRUE or FALSE
 */
int save_map( MAP *map , char *filename )
{

   PACKFILE *saved;

   int it = 0 , it1 = 0 ;

   saved = pack_fopen( filename , "wb" );

   /* writting name */
   pack_iputl( ustrsizez( map -> name ) , saved );
   pack_fwrite( map -> name , ustrsizez( map -> name ) , saved );

   /* writting states */
   pack_iputl( map -> XSIZE , saved );
   pack_iputl( map -> YSIZE , saved );
   pack_iputl( map -> TILEW , saved );
   pack_iputl( map -> TILEH , saved );
   pack_iputl( map -> ptanchorX , saved );
   pack_iputl( map -> ptanchorY , saved );
   pack_iputl( map -> X , saved );
   pack_iputl( map -> Y , saved );
   pack_iputl( map -> bgcolor , saved );
   pack_iputl( map -> wirecolor , saved );

   /* saving the grid */
   for( it = 0 ; it < map -> XSIZE ; it ++ ) {
      for( it1 = 0 ; it1 < map -> YSIZE ; it1 ++ ){
         pack_iputl( map -> grid[ it + it1 * map -> XSIZE ] . tilenumber , saved );
         pack_iputl( map -> grid[ it + it1 * map -> XSIZE ] . ability , saved );
         pack_iputl( map -> grid[ it + it1 * map -> XSIZE ] . objectnumber , saved );
         pack_iputl( map -> grid[ it + it1 * map -> XSIZE ] . music , saved );
      }
   }

   /*save_animlib( saved , map -> libtiles );*/
   /*save_animlib( saved , map -> libanims );*/

   pack_fclose( saved );


   return TRUE;
} /* save_map( ... ) */



/*!\fn free_map( MAP **map )
 *\brief Free the map
 *\param map The map to free. The pointer will be set to NULL
 *\return TRUE or FALSE 
 */
int free_map( MAP **map )
{

   if( !(*map) )
      return TRUE;
   if( (*map) -> grid )
   {
      Free( ( *map ) -> grid );
   }
   if( (*map) -> name )
   {
      Free( ( *map ) -> name );
   }
   if( (*map) -> colortile )
      destroy_bitmap( ( *map ) -> colortile );
   if( (*map) -> mousemap )
      destroy_bitmap( ( *map ) -> mousemap );

   Free( ( *map ) );

   return TRUE;
} /* free_map( ... ) */

#endif /* #ifndef NOISOENGINE */
