//Asper
#ifndef INCLUDE_LIBIMG_H
#define INCLUDE_LIBIMG_H

#ifndef INCLUDE_KOLIBRI_H
#include "../lib/kolibri.h"
#endif

#ifndef INCLUDE_MEM_H
#include "../lib/mem.h"
#endif

#ifndef INCLUDE_DLL_H
#include "../lib/dll.h"
#endif

#ifndef INCLUDE_LIBIO_H
#include "../lib/obj/libio.h"
#endif

//library
dword libimg = #alibimg;
char alibimg[] = "/sys/lib/libimg.obj";
	
dword libimg_init = #alibimg_init;
dword img_is_img  = #aimg_is_img;
dword img_to_rgb2 = #aimg_to_rgb2;
dword img_decode  = #aimg_decode;
dword img_destroy = #aimg_destroy;
dword img_draw    = #aimg_draw;
dword img_create  = #aimg_create;
dword img_encode  = #aimg_encode;
dword img_convert = #aimg_convert;

//dword img_flip    = #aimg_flip;
//dword img_rotate  = #aimg_rotate;
$DD 2 dup 0

//import  libimg                     , \
char alibimg_init[] = "lib_init";
char aimg_is_img[]  = "img_is_img";
char aimg_to_rgb2[] = "img_to_rgb2";
char aimg_decode[]  = "img_decode";
char aimg_destroy[] = "img_destroy";
char aimg_draw[]    = "img_draw";
char aimg_create[]  = "img_create";
char aimg_encode[]  = "img_encode";
char aimg_convert[] = "img_convert";
//char aimg_flip[]    = "img_flip";
//char aimg_rotate[]  = "img_rotate ";

//invoke  img.scale, ebx, 0, 0, [ebx + Image.Width], [ebx + Image.Height], 0, LIBIMG_SCALE_TYPE_STRETCH, LIBIMG_SCALE_ALG_BILINEAR, edx, ecx

#define LIBIMG_FORMAT_BMP       1
#define LIBIMG_FORMAT_ICO       2
#define LIBIMG_FORMAT_CUR       3
#define LIBIMG_FORMAT_GIF       4
#define LIBIMG_FORMAT_PNG       5
#define LIBIMG_FORMAT_JPEG      6
#define LIBIMG_FORMAT_TGA       7
#define LIBIMG_FORMAT_PCX       8
#define LIBIMG_FORMAT_XCF       9
#define LIBIMG_FORMAT_TIFF     10
#define LIBIMG_FORMAT_PNM      11
#define LIBIMG_FORMAT_WBMP     12
#define LIBIMG_FORMAT_XBM      13
#define LIBIMG_FORMAT_Z80      14

// values for Image.Type
// must be consecutive to allow fast switch on Image.Type in support functions
#define IMAGE_BPP8i  1  // indexed
#define IMAGE_BPP24  2
#define IMAGE_BPP32  3
#define IMAGE_BPP15  4
#define IMAGE_BPP16  5
#define IMAGE_BPP1   6
#define IMAGE_BPP8g  7  // grayscale
#define IMAGE_BPP2i  8
#define IMAGE_BPP4i  9
#define IMAGE_BPP8a 10  // grayscale with alpha channel; application layer only!!! 
                        // kernel doesn't handle this image type, 
                        // libimg can only create and destroy such images

struct libimg_image
{
    dword checksum; // ((Width ROL 16) OR Height) XOR Data[0]        ; ignored so far
    dword w;
    dword h;
    dword next;
    dword previous;
    dword type;     // one of Image.bppN
    dword imgsrc;
    dword palette;  // used iff Type eq Image.bpp1, Image.bpp2, Image.bpp4 or Image.bpp8i
    dword extended;
    dword flags;    // bitfield
    dword delay;    // used iff Image.IsAnimated is set in Flags
    dword image;
    void load();
    void convert_into();
    void replace_color();
    void set_vars();
    void draw();
};

:void libimg_image::set_vars()
{
    $push edi
    EDI = image;
    checksum = DSWORD[EDI];
    w = ESDWORD[EDI+4];
    h = ESDWORD[EDI+8];
    next = ESDWORD[EDI+12];
    previous = ESDWORD[EDI+16];
    imgsrc = ESDWORD[EDI+24];       
    palette = ESDWORD[EDI+28];      
    extended = ESDWORD[EDI+32];     
    flags = ESDWORD[EDI+36];        
    delay = ESDWORD[EDI+40];    
    $pop edi
}

:void libimg_image::load(dword file_path)
{
    load_image(file_path);
    if (!EAX) {
        notify("'Error: Image not loaded'E");
    } else {
        image = EAX;
        set_vars();
    }
}

:void libimg_image::replace_color(dword old_color, new_color)
{
    EDX =  w * h * 4 + imgsrc;
    for (ESI = imgsrc; ESI < EDX; ESI += 4) if (DSDWORD[ESI]==old_color) DSDWORD[ESI] = new_color;
}

:void libimg_image::draw(dword _x, _y, _w, _h, _xoff, _yoff)
{
    if (image) img_draw stdcall(image, _x, _y, _w, _h, _xoff, _yoff);
}

