/*! \file basegfx.h
   \brief basegfx.h is the interface for the graphic routines (of which some are platform dependent).

   basegfx.h additionally includes the definitions of routines, 
    whose implementation is platform dependant, but whose interface
    is the same on all platforms. Their implementation is in the various
    subdirectories for the platforms.
    The graphic routines are older than ASC itself, first written in 1993 as a protected
    mode replacement for Borlands BGI. (That's why the uncompressed image structure
    is exactly the same as in the BGI).
    This is the most badly designed and ugly part of ASC, which should be completely rewritten,
    to make use of high- and truecolor, hardware acceleration and to provide a nice, object
    oriented C++ interface.
*/

/*
    This file is part of Advanced Strategic Command; http://www.asc-hq.de
    Copyright (C) 1994-1999  Martin Bickel  and  Marc Schellenberger

    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; see the file COPYING. If not, write to the 
    Free Software Foundation, Inc., 59 Temple Place, Suite 330, 
    Boston, MA  02111-1307  USA
*/

#ifndef basegfxH
#define basegfxH

#include "libs/sdlmm/src/sdlmm.h"
#include "tpascal.inc"
#include "global.h"
#include "palette.h"
#include "sdl/graphics.h"

#pragma pack(1)

//! a graphical surface.
struct  tgraphmodeparameters {
            int           resolutionx      ;       //!< the number of visible pixels in x direction
            int           resolutiony      ;       //!< the number of visible pixels in y direction
            int           actsetpage       ;       //!< only used in DOS where it may be necessary to access the graphic memory in 64 kB pages
            int           windowstatus     ;       //!< determines whether the memory is a linear (windowstatus==100) or paged. When using SDL, the memory is always linear addressable.
            int           granularity      ;       //!< the offset in graphics memory between two pages. Can be ignored nowadays
            int           scanlinelength   ;       //!< the size of a line in byte (may be larger than resolutionx due to offscreen memory)
            int           scanlinenumber   ;       //!< the number of lines (may be larger than resolutiony due to offscreen memory)
            int           bytesperscanline ;       //!< the size of a line in byte
            int           activegraphmode  ;       //!< the number of the active graphic mode. Can be ignored nowadays
            int           videomemory      ;       //!< the amount of memory in the video card. Not used any more.
            int           byteperpix       ;       //!< the distance between two pixel
            PointerSizedInt linearaddress    ;       //!< the pointer to the actual memory (typecast to char* )
            int           pagetoset        ;       //!< only used in DOS with paged graphic memory
            char          redmasksize       ;      //!< RGB only: the number of bits in the red component
            char          redfieldposition  ;      //!< RGB only: the position of the first red bit relative to the start of the pixel
            char          greenmasksize     ;      //!< RGB only: the number of bits in the green component
            char          greenfieldposition;      //!< RGB only: the position of the first green bit relative to the start of the pixel
            char          bluemasksize      ;      //!< RGB only: the number of bits in the blue component
            char          bluefieldposition ;      //!< RGB only: the position of the first blue bit relative to the start of the pixel
            char          bitperpix         ;      //!< the size of a pixel(?) in bits
            char          memorymodel;             //!< unused
            int           directscreenaccess;      //!< if 0 no call to an update function (which copies the buffer to the screen) is performed
            int           reserved[9];
    };


struct trleheader {
   Uint16 id;
   Uint16 size;
   pascal_byte rle;
   Uint16 x;
   Uint16 y;
};

const int black = 0;
const int    blue        = 1;
const int    green       = 2;
const int    cyan        = 3;
const int    red         = 4;
const int    magenta     = 5 ;
const int    brown       = 6  ;
const int    lightgray   = 7   ;
const int    darkgray    = 8    ;
const int    lightblue   = 9     ;
const int    lightgreen  = 42  ; // 10      
const int    lightcyan   = 119 ; // 11      
const int    lightred    = 52  ; // 12        
const int    lightmagenta = 5  ; // 13         
const int    yellow       = 103;  // 14          
const int    white        = 160;  // 15           

extern tgraphmodeparameters   *agmp;
extern tgraphmodeparameters   *hgmp;

extern dacpalette256* activepalette256;


extern tgraphmodeparameters activegraphmodeparameters;
extern tgraphmodeparameters hardwaregraphmodeparameters;

