/* -*- mode: c; tab-width: 4; c-basic-offset: 3; c-file-style: "linux" -*- */
//
// Copyright (c) 2009, Wei Mingzhi <whistler_wmz@users.sf.net>.
// All rights reserved.
//
// This file is part of SDLPAL.
//
// SDLPAL 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 3 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, see <http://www.gnu.org/licenses/>.
//

#include "palcommon.h"
#include "map.h"

LPPALMAP
PAL_LoadMap(
   INT               iMapNum,
   FILE             *fpMapMKF,
   FILE             *fpGopMKF
)
/*++
  Purpose:

    Load the specified map from the MKF file, as well as the tile bitmaps.

  Parameters:

    [IN]  iMapNum - Number of the map to load.

    [IN]  fpMapMKF - Pointer to the fopen'ed map.mkf file, which
                     contains the map tile data.

    [IN]  fpGopMKF - Pointer to the fopen'ed gop.mkf file, which
                     contains the tile bitmaps. The bitmap can be read
                     by PAL_SpriteGetFrame() function.

  Return value:

    Pointer to the loaded map. NULL if failed.

--*/
{
   LPBYTE                     buf;
   INT                        size, i, j;
   LPPALMAP                   map;

   //
   // Check for invalid map number.
   //
   if (iMapNum >= PAL_MKFGetChunkCount(fpMapMKF) ||
      iMapNum >= PAL_MKFGetChunkCount(fpGopMKF) ||
      iMapNum <= 0)
   {
      return NULL;
   }

   //
   // Load the map tile data.
   //
   size = PAL_MKFGetChunkSize(iMapNum, fpMapMKF);

   //
   // Allocate a temporary buffer for the compressed data.
   //
   buf = (LPBYTE)malloc(size);
   if (buf == NULL)
   {
      return NULL;
   }

   //
   // Create the map instance.
   //
   map = (LPPALMAP)malloc(sizeof(PALMAP));
   if (map == NULL)
   {
      return NULL;
   }

   //
   // Read the map data.
   //
   if (PAL_MKFReadChunk(buf, size, iMapNum, fpMapMKF) < 0)
   {
      free(buf);
      free(map);
      return NULL;
   }

   //
   // Decompress the tile data.
   //
   if (Decompress(buf, (LPBYTE)(map->Tiles), sizeof(map->Tiles)) < 0)
   {
      free(map);
      free(buf);
      return NULL;
   }

   //
   // The compressed data is useless now; delete it.
   //
   free(buf);

   //
   // Adjust the endianness of the decompressed data.
   //
   for (i = 0; i < 128; i++)
   {
      for (j = 0; j < 64; j++)
      {
         map->Tiles[i][j][0] = SWAP32(map->Tiles[i][j][0]);
         map->Tiles[i][j][1] = SWAP32(map->Tiles[i][j][1]);
      }
   }

   //
   // Load the tile bitmaps.
   //
   size = PAL_MKFGetChunkSize(iMapNum, fpGopMKF);
   if (size <= 0)
   {
      free(map);
      return NULL;
   }
   map->pTileSprite = (LPSPRITE)malloc(size);
   if (map->pTileSprite == NULL)
   {
      free(map);
      return NULL;
   }
   if (PAL_MKFReadChunk(map->pTileSprite, size, iMapNum, fpGopMKF) < 0)
   {
      free(map);
      return NULL;
   }

   //
   // Done.
   //
   map->iMapNum = iMapNum;

   return map;
}

VOID
PAL_FreeMap(
   LPPALMAP          lpMap
)
/*++
  Purpose:

    Free a loaded map, as well as the tile bitmaps.

  Parameters:

    [IN]  lpMap - Pointer to the loaded map structure.

  Return value:

    None.

--*/
{
   //
   // Check for NULL pointer.
   //
   if (lpMap == NULL)
   {
      return;
   }

   //
   // Free the tile bitmaps.
   //
   if (lpMap->pTileSprite != NULL)
   {
      free(lpMap->pTileSprite);
   }

   //
   // Delete the instance.
   //
   free(lpMap);
}

