#ifndef __DUKE_NUKEM_MAP_H
#define __DUKE_NUKEM_MAP_H
// http://www.programmersheaven.com/discussion/81579/duke-nukem-map-format

#include <string>
#include <iostream>
#include <fstream>
#include <vector>
#include <map>

#include "face.h"
#include "scene.h"

namespace DukeNukem3D
{

    typedef char             int8_t;
    typedef unsigned char   uint8_t;
    typedef short           int16_t;
    typedef unsigned short uint16_t;
    typedef unsigned int   uint32_t;
    typedef int             int32_t;

    
    
    
    #define SECTOR_FLAG_PARALLAXING                 1
    #define SECTOR_FLAG_SLOPED                      2
    #define SECTOR_FLAG_FLIPXY                      4
    #define SECTOR_FLAG_DOUBLERESOLUTION            8
    #define SECTOR_FLAG_FLIPX                      16
    #define SECTOR_FLAG_FLIPY                      32
    #define SECTOR_FLAG_ALIGNTEXTUREWITHFIRSTWALL  64

    struct Sector
    {
        uint16_t FirstWall;
        uint16_t NumberOfWalls;
        
        int32_t CeilingZPos;
        int32_t FloorZPos;
        
        uint16_t CeilingFlags;
        uint16_t FloorFlags;
        
        uint16_t CeilingTexture;
        uint16_t CeilingSlope;
        uint8_t  CeilingShade;
        uint8_t  CeilingPalette;
        int8_t  CeilingTextureOffsetX;
        int8_t  CeilingTextureOffsetY;
        
        uint16_t FloorTexture;
        int16_t FloorSlope;
        uint8_t  FloorShade;
        uint8_t  FloorPalette;
        int8_t  FloorTextureOffsetX;
        int8_t  FloorTextureOffsetY;
        
        uint8_t  Visibility;
        uint8_t  Unused;
        
        uint16_t Tag1;
        uint16_t Tag2;
        uint16_t ExtraTag;
        
    } __attribute__((packed));

    
    
    
    #define WALL_FLAG_BLOCKING                    1
    #define WALL_FLAG_SWAPBOTTOMS                 2
    #define WALL_FLAG_TEXTUREALIGNEDWITHBOTTOM    4
    #define WALL_FLAG_FLIPX                       8
    #define WALL_FLAG_FLIPY                     256
    #define WALL_FLAG_MASKED                     16
    #define WALL_FLAG_ONEWAYWALL                 32
    #define WALL_FLAG_BLOCKINGWEAPONS            64
    #define WALL_FLAG_TRANSPARENT1              128
    #define WALL_FLAG_TRANSPARENT2              512

    struct Wall
    {
        int32_t LeftPointX;
        int32_t LeftPointY;
        uint16_t NextWall;
        uint16_t WallOnTheOtherSideOfTheWall; // -1 for none
        uint16_t SectorOnTheOtherSideOfTheWall; // -1 for none
        uint16_t Flags;
        uint16_t TextureOnBottom;
        uint16_t TextureBetween;
        
        uint8_t  Shade;
        uint8_t  Palette;
        uint8_t  ScaleX;
        uint8_t  ScaleY;
        uint8_t  OffsetX;
        uint8_t  OffsetY;
        
        uint16_t Tag1;
        uint16_t Tag2;
        uint16_t ExtraTag;
    } __attribute__((packed));
    
    
    

    #define SPRITE_FLAG_BLOCKING        1
    #define SPRITE_FLAG_TRANSPARENT1    2
    #define SPRITE_FLAG_TRANSPARENT2  512
    #define SPRITE_FLAG_FLIPX           4
    #define SPRITE_FLAG_FLIPY           8
    #define SPRITE_FLAG_ONTHEWALL      16
    #define SPRITE_FLAG_ONTHEFLOOR     32 // ONTHEWALL and ONTHEFLOOR are mutually exclusive
    #define SPRITE_FLAG_ONEWAYSPRITE   64
    #define SPRITE_FLAG_CENTERED      128
    #define SPRITE_FLAG_HITTABLE      256
    
    struct Sprite
    {
        int32_t x;
        int32_t y;
        int32_t z;
        uint16_t Flags;
        uint16_t Texture;
        uint8_t Shade;
        uint8_t Palette;
        uint8_t ClippingDistance;
        uint8_t Unused;
        
        uint8_t ScaleX;
        uint8_t ScaleY;
        int8_t OffsetX;
        int8_t OffsetY;
        
        uint16_t StartingSector;
        uint16_t StartingStatusList;
        
        int16_t StartingAngle;
        uint16_t Owner;
        uint16_t SpeedX;
        uint16_t SpeedY;
        uint16_t SpeedZ;
        
        uint16_t Tag1;
        uint16_t Tag2;
        uint16_t ExtraTag;
    } __attribute__((packed));
    
    
    struct MapHeader
    {
        uint32_t Version;
        int32_t StartPositionX;
        int32_t StartPositionY;
        int32_t StartPositionZ;
        int16_t StartPositionAngle;
        uint16_t StartPositionSector;
    } __attribute__((packed));


    class DukeMap
    {
    private:
        uint8_t* rawdata;
        
    public:
        MapHeader* pHeader;

        uint16_t* pNumberOfSectors;
        Sector* Sectors;
        
        uint16_t* pNumberOfWalls;
        Wall* Walls;
        
        uint16_t* pNumberOfSprites;
        Sprite* Sprites;

        DukeMap(std::string FileName);
        ~DukeMap();
        static DukeMap* LoadFromFile(std::string FileName);
        Scene* ToScene();
    };

} // namespace DukeNukem3D

#endif