extern dacpalette256 activepalette;
extern int       palette16[256][4];
extern void*     xlatbuffer;



 //! paints a colored bar
 extern void bar(int x1, int y1, int x2, int y2, char color);

 //! copy an image to a buffer. buffer must have been alloced before with malloc ( imagesize ( x1,y1,x2,y2 ))
 extern void getimage(int x1, int y1, int x2, int y2, void *buffer);

 //! puts the image pointed to by buffer to the screen
 extern void putimage(int x1, int y1, void *buffer);

 /** puts the image in pictbuffer on the screen performing a color translation.
      xlattables points to an array of n*256 byte. n must be equal or greater than the highest 
      pixel in pictbuffer.
      Example: you want to paint the tracks on the screen which are left by heavy vehicles.
        There is an image of a track which contains just pixels of 0, 1 and two. You now provide
        a set of 3 tables each 256 bytes large. The first one just counts from 0 to 255
        The second one has color values that are a bit darker than the original: If color #2 was
        blue, table[2] would have the index of a dark blue color.
        The third table is like the second one, but points to even darker colors.
        If you now paint the track using this function:
           where there is color #0 in the track image, the pixel on the screen would be unchanged
           where there is color #1 in the track image, the pixel on the screen is made darker
           and where there is color #2, the pixel on the screen is becoming even more darker
      Ok, that's all. A very specialized function. I just checked and noticed that it is not used
      any more by ASC. Damn, why did I write this description instead of just deleting this
      procedure ;-)
 */
 extern void putxlatfilter ( int x1, int y1, void* pictbuffer, char* xlattables );

 //! puts the image pointed to by buffer on the screen. All pixels with color #255 are treated as transparent
 extern void putspriteimage(int x1, int y1, void *buffer);

 /** like #putspriteimage, but all color with an 16 <= colorIndex <= 23 are increased by 
      rotationvalue. This is used to display colored units. The mentioned color range are the
      red colors, which can be made blue by adding 8 to them, 16 makes them brown, etc.. */
 extern void putrotspriteimage(int x1, int y1, void *buffer, int rotationvalue);

 //! like #putspriteimage, but rotates the image by 90ø clock-wise
 extern void putrotspriteimage90(int x1, int y1, void *buffer, int rotationvalue);

 //! like #putspriteimage, but rotates the image by 180ø clock-wise
 extern void putrotspriteimage180(int x1, int y1, void *buffer, int rotationvalue);

 //! like #putspriteimage, but rotates the image by 270ø clock-wise
 extern void putrotspriteimage270(int x1, int y1, void *buffer, int rotationvalue);

 //! function not used any more and obsolete. Use #putimageprt instead
 extern void puttexture ( int x1, int y1, int x2, int y2, void *texture );

 /** Puts a part of texture on the screen. Texture must be the same size as the screen,
      so the coordinates x1/y1 and x2/y2 describe the same rectangle on texture and the screen.
      This rectangle is copied from texture to the screen. color #255 is treated as transparent */
 extern void putspritetexture ( int x1, int y1, int x2, int y2, void *texture );

 /** copies a part of texture to the screen. Texture and the screen may have different size.
      x1/y1 and x2/y2 is the rectangle of the screen that texture is copied to. 
      dx/dy is the offset between x1/y1 and the upper left corner of texture.
      If dx==0 and dy==1, the uppermost line of texture would not be displayed.
      The rectangle x1/y1 - x2/y2 may be larger than texture.                   */
 extern void putimageprt ( int x1, int y1, int x2, int y2, void *texture, int dx, int dy );

 /** Performs a color translation of the image pntr. The image is not modified. The new image
      is written to a static buffer. The address of the buffer is returned. The call to xlatpict
      will overwrite the buffer. The conversion itself will replace color by xl[color]    */
 extern void* xlatpict ( ppixelxlattable xl, void* pntr );

 //! returns the position of the lowest bit of a which is set. This equals an inter logarithm to the base of 2
 extern int loga2 ( int a );
 




 //! sets the color palette in 8 bit mode
 extern void setvgapalette256 ( dacpalette256 pal );

 //! puts a single pixel on the screen. This is one of the few functions that work in truecolor mode too
 extern void putpixel(int x1, int y1, int color);
 
 //! gets the color of a single pixel from the screen. This is one of the few functions that work in truecolor mode too
 extern int getpixel(int x1, int y1);

 //! returns the size for a buffer that can contain an image of the given size.
 extern int imagesize(int x1, int y1, int x2, int y2);
 
 //! write the dimension of the image p to width and height
 extern void getpicsize(void* p, int& width, int& height);

 //! returns the size that the picture p occupies in memory
 extern int  getpicsize2(void* p );

 /** uncompresses the RLE-compressed image pict. A buffer for the uncompressed image
      is allocated and returned, so it must be freed later                          */
 extern void* uncompress_rlepict ( void* pict );

 /** A translation table ( to be used by #xlatpict ) is generated to translate any color of palette
      pal to its gray value. The new pixels after the translation are still to be used with the 
      palette pal. offset and size specify a range of colors that are assumed to be a linear
      transition from white to black. So all colors are mapped to one of the colors of the
      offset-size range. */
 extern void  generategrayxlattable( ppixelxlattable tab, char offset, char size, dacpalette256* pal );

 /** puts a shadow of an image on the screen. This is done by replacing all pixels on the screen
      by xl[pixel] if the pixel of the image ptr is non-transparent. The actual color of the image
      does not matter. */
 extern void putshadow ( int x1, int y1, void* ptr, ppixelxlattable xl  );