LPCBITMAPRLE
PAL_MapGetTileBitmap(
   BYTE       x,
   BYTE       y,
   BYTE       h,
   BYTE       ucLayer,
   LPCPALMAP  lpMap
)
/*++
  Purpose:

    Get the tile bitmap on the specified layer at the location (x, y, h).

  Parameters:

    [IN]  x - Column number of the tile.

    [IN]  y - Line number in the map.

    [IN]  h - Each line in the map has two lines of tiles, 0 and 1.
              (See map.h for details.)

    [IN]  ucLayer - The layer. 0 for bottom, 1 for top.

    [IN]  lpMap - Pointer to the loaded map.

  Return value:

    Pointer to the bitmap. NULL if failed.

--*/
{
   DWORD d;

   //
   // Check for invalid parameters.
   //
   if (x >= 64 || y >= 128 || h > 1 || lpMap == NULL)
   {
      return NULL;
   }

   //
   // Get the tile data of the specified location.
   //
   d = lpMap->Tiles[y][x][h];

   if (ucLayer == 0)
   {
      //
      // Bottom layer
      //
      return PAL_SpriteGetFrame(lpMap->pTileSprite, (d & 0xFF) | ((d >> 4) & 0x100));
   }
   else
   {
      //
      // Top layer
      //
      d >>= 16;
      return PAL_SpriteGetFrame(lpMap->pTileSprite, ((d & 0xFF) | ((d >> 4) & 0x100)) - 1);
   }
}

BOOL
PAL_MapTileIsBlocked(
   BYTE       x,
   BYTE       y,
   BYTE       h,
   LPCPALMAP  lpMap
)
/*++
  Purpose:

    Check if the tile at the specified location is blocked.

  Parameters:

    [IN]  x - Column number of the tile.

    [IN]  y - Line number in the map.

    [IN]  h - Each line in the map has two lines of tiles, 0 and 1.
              (See map.h for details.)

    [IN]  lpMap - Pointer to the loaded map.

  Return value:

    TRUE if the tile is blocked, FALSE if not.

--*/
{
   //
   // Check for invalid parameters.
   //
   if (x >= 64 || y >= 128 || h > 1 || lpMap == NULL)
   {
      return TRUE;
   }

   return (lpMap->Tiles[y][x][h] & 0x2000) >> 13;
}

BYTE
PAL_MapGetTileHeight(
   BYTE       x,
   BYTE       y,
   BYTE       h,
   BYTE       ucLayer,
   LPCPALMAP  lpMap
)
/*++
  Purpose:

    Get the logical height value of the specified tile. This value is used
    to judge whether the tile bitmap should cover the sprites or not.

  Parameters:

    [IN]  x - Column number of the tile.

    [IN]  y - Line number in the map.

    [IN]  h - Each line in the map has two lines of tiles, 0 and 1.
              (See map.h for details.)

    [IN]  ucLayer - The layer. 0 for bottom, 1 for top.

    [IN]  lpMap - Pointer to the loaded map.

  Return value:

    The logical height value of the specified tile.

--*/
{
   DWORD      d;

   //
   // Check for invalid parameters.
   //
   if (y >= 128 || x >= 64 || h > 1 || lpMap == NULL)
   {
      return 0;
   }

   d = lpMap->Tiles[y][x][h];

   if (ucLayer)
   {
      d >>= 16;
   }

   d >>= 8;
   return (BYTE)(d & 0xf);
}

VOID
PAL_MapBlitToSurface(
   LPCPALMAP             lpMap,
   SDL_Surface          *lpSurface,
   const SDL_Rect       *lpSrcRect,
   BYTE                  ucLayer
)
/*++
  Purpose:

    Blit the specified map area to a SDL Surface.

  Parameters:

    [IN]  lpMap - Pointer to the map.

    [OUT] lpSurface - Pointer to the destination surface.

    [IN]  lpSrcRect - Pointer to the source area.

    [IN]  ucLayer - The layer. 0 for bottom, 1 for top.

  Return value:

    None.

--*/
{
   int              sx, sy, dx, dy, x, y, h, xPos, yPos;
   LPCBITMAPRLE     lpBitmap = NULL;

   //
   // Convert the coordinate
   //
   sy = lpSrcRect->y / 16 - 1;
   dy = (lpSrcRect->y + lpSrcRect->h) / 16 + 2;
   sx = lpSrcRect->x / 32 - 1;
   dx = (lpSrcRect->x + lpSrcRect->w) / 32 + 2;

   //
   // Do the drawing.
   //
   yPos = sy * 16 - 8 - lpSrcRect->y;
   for (y = sy; y < dy; y++)
   {
      for (h = 0; h < 2; h++, yPos += 8)
      {
         xPos = sx * 32 + h * 16 - 16 - lpSrcRect->x;
         for (x = sx; x < dx; x++, xPos += 32)
         {
            lpBitmap = PAL_MapGetTileBitmap((BYTE)x, (BYTE)y, (BYTE)h, ucLayer, lpMap);
            if (lpBitmap == NULL)
            {
               if (ucLayer)
               {
                  continue;
               }
               lpBitmap = PAL_MapGetTileBitmap(0, 0, 0, ucLayer, lpMap);
            }
            PAL_RLEBlitToSurface(lpBitmap, lpSurface, PAL_XY(xPos, yPos));
         }
      }
   }
}