:void libimg_image::convert_into(dword _to)
{
    img_convert stdcall(image, 0, _to, 0, 0);
    if (!EAX) {
        notify("'LibImg convertation error!'E");
    } else {
        image = EAX;
        set_vars();
    }
}

:dword load_image(dword filename)
{
        //align 4
        dword img_data=0;
        dword img_data_len=0;
        dword fh=0;
        dword image=0;

        byte tmp_buf[40];
        $and     img_data, 0
        //$mov     eax, filename
        //$push    eax        
        //invoke  file.open, eax, O_READ
        file_open stdcall (filename, O_READ);
        $or      eax, eax
        $jnz      loc05  
        $stc
        return 0;
    @loc05:    
        $mov     fh, eax
        //invoke  file.size
        file_size stdcall (filename);
        $mov     img_data_len, ebx
        //stdcall mem.Alloc, ebx
        mem_Alloc(EBX);
        
        $test    eax, eax
        $jz      error_close
        $mov     img_data, eax
        //invoke  file.read, [fh], eax, [img_data_len]
        file_read stdcall (fh, EAX, img_data_len);
        $cmp     eax, -1
        $jz      error_close
        $cmp     eax, img_data_len
        $jnz     error_close
        //invoke  file.close, [fh]
        file_close stdcall (fh);
        $inc     eax
        $jz      error_
//; img.decode checks for img.is_img
//;       //invoke  img.is_img, [img_data], [img_data_len]
//;       $or      eax, eax
//;       $jz      exit
        //invoke  img.decode, [img_data], [img_data_len], 0
        EAX=img_data;
        img_decode stdcall (EAX, img_data_len,0);
        $or      eax, eax
        $jz      error_
        $cmp     image, 0
        $pushf
        $mov     image, eax
        //call    init_frame
        $popf
        //call    update_image_sizes
        mem_Free(img_data);//free_img_data(img_data);
        $clc
        return image;

@error_free:
        //invoke  img.destroy, [image]
        img_destroy stdcall (image);
        $jmp     error_

@error_pop:
        $pop     eax
        $jmp     error_
@error_close:
        //invoke  file.close, [fh]
        file_close stdcall (fh);
@error_:
        mem_Free(img_data);
        $stc
        return 0;
}

:dword create_image(dword type, dword width, dword height) {
    img_create stdcall(width, height, type);
    return EAX;
}

// size - output parameter, error code / the size of encoded data
:dword encode_image(dword image_ptr, dword options, dword specific_options, dword* size) {
    img_encode stdcall(image_ptr, options, specific_options);
    ESDWORD[size] = ECX;
    
    return EAX;
}

//NOTICE: DO NOT FORGET TO INIT libio AND libimg!!!
#ifdef LANG_RUS
#define TEXT_FILE_SAVED_AS "'���� ��࠭�� ��� "
#else
#define TEXT_FILE_SAVED_AS "'File saved as "
#endif
:void save_image(dword _image_pointer, _w, _h, _path)
{
    char save_success_message[4096+200];
    dword encoded_data=0;
    dword encoded_size=0;
    dword image_ptr = 0;
    
    image_ptr = create_image(IMAGE_BPP24, _w, _h);

    if (image_ptr == 0) {
        notify("'Error saving file, probably not enought memory!' -E");
    }
    else {
        EDI = image_ptr;
        memmov(EDI.libimg_image.imgsrc, _image_pointer, _w * _h * 3);

        encoded_data = encode_image(image_ptr, LIBIMG_FORMAT_PNG, 0, #encoded_size);

        img_destroy stdcall(image_ptr);

        if(encoded_data == 0) {
            notify("'Error saving file, incorrect data!' -E");
        }
        else {
            if (CreateFile(encoded_size, encoded_data, _path) == 0) {
                strcpy(#save_success_message, TEXT_FILE_SAVED_AS);
                strcat(#save_success_message, _path);
                strcat(#save_success_message, "' -O");
                notify(#save_success_message);
            }
            else {
                notify("'Error saving image file!\nNot enough space? Path wrong?\nFile system is not writable?..' -E");
            }
        }
    }
}



/////////////////////////////
/*
//  DRAW ICON PATTERN / TEMP
*/
/////////////////////////////

:void DrawIcon32(dword x,y, _bg, icon_n) {
    static libimg_image i32;
    static dword bg;
    //load_dll(libimg, #libimg_init,1);
    if (!i32.image) || (bg!=_bg) {
        bg = _bg;
        i32.load("/sys/icons32.png");
        i32.replace_color(0x00000000, bg);
    }
    if (icon_n>=0) i32.draw(x, y, 32, 32, 0, icon_n*32);
}

:void DrawIcon16(dword x,y, bg, icon_n) {
    static libimg_image i16;
    //load_dll(libimg, #libimg_init,1);
    if (!i16.image) {
        i16.load("/sys/icons16.png");
        i16.replace_color(0xffFFFfff, bg);
        i16.replace_color(0xffCACBD6, MixColors(bg, 0, 220));
    }
    if (icon_n>=0) i16.draw(x, y, 16, 16, 0, icon_n*16);
}


#endif