#define SHAPE_MAXSIZE     128
#define SHAPE_MAXRMATRIX    5   // for size 8,16,32,64,128

//  _____________________________________________________________
// /                                                             \
// | device independent bitmap
// \_____________________________________________________________/

#include "dib.h"

typedef PBITMAPINFO PDIB;

class DIBitmap {

public:
    DIBitmap    (char *filename);
    DIBitmap    (DIBitmap &);
    ~DIBitmap   ();
    bool    isdead()    { return idead; }

    uchar    getpixvalue(cpix x,cpix y);
    rcode    setpixvalue(cpix x,cpix y,uchar val);
    COLORREF getpixcolor(cpix x,cpix y);
    LPBITMAPINFO    dib()  { return pdib; }

private:
    rcode   load();
    void    deletedib();    // if any

    bool    idead;
    char    *ifilename;
    LPBITMAPINFO    pdib;
};

//  _____________________________________________________________
// /                                                             \
// | rotation matrix
// \_____________________________________________________________/

class RotationMatrix {

public:
    RotationMatrix  (ushort size, ushort steps);
    ~RotationMatrix ();
    bool isdead()   { return idead; }

    rcode   srcxy(short  targx,   short  targy,
                  short &sourcex, short &sourcey,
                  ushort rotationstep);

private:
    bool    idead;
    ushort  size;
    ushort  steps;
    long    sizeperstep;
    char   *data[SHAPE_MAXSIZE];
    };

//  _____________________________________________________________
// /                                                             \
// | ShapeClass
// \_____________________________________________________________/

class ShapeFace;

class ShapeClass {

public:
    ShapeClass  (PixelDisplay &pd,
                 char   *firstnamebase, // "00" ff. is appended
                 ushort  nfaces,
                 cpix    maxsize,
                 ushort  nrot,
                 ushort  nzoom,
                 ulong   flags);
    ~ShapeClass    ();
    bool    isdead  ()  {return idead;}

    enum    EFlags {
        JUSTVIDEO   = (1<<0)    // do NOT plot into framebuffer
        };

    cpix    cfrm(uchar x)   {   return hwfrm[x & 3];    }
    // for every shape class, an own coordinate range could be set.
    // this MUST lie WITHIN the cfrm() of the PixelDisplay.
    rcode   setcfrm(cpix xmin,cpix ymin,cpix xmax,cpix ymax);
    // returns INDATA if not lying within pd.cfrm.
     
    rcode   checkbnd(cpix x, cpix y)    // returns OK if coord is valid
        {
            if(     x < hwfrm[0] || x > hwfrm[2]
                ||  y < hwfrm[1] || y > hwfrm[3]    )
                return FAILED;
            return OK;
        }

private:
    bool    idead;

    PixelDisplay &pd;
    FastTrig      ft;

    RotationMatrix  *prm;   // only used while creating faces

    ushort  nfaces;
    ushort  maxsize;
    ShapeFace     **face;  // [nfaces]

    HDC     hdcsrc;     // work-DC
    HDC     hdcdst;     // work-DC

    HPALETTE hpolds;    // old source DC palette
    HPALETTE hpoldd;    // old dest.  DC palette

    // the following is, by default, a copy of pd.hwfrm
    cpix    hwfrm[4];   // allowed left/top/right/bottom min/max coords

friend class ShapeFace;
friend class Shape;
};

class ShapeFace
{
public:
    ShapeFace   (ShapeClass &scl,
                 char   *fullfilename,
                 cpix    size,
                 ushort  nrot,
                 ushort  nzoom,
                 ulong   flags);
    ~ShapeFace  ();
    bool isdead () { return idead; }

    void    decodebitmap(ushort rcur,ushort zcur);
    void    makelinfo(uchar *lprof);
    cpix    frontsize(void) { return ifrontsize; }
    cpix    backsize(void)  { return ibacksize;  }
    bool    useframebuf(void)
        {   return (flags & ShapeClass::JUSTVIDEO) ? 0 : 1; }

private:
    bool        idead;