/** paints a pseudo-3D rectangle on the screen. invers specifies wheather the rectangle
     seems to be pressed into the screen or coming out  */
extern void         rahmen( bool invers, integer x1, integer y1, integer x2, integer y2);

//! draws a simple line on the screen. Not very fast...
extern void         line(int x1, int y1, int x2, int y2, char color );

/** draws a simple line on the screen, but performs a XOR operation between the pixel already 
     on screen and color. Thus the line will always have a color different then the one
     that was previously there. And it can be undone by displaying it a second time. */
extern void xorline( int x1, int y1, int x2, int y2, char color );

//! draws a simple rectangle
extern void rectangle(int x1, int y1, int x2, int y2, char color );

/** draws a simple rectangle on the screen, but performs a XOR operation between the pixel already
     on screen and color. Thus the rectangle will always have a color different then the one
     that was previously there. And it can be undone by displaying it a second time. */
extern void  xorrectangle(int x1, int y1, int x2, int y2, char color) ;

//! obsolete. not used any more. can be removed.
void putinterlacedrotimage ( int x1, int y1, void* ptr, int rotation );

/** rotates the image s by 90ø clockwise and writes it to d. d must point to a buffer
     with the size of imagesize2(s)                    */
void rotatepict90 ( void* s, void* d );

/** mirrors a picture horizontally (dir=1) or vertically (dir=0). d must point to a buffer
     with the size of imagesize2(s)                                  */
void flippict ( void* s, void* d, int dir = 1 );

/** reduces a pictures size. The new picture will have half the size in x and y direction, 
     resulting in a quarting of the area. The new image will be written to a static buffer that
     is overwritten the next time halfpict or #xlatpict is called. The address of the buffer is
     returned.                                                                 */
void* halfpict ( void* vbuf );


/** rotates the picture image by angle clockwise. The resulting image will have exactly the
    same size as the original image, resulting in the image being clipped      */
char* rotatepict ( void* image, int angle );


/** rotates the picture image by angle clockwise. The resulting image will be larger
    than the original one   */
char* rotatepict_grw ( void* image, int organgle );


//! returns the pixel at position x/y of the image buf or -1 if the pixel does not exist
extern int getpixelfromimage ( void* buf, int x, int y );

/** paints an ellipse on the screen that fills the rectangle x1/y1 - x2/y2.
     The thickness of the ellipse depends on tolerance. This function is just for highlighting
     elements on the screen, not for drawing nice and mathematically correct ellipse    */
extern void ellipse ( int x1, int y1, int x2, int y2, int color, float tolerance );

//! puts an image on the screen , with all pixels of newtransparence treated as transparent.
extern void putmask ( int x1, int y1, void* vbuf, int newtransparence );

/** puts the image ptr on the screen, mixing it with the colors there. Mixbuf must point to 
     256*256 byte large structure, where mixbuf[a][b] contains the colors that results from
     mixing a and b.                  */
extern void putpicturemix ( int x1, int y1, void* ptr, int rotation, char* mixbuf );


/** when using SDL, all operations are not performed on the displaymemory itself (as is the 
     case with DOS), but on a buffer. This function copies the buffer to the screen, performing 
     a color conversion if necessary */
extern void copySurface2screen( void );
extern void copySurface2screen( int x1, int y1, int x2, int y2 );



/** An abstract class that draws a line. This can not only be used for drawing a straight line
     on the screen, but also on the gamemap for example */
class tdrawline {
         public: 
           void start ( int x1, int y1, int x2, int y2 );
           virtual void putpix ( int x, int y ) = 0;
       };

