#ifndef IMLIB2_LOADER_H
#define IMLIB2_LOADER_H 1
/**
 * The Imlib2 loader API
 *
 * NB! TO BE USED BY LOADERS ONLY
 */
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

/* types.h */

typedef struct _ImlibLoader ImlibLoader;

typedef struct _ImlibImage ImlibImage;

/* common.h */

#undef __EXPORT__
#if defined(__GNUC__) && __GNUC__ >= 4
#define __EXPORT__ __attribute__ ((visibility("default")))
#else
#define __EXPORT__
#endif

#if __GNUC__
#define __PRINTF_N__(no)  __attribute__((__format__(__printf__, (no), (no)+1)))
#else
#define __PRINTF_N__(no)
#endif
#define __PRINTF__   __PRINTF_N__(1)
#define __PRINTF_2__ __PRINTF_N__(2)

#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))

#define SWAP32(x) \
    ((((x) & 0x000000ff ) << 24) | \
     (((x) & 0x0000ff00 ) <<  8) | \
     (((x) & 0x00ff0000 ) >>  8) | \
     (((x) & 0xff000000 ) >> 24))

#define SWAP16(x) \
    ((((x) & 0x00ff ) << 8) | \
     (((x) & 0xff00 ) >> 8))

#ifdef WORDS_BIGENDIAN
#define SWAP_LE_16(x) SWAP16(x)
#define SWAP_LE_32(x) SWAP32(x)
#define SWAP_LE_16_INPLACE(x) x = SWAP16(x)
#define SWAP_LE_32_INPLACE(x) x = SWAP32(x)
#else
#define SWAP_LE_16(x) (x)
#define SWAP_LE_32(x) (x)
#define SWAP_LE_16_INPLACE(x)
#define SWAP_LE_32_INPLACE(x)
#endif

#define PIXEL_ARGB(a, r, g, b)  ((uint32_t)(a) << 24) | ((r) << 16) | ((g) << 8) | (b)

#define PIXEL_A(argb)  (((argb) >> 24) & 0xff)
#define PIXEL_R(argb)  (((argb) >> 16) & 0xff)
#define PIXEL_G(argb)  (((argb) >>  8) & 0xff)
#define PIXEL_B(argb)  (((argb)      ) & 0xff)

#ifndef WORDS_BIGENDIAN
#define A_VAL(p) ((uint8_t *)(p))[3]
#define R_VAL(p) ((uint8_t *)(p))[2]
#define G_VAL(p) ((uint8_t *)(p))[1]
#define B_VAL(p) ((uint8_t *)(p))[0]
#else
#define A_VAL(p) ((uint8_t *)(p))[0]
#define R_VAL(p) ((uint8_t *)(p))[1]
#define G_VAL(p) ((uint8_t *)(p))[2]
#define B_VAL(p) ((uint8_t *)(p))[3]
#endif

/* debug.h */

#if IMLIB2_DEBUG

#define DC(PFX, M, fmt...) if (__imlib_debug & M) __imlib_printf(PFX, fmt)

#define DBG_FILE    0x0001
#define DBG_LOAD    0x0002
#define DBG_LDR     0x0004
#define DBG_LDR2    0x0008

#define D(fmt...)  DC(DBG_PFX, DBG_LDR, fmt)
#define Dx(fmt...) DC(NULL, DBG_LDR, fmt)
#define DL(fmt...) DC(DBG_PFX, DBG_LDR2, fmt)

extern unsigned int __imlib_debug;

__PRINTF_2__ void __imlib_printf(const char *pfx, const char *fmt, ...);

unsigned int    __imlib_time_us(void);

#else

#define DC(fmt...)
#define D(fmt...)
#define Dx(fmt...)
#define DL(fmt...)

#endif                          /* IMLIB2_DEBUG */

#define E(fmt...) __imlib_perror(DBG_PFX, fmt)
__PRINTF_2__ void __imlib_perror(const char *pfx, const char *fmt, ...);

/* image.h */

/* 32767 is the maximum pixmap dimension and ensures that
 * (w * h * sizeof(uint32_t)) won't exceed ULONG_MAX */
#define X_MAX_DIM 32767
/* NB! The image dimensions are sometimes used in (dim << 16) like expressions
 * so great care must be taken if ever it is attempted to change this
 * condition */

#define IMAGE_DIMENSIONS_OK(w, h) \
   ( ((w) > 0) && ((h) > 0) && ((w) <= X_MAX_DIM) && ((h) <= X_MAX_DIM) )

#define LOAD_BREAK       2      /* Break signaled by progress callback */
#define LOAD_SUCCESS     1      /* Image loaded successfully           */
#define LOAD_FAIL        0      /* Image was not recognized by loader  */
#define LOAD_OOM        -1      /* Could not allocate memory           */
#define LOAD_BADFILE    -2      /* File could not be accessed          */
#define LOAD_BADIMAGE   -3      /* Image is corrupt                    */
#define LOAD_BADFRAME   -4      /* Requested frame not found           */

