#pragma description PNG image files 

#pragma MIME image/png
#pragma endian big

#include <std/mem.pat>

struct header_t {
    u8 highBitByte;
    char signature[3];
    char dosLineEnding[2];
    char dosEOF;
    char unixLineEnding;
};

struct actl_t {
    u32 frames [[comment("Total № of frames in animation")]];
    u32 plays [[comment("№ of times animation will loop")]];
} [[comment("Animation control chunk"), name("acTL")]];

enum ColorType: u8 {
    Grayscale = 0x0,
    RGBTriple = 0x2,
    Palette,
    GrayscaleAlpha,
    RGBA = 0x6
};

enum Interlacing: u8 {
    None,
    Adam7
};

struct ihdr_t {
    u32 width [[comment("Image width")]];
    u32 height [[comment("Image height")]];
    u8 bit_depth;
    ColorType color_type [[comment("PNG Image Type")]];
    u8 compression_method [[comment("Only 0x0 = zlib supported by most")]];
    u8 filter_method [[comment("Only 0x0 = adaptive supported by most")]];
    Interlacing interlacing;
};

enum sRGB: u8 {
    Perceptual = 0x0,
    RelativeColorimetric,
    Saturation,
    AbsoluteColorimetric
};

enum Unit: u8 {
    Unknown,
    Meter
};

struct phys_t {
    u32 ppu_x [[comment("Pixels per unit, X axis")]];
    u32 ppu_y [[comment("Pixels per unit, Y axis")]];
    Unit unit;
};

enum BlendOp: u8 {
    Source = 0x0,
    Over
};

enum DisposeOp: u8 {
    None = 0x0,
    Background,
    Previous
};

struct fctl_t {
    u32 sequence_no [[comment("Sequence №")]];
    u32 width [[comment("Frame width")]];
    u32 height;
    u32 xoff;
    u32 yoff;
    u16 delay_num;
    u16 delay_den;
    DisposeOp dispose_op;
    BlendOp blend_op;
};

struct fdat_t {
    u32 sequence_no;
};

fn text_len() {
   u64 len = parent.parent.length - ($ - addressof(parent.keyword));
   return len;
};

struct itxt_t {
    char keyword[];
    u8 compression_flag;
    u8 compression_method;
    char language_tag[];
    char translated_keyword[];
    char text[text_len()];
};

struct ztxt_t {
    char keyword[];
    u8 compression_method;
    char text[text_len()];
};

struct text_t {
    char keyword[];
    char text[text_len()];
};

struct iccp_t {
    char keyword[];
    u8 compression_method;
    u8 compressed_profile[text_len()];
};

struct palette_entry_t {
    u24 color;
} [[inline]];

struct chunk_t {
    u32 length [[color("17BECF")]];
    char name[4];

    #define IHDR_k "IHDR"
    #define PLTE_k "PLTE"
    #define sRGB_k "sRGB"
    #define pHYs_k "pHYs"
    #define iTXt_k "iTXt"
    #define tEXt_k "tEXt"
    #define zTXt_k "zTXt"
    #define IDAT_k "IDAT"
    #define IEND_k "IEND"
    #define gAMA_k "gAMA"
    #define iCCP_k "iCCP"
    #define acTL_k "acTL"
    #define fdAT_k "fdAT"
    #define fcTL_k "fcTL"

    if (name == IHDR_k) {
        ihdr_t ihdr [[comment("Image Header chunk"), name("IHDR")]];
    } else if (name == PLTE_k) {
        palette_entry_t entries[length / 3];
    } else if (name == sRGB_k) {
        sRGB srgb;
    } else if (name == pHYs_k) {
        phys_t phys;
    } else if (name == acTL_k) {
        actl_t  actl [[comment("Animation control chunk")]];
    } else if (name == fcTL_k) {
        fctl_t fctl [[comment("Frame control chunk")]];
    } else if (name == iTXt_k) {
        itxt_t text;
    } else if (name == gAMA_k) {
    	u32 gamma [[name("image gamma"), comment("4 byte unsigned integer representing gamma times 100000")]];
    } else if (name == iCCP_k) {
    	iccp_t iccp;
    } else if (name == tEXt_k) {
        text_t text;
    } else if (name == zTXt_k) {
        ztxt_t text;
    } else if (name == iCCP_k) {
        iccp_t iccp;
    } else if (name == fdAT_k) {
        fdat_t fdat [[comment("Frame data chunk")]];
        u8 data[length-sizeof(u32)];
    } else {
        u8 data[length];
    }

    u32 crc;
} [[name(chunkValueName(this))]];

fn chunkValueName(ref chunk_t chunk) {
    return chunk.name;
};

struct chunk_set {
    chunk_t chunks[while(builtin::std::mem::read_string($ + 4, 4) != "IEND")] [[inline]];
} [[inline]];

struct Chunks {
    chunk_t ihdr_chunk [[comment("PNG Header chunk")]];
    chunk_set set [[comment("PNG Chunks"), name("Chunks"), inline]];
    chunk_t iend_chunk [[comment("Image End Chunk")]];
};

u8 visualizer[std::mem::size()] @ 0x00 [[sealed, hex::visualize("image", this)]];
header_t header @ 0x00 [[comment("PNG file signature"), name("Signature")]];
Chunks chunks @ 0x08 [[name("Chunks")]];