/** A virtual screen is allocated and the agmp pointer set to it. All graphic operations will
     operate on this virtual display until it is deleted              */
class tvirtualdisplay {
           void* buf;
           tgraphmodeparameters oldparams;
           void init ( int x, int y, int col, int depth );
        public: 
           tvirtualdisplay ( int x, int y );
           tvirtualdisplay ( int x, int y, int color, int depth= 8 );
           ~tvirtualdisplay ();
        };



/**************************************************************
The following routines were an early attempt at rewriting the graphic engine. 
It was never continued and the routines are hardly used at all.
They can be completely scrapped should the graphic engine be rewritten 
sometime...
***************************************************************/

//! A class for a single pixel. 
struct trgbpixel {
       public:
         union {
            int rgb;
            #if SDL_BYTEORDER == SDL_LIL_ENDIAN
            struct { char r,g,b,a;  }channel;
            #else
            struct { char a,b,g,r;  }channel;
            #endif
          };
       //    mix ( const trgbpixel* pix );
          bool isTransparent();
        };

#define alphabase 64

#if SDL_BYTEORDER == SDL_LIL_ENDIAN
#define TCalpha 0xfefefe
#else
#define TCalpha 0xfefefe
#endif

//! A class for a RGB image. Was an attempt to rewrite the graphics engine, but should be scrapped. Any new code should use SDLmm-Surfaces.
class TrueColorImage {
         int xsize;
         int ysize;
         trgbpixel* pix;
      public:
         TrueColorImage ( int x, int y );
         trgbpixel getpix ( int x, int y );
         trgbpixel* getpix ( void );
         void setpix ( int x, int y, int r, int g, int b, int alpha = 0 );
         void setpix ( int x, int y, const trgbpixel& pix );
         int getxsize( void );
         int getysize( void );

         ~TrueColorImage();

   };

//! changes an images size. The source image (in buf) is 8-bit with palette pal .
extern TrueColorImage* zoomimage ( void* buf, int xsize, int ysize, dacpalette256 pal, int interpolate = 1, int proportional = 1 );

/** the truecolor image img is reduced to 8 bit color using the palette pal. A new buffer is 
     allocated and returned. It must be freed after use.          */
extern char* convertimage ( TrueColorImage* img, dacpalette256 pal );

//! converts a 8-bit image to a truecolor image
extern TrueColorImage* convertimage2tc ( void* buf, dacpalette256 pal );

/** a table to speed up conversion from truecolor to 8bit palette. The 6 most significant bits
     of each color component (RGB) form the the index.       */
extern char truecolor2pal_table[262144];

//! puts the image pointed to by tci to the screen. Both must be truecolor images. This function is a quick and unoptimized hack!
extern void putimage ( int x1, int y1, TrueColorImage* tci );
extern void putimage_noalpha ( int x1, int y1, TrueColorImage* tci );

TrueColorImage* smoothimage ( TrueColorImage* src );


extern TrueColorImage* getimage(int x1, int y1, int x2, int y2 );


//! a class that is thrown as exception. Should be moved to error.h ...
class fatalgraphicserror { 
        char st[1000];
      public:
        fatalgraphicserror ( char* strng );
        fatalgraphicserror ( void );
      };

/** \brief Collects all graphic operations and updates the screen on destruction
     When porting ASC from DOS to SDL, the problem arose that under DOS all graphic operations
     directly modified the graphics memory and were directly visible without any overhead. Using
     SDL, the operations operated on a buffer that had to be copied to display memory in a 
     seperate operation, which was costly. Updating the screen after every basic graphic operation
     was too expensive, so I wrote this class that suspended all updates until it was destructed. */
class collategraphicoperations {
         int olddirectscreenaccess;
         int x1, y1, x2, y2;
         int status;
       public:
         collategraphicoperations ( void );
         collategraphicoperations ( int _x1, int _y1, int _x2, int _y2 );
         ~collategraphicoperations ();
         void on ( void );
         void off ( void );
};



#pragma pack()

//! this variable determines whether the next call to initgraphics will open a window or a fullscreen session
extern int fullscreen;

//! sets the caption of the main window
extern void setWindowCaption ( const char* s );

/** converts a SDLmm::Surface to an old style image buffer
    \param s The source surface
    \param paletteTranslation If the source surface is 8 bit, convertSurface will convert the palette to the ASC palette if this is set to true.
*/
extern void* convertSurface ( SDLmm::Surface& s, bool paletteTranslation = true );

#endif