typedef struct _ImlibImageFileInfo {
    struct _ImlibImageFileInfo *next;
    char           *name;
    FILE           *fp;
    const void     *fdata;
    off_t           fsize;
} ImlibImageFileInfo;

typedef struct _ImlibLoaderCtx ImlibLoaderCtx;

typedef struct _ImlibImageTag {
    char           *key;
    int             val;
    void           *data;
    void            (*destructor)(ImlibImage * im, void *data);
    struct _ImlibImageTag *next;
} ImlibImageTag;

typedef struct {
    int             canvas_w;   /* Canvas size      */
    int             canvas_h;
    int             frame_count;        /* Number of frames */
    int             frame_x;    /* Frame origin     */
    int             frame_y;
    int             frame_flags;        /* Frame flags      */
    int             frame_delay;        /* Frame delay (ms) */
    int             loop_count; /* Animation loops  */
} ImlibImageFrame;

struct _ImlibImage {
    ImlibImageFileInfo *fi;
    ImlibLoaderCtx *lc;

    int             w, h;
    uint32_t       *data;
    char            has_alpha;
    char            rsvd[3];

    int             frame;
};

#define LDR_ALPHA_NO            0       /* No alpha */
#define LDR_ALPHA_YES           1       /* Has alpha */
#define LDR_ALPHA_CHECK         2       /* May have alpa pixels */

/* Must match the ones in Imlib2.h.in */
#define FF_IMAGE_ANIMATED       (1 << 0)        /* Frames are an animated sequence    */
#define FF_FRAME_BLEND          (1 << 1)        /* Blend current onto previous frame  */
#define FF_FRAME_DISPOSE_CLEAR  (1 << 2)        /* Clear before rendering next frame  */
#define FF_FRAME_DISPOSE_PREV   (1 << 3)        /* Revert before rendering next frame */

ImlibLoader    *__imlib_FindBestLoader(const char *file, const char *format,
                                       int for_save);
int             __imlib_LoadEmbedded(ImlibLoader * l, ImlibImage * im,
                                     int load_data, const char *file);
int             __imlib_LoadEmbeddedMem(ImlibLoader * l, ImlibImage * im,
                                        int load_data, const void *fdata,
                                        unsigned int fsize);

uint32_t       *__imlib_AllocateData(ImlibImage * im);
void            __imlib_FreeData(ImlibImage * im);

typedef void    (*ImlibDataDestructorFunction)(ImlibImage * im, void *data);

void            __imlib_AttachTag(ImlibImage * im, const char *key,
                                  int val, void *data,
                                  ImlibDataDestructorFunction destructor);
ImlibImageTag  *__imlib_GetTag(const ImlibImage * im, const char *key);
ImlibImageTag  *__imlib_RemoveTag(ImlibImage * im, const char *key);
void            __imlib_FreeTag(ImlibImage * im, ImlibImageTag * t);

const char     *__imlib_GetKey(const ImlibImage * im);

ImlibImageFrame *__imlib_GetFrame(ImlibImage * im);

void            __imlib_LoadProgressSetPass(ImlibImage * im,
                                            int pass, int n_pass);
int             __imlib_LoadProgress(ImlibImage * im,
                                     int x, int y, int w, int h);
int             __imlib_LoadProgressRows(ImlibImage * im, int row, int nrows);

/* loader.h */

#define IMLIB2_LOADER_VERSION 3

#define LDR_FLAG_KEEP   0x01    /* Don't unload loader */

typedef struct {
    unsigned char   ldr_version;        /* Module ABI version */
    unsigned char   ldr_flags;  /* LDR_FLAG_... */
    unsigned short  num_formats;        /* Length og known extension list */
    const char     *const *formats;     /* Known extension list */
    void            (*inex)(int init);  /* Module init/exit */
    int             (*load)(ImlibImage * im, int load_data);
    int             (*save)(ImlibImage * im);
} ImlibLoaderModule;

#define IMLIB_LOADER_(_fmts, _ldr, _svr, _inex, _flags) \
    __EXPORT__ ImlibLoaderModule loader = { \
        .ldr_version = IMLIB2_LOADER_VERSION, \
        .ldr_flags = _flags, \
        .num_formats = ARRAY_SIZE(_fmts), \
        .formats = _fmts, \
        .inex = _inex, \
        .load = _ldr, \
        .save = _svr, \
    }

#define IMLIB_LOADER(_fmts, _ldr, _svr) \
    IMLIB_LOADER_(_fmts, _ldr, _svr, NULL, 0)

#define IMLIB_LOADER_KEEP(_fmts, _ldr, _svr) \
    IMLIB_LOADER_(_fmts, _ldr, _svr, NULL, LDR_FLAG_KEEP)

#define IMLIB_LOADER_INEX(_fmts, _ldr, _svr, _inex) \
    IMLIB_LOADER_(_fmts, _ldr, _svr, _inex, 0)

#define QUIT_WITH_RC(_err) { rc = _err; goto quit; }

#define PCAST(T, p) ((T)(const void *)(p))

#endif                          /* IMLIB2_LOADER_H */