    rcode    rotate(    // source is dib
     HDC     hdst,
     ushort  xdst,
     ushort  ydst,
     ushort  steps
     );
 
    ShapeClass  &scl;
    cpix        size;
    ushort      sizebits;   // size == 1 << sizebits
    ushort      nrot;
    ushort      nzoom;
    ulong       flags;
    short       ifrontsize;
    short       ibacksize;

    HBITMAP     hbmpstamp;  // size*size for misc. purposes
    DIBitmap    dib,dib2;
 
    struct  TransformedArea {
 
        cpix    width;      // size * nzoom
        cpix    height;     // size * nrot
  
        HBITMAP hbmp;       // all images, transformed, color
        HBITMAP hbmpmask;   // all images, transformed, mono positive
        HBITMAP hbmpmono;   // all images, transformed, mono negative
  
        }   tr;
 
    struct  MonoBitmapBuffer    {

        HBITMAP hbmpimg; // an image ist 1st copied hereinto, then decoded

        long    size;    // mono bitmap bits buffer size
        uchar   *adr;    // mono bitmap bits buffer for temporar bitmap decodings

        }   mbb;

    struct  BytemapBuffer   {

        long    size;    // buffer size
        uchar   *adr;    // a single image for temporar bitmap decodings

        }   bytemap;

    struct  LineInfos   {

        long    size;   // of whole array
        uchar   *adr;   // size*2 bytes per image

        }   linfo;

    ushort  xtr     (ushort r, ushort z)    { return z << sizebits; }
    ushort  ytr     (ushort r, ushort z)    { return r << sizebits; }
    uchar  *li_start(ushort r, ushort z)    { return &linfo.adr[(size<<1)*((nzoom*r)+z)]; }

friend class Shape;
};

typedef bool (*POBSTFN)(pcol,void*);

class Shape {
public:
    Shape  (ShapeClass &, pcol color = 0);
    // in case shapeclass is JUSTVIDEO, 'color' has no function.
    ~Shape ();
    bool    isdead()    {return idead;}

    rcode   moveto  (cpix x, cpix y);
    rcode   turnto  (ushort ftrig_angle);
    ushort  truncangle(void);   // truncated angle in ftrig degrees
    rcode   setface (ushort faceidx);
    ushort  faces   (void)  { return scl.nfaces; }
    rcode   plot    (void);
    rcode   unplot  (void);
    void    markAsUnplotted(void)   { plotted = 0; }
    bool    ispixset(cpix x,cpix y); // SIGNED coords from -size/2 to +size/2
    rcode   prescan (cpix &obstx, cpix &obsty);
    void    pdplot  (cpix x,cpix y);
    void    setObstacleFunction (POBSTFN p, void *pUserData)
            { obstfn = p; iuserdata = pUserData; }
    cpix    width   (void)  { return scl.face[curface]->size; }
    cpix    height  (void)  { return scl.face[curface]->size; }

    // from the shape's middle, how many pixels
    // are set in front and back direction?
    cpix    frontsize   (void);
    cpix    backsize    (void);

    // from the shape's middle, stepping forward or backward,
    // where would we get?
    void    dirpos      (cpix &x,cpix &y,cpix distance);

    // if we want to place something exactly in front of the shape,
    // which position would that be?
    void    frontpos    (cpix &x,cpix &y,cpix offset=1);
    void    backpos     (cpix &x,cpix &y,cpix offset=1);
    void    setcolcode  (pcol colcode); 

private:
    bool    idead;
    POBSTFN obstfn;
    void   *iuserdata;
    bool    plotted;    // 1 after plot(), 0 after unplot()

protected:
    ShapeClass &scl;

    cpix    curx;
    cpix    cury;
    ushort  curface;    // current shapeface
    ushort  currot;     // current rotation step
    ushort  curzoom;    // current zoom step
    ushort  curftangle; // current angle in ftrig units

    cpix    oldx,oldy;  // needed
    ushort  oldface;    // for
    ushort  oldrot;     // unplot()
    ushort  oldzoom;

    pcol    curcol;     // for PixelDisplay framebuffer plots
    HBITMAP hbmpback;   // saved background
};
