#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <jpeglib.h>
#include <png.h>


typedef struct {
	int width;
	int height;
	int bytes_per_pixel;
	unsigned char **scanLine;
} Image;


/*
typedef struct {
    unsigned	    width;
    unsigned	    height;
    unsigned	    bytes_per_pixel;
    unsigned char   pixel_data[];
} Image;
*/

typedef struct {
    unsigned	    magic;
    unsigned	    version;
    unsigned	    size;

    unsigned	    imageCount;
} Images;


typedef struct {
    char	    name[32];

    unsigned	    width;
    unsigned	    height;

    unsigned	    pixel_data_bpp;
    unsigned	    pixel_advance;
    unsigned	    pixel_data_offset;

    unsigned	    alpha_data_bpp;
    unsigned	    alpha_advance;
    unsigned	    alpha_data_offset;
} ImageNew;


Image *createImage(int width, int height)
{
    int i;
    Image *img = (Image *)malloc(sizeof(Image));
    img->width = width;
    img->height = height;
    //printf("Allocating Image: %i x %i\n", width, height);
    img->scanLine = (unsigned char**)malloc(sizeof(char*)*height);
    for (i = height - 1; i >= 0; i--)
	img->scanLine[i] = (unsigned char*)malloc(4*width);
    return img;
}


Image *loadJpegImage(char *jpegFileName)
{
    int i;
    Image *img;
    struct jpeg_decompress_struct cinfo;
    struct jpeg_error_mgr err;

    /* Open and initialise */
    FILE *file = fopen(jpegFileName, "r");
    if ( !file ) {
	printf("Error opening file %s\n", jpegFileName);
	return 0;
    }
    cinfo.err = jpeg_std_error(&err);
    //printf("loading image: %s (%i)\n", jpegFileName, file);
    jpeg_create_decompress(&cinfo);
    jpeg_stdio_src(&cinfo, file);
    jpeg_read_header(&cinfo, 1);
    jpeg_calc_output_dimensions(&cinfo);
    jpeg_start_decompress(&cinfo);

    /* Setup image */
    img = createImage(cinfo.output_width, cinfo.output_height);
    img->bytes_per_pixel = 3;

    /* Decode image */
    for (i = 0; i < img->height; i++)
	jpeg_read_scanlines(&cinfo, (void*)&img->scanLine[i], 1);

    /* Clean up */
    jpeg_finish_decompress(&cinfo);
    jpeg_destroy_decompress(&cinfo);
    fclose(file);
    return img;
}


Image *loadPngImage(char *pngFileName)
{
    Image *img;
    png_structp png;
    png_infop cinfo;
    png_infop cinfo_end;
    struct png_read_struct *reads;
    int width, height;

    /* Open and initialise */
    FILE *file = fopen(pngFileName, "r");
    if ( !file ) {
	printf("Error opening file %s\n", pngFileName);
	return 0;
    }
    png = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
    cinfo = png_create_info_struct(png);
    cinfo_end = png_create_info_struct(png);

    png_init_io(png, file);
    png_read_info(png, cinfo);
    width = png_get_image_width(png, cinfo);
    height = png_get_image_height(png, cinfo);

    /* Setup image */
    img = createImage(width, height);
    img->bytes_per_pixel = 4;

    /* Decode image */
    png_start_read_image(png);
    png_read_image(png, (png_bytepp)img->scanLine);

    /* Clean up */
    png_read_end(png, cinfo_end);
    png_destroy_read_struct(&png, &cinfo, &cinfo_end);
    return img;
}


Image *loadImage(char *fileName)
{
    if (fileName) {
	int i = strlen(fileName) - 1;
	while (fileName[i] != '.')
	    i--;
	if (!strcmp(fileName + i, ".png"))
	    return loadPngImage(fileName);
	if (!strcmp(fileName + i, ".jpg"))
	    return loadJpegImage(fileName);
    }
    return 0;
}


void writeInt(int fd, unsigned val)
{
    unsigned char c1 = (val >>  0) & 0xFF; 
    unsigned char c2 = (val >>  8) & 0xFF; 
    unsigned char c3 = (val >> 16) & 0xFF; 
    unsigned char c4 = (val >> 24) & 0xFF; 
    write(fd, &c1, 1);
    write(fd, &c2, 1);
    write(fd, &c3, 1);
    write(fd, &c4, 1);
}


#define FORMAT_TO_16
void writeMapableImage(const char *name, Image *image)
{
    unsigned i, j;
    int fd = open(name, O_WRONLY | O_CREAT);
    writeInt(fd, image->width);
    writeInt(fd, image->height);
#ifdef FORMAT_TO_16
    if ( image->bytes_per_pixel == 3 ) {
        writeInt(fd, 2);
	writeInt(fd, 0);
	for (j = 0; j < image->height; j++) {
	    unsigned char *row = image->scanLine[j];
	    for (i = 0; i < (image->width>>1); i++) {
		unsigned int col1 = ((row[2]>>3)<<11) | ((row[1]>>2)<<5) | (row[0]>>3);
		unsigned int col2 = ((row[5]>>3)<<11) | ((row[4]>>2)<<5) | (row[3]>>3);
		unsigned int dst = (col2 << 16) | col1;
		write(fd, &dst, 4);
		row += 6;
	    }
	    if (image->width & 1) {
		unsigned short col1 = ((row[0]>>3)<<11) | ((row[1]>>2)<<5) | (row[2]>>3);
		write(fd, &col1, 2);
	    }
	}
    } else if ( image->bytes_per_pixel == 4 ) {
	writeInt(fd, -2);
	writeInt(fd, 0);
	for (j = 0; j < image->height; j++) {
	    unsigned char *row = image->scanLine[j];
	    for (i = 0; i < image->width; i++) {
		unsigned int r, g, b, col1;
		unsigned char r1, g1, b1;
		unsigned int a1 = row[3];

		r1 = (a1 * row[0]) / 255;
		g1 = (a1 * row[1]) / 255;
		b1 = (a1 * row[2]) / 255;
//		r1 = r;
//		g1 = g;
//		b1 = b;

		a1 = 255 - a1;
		write(fd, &r1, 1);
		write(fd, &g1, 1);
		write(fd, &b1, 1);
		write(fd, &a1, 1);
		row += 4;
	    }
	}
//	for (j = 0; j < image->height; j++)
//	    write(fd, image->scanLine[j], image->width*image->bytes_per_pixel);
/*
	writeInt(fd, -2);
	writeInt(fd, 0);
	for (j = 0; j < image->height; j++) {
	    char *line = image->scanLine[j];
	    for (i = 0; i < image->width; i++) {
		unsigned short col = ((line[0]>>3)<<11) | ((line[1]>>2)<<5) | (line[2]>>3);
		write(fd, &col, 2);
		write(fd, &line[3], 1);
		line += 4;
	    }
	}
*/
    } else {
	printf("Not supported depth (shouldn't happen)\n");
    }
#else
    writeInt(fd, image->bytes_per_pixel);
    writeInt(fd, 0);
    for (j = 0; j < image->height; j++)
        write(fd, image->scanLine[j], image->width*image->bytes_per_pixel);
#endif
    close(fd);
}


int main(int argc, char *argv[])
{
    writeMapableImage(argv[2], loadImage(argv[1]));
    return 0;
}

