#ifndef __GEGL_BUFFER_INDEX_H
#define __GEGL_BUFFER_INDEX_H
/* File format building blocks

GeglBuffer on disk representation
=================================

*/


/* Increase this number when the structures change.*/
#define GEGL_FILE_SPEC_REV     0
#define GEGL_MAGIC             {'G','E','G','L'}

#define GEGL_FLAG_TILE         1
#define GEGL_FLAG_FREE_TILE    0xf+2

/* a VOID message, indicating that the specified tile has been rewritten */
#define GEGL_FLAG_INVALIDATED  2

/* these flags are used for the header, the lower bits of the
 * header store the revision
 */
#define GEGL_FLAG_LOCKED       (1<<(8+0))
#define GEGL_FLAG_FLUSHED      (1<<(8+1))
#define GEGL_FLAG_IS_HEADER    (1<<(8+3))

/* The default header we expect to see on a file is that it is
 * flushed, and has the revision the file conforms to written
 * to it.
 */
#define  GEGL_FLAG_HEADER    (GEGL_FLAG_FLUSHED  |\
                              GEGL_FLAG_IS_HEADER|\
                              GEGL_FILE_SPEC_REV)

/*
 * This header is the first 256 bytes of the GEGL buffer.
 */
typedef struct {
  gchar   magic[4];    /* - a 4 byte identifier */
  guint32 flags;       /* the header flags is used to encode state and revision
                          */
  guint64 next;        /* offset to first GeglBufferBlock */

  guint32 tile_width;
  guint32 tile_height;
  guint16 bytes_per_pixel;

  gchar   description[64]; /* GEGL stores the string of the babl format
                            * here, as well as after the \0 a debug string
                            * describing the buffer.
                            */

  /* the ROI could come as a separate block */
  gint32  x;               /* indication of bounding box for tiles stored. */
  gint32  y;               /* this isn't really needed as each GeglBuffer as */
  guint32 width;           /* represented on disk doesn't really have any */
  guint32 height;          /* dimension restriction. */

  guint32 rev;             /* if it changes on disk it means the index has changed */

  gint32  padding[36];     /* Pad the structure to be 256 bytes long */
} GeglBufferHeader;

/* the revision of the format is stored in the flags of the header in the
 * lower 8 bits
 */
#define gegl_buffer_header_get_rev(header)  (((GeglBufferHeader*)(header))->flags&0xff)

/* The GeglBuffer index is written to the file as a linked list of
 * GeglBufferBlock's, each block encodes it's own length and the offset
 * of the file which the next block can be found. The last block in the
 * list has the next offset set to 0.
 */

typedef struct {
  guint32  length; /* the length of this block */
  guint32  flags;  /* flags (can be used to handle different block types
                    * differently
                    */
  guint64  next;   /*next block element in file, this is the offset in the
                    *file that the next block in the chain resides at
                    */
} GeglBufferBlock;

/* The header is followed by entries describing tiles stored in the swap,
 */
typedef struct {
  GeglBufferBlock block; /* The block definition for this tile entry   */
  guint64 offset;        /* offset into file for this tile             */
  gint32  x;             /* upperleft of tile % tile_width coordinates */
  gint32  y;

  gint32  z;             /* mipmap subdivision level of tile (0=100%)  */

  guint32 rev;           /* revision, if a buffer is monitored for header
                            revision changes, the existing loaded index
                            can be compare the revision of tiles and update
                            own state when revision differs. */
} GeglBufferTile;

/* A convenience union to allow quick and simple casting */
typedef union {
  guint32          length;
  GeglBufferBlock  block;
  GeglBufferHeader header;
  GeglBufferTile   tile;
} GeglBufferItem;

/* functions to initialize data structures */
GeglBufferTile * gegl_tile_entry_new (gint x,
                                      gint y,
                                      gint z);

/* intializing the header causes the format to be written out
 * as well as a hidden comment after the zero terminated format
 * with additional human readable information about the header.
 */
void gegl_buffer_header_init (GeglBufferHeader *header,
                              gint              tile_width,
                              gint              tile_height,
                              gint              bpp,
                              const Babl*       format);

void gegl_tile_entry_destroy (GeglBufferTile *entry);

GeglBufferItem *gegl_buffer_read_header(int      i,
                                        goffset *offset);
GList          *gegl_buffer_read_index (int      i,
                                        goffset *offset);

#define struct_check_padding(type, size) \
  if (sizeof (type) != size) \
    {\
      g_warning ("%s %s is %i bytes, should be %i padding is off by: %i bytes %i ints", G_STRFUNC, #type,\
         (int) sizeof (type),\
         size,\
         (int) sizeof (type) - size,\
         (int)(sizeof (type) - size) / 4);\
      return;\
    }
#define GEGL_BUFFER_STRUCT_CHECK_PADDING \
  {struct_check_padding (GeglBufferBlock, 16);\
  struct_check_padding (GeglBufferHeader, 256);}
#define GEGL_BUFFER_SANITY {static gboolean done=FALSE;if(!done){GEGL_BUFFER_STRUCT_CHECK_PADDING;done=TRUE;}}

#endif
