#include "image_read.h"
#include "files.h"
#include <cstring>

// The file type is determined from the file extension.
enum FileType FileRead::get_file_type(const char *filename){
    // find the last '.' in the file name
    int dotposition=-1;
    for(int i=strlen(filename)-1;i>=0;--i){
        if(filename[i]=='.'){
            dotposition=i;
            break;
        }
    }
    // if '.' was not found, the whole string is considered extension
    char *ext=(char*)filename+dotposition+1; // file extension
    if((ext[0]=='p'||ext[0]=='P')&&
       (ext[1]=='n'||ext[1]=='b'||ext[1]=='g'||ext[1]=='p'||
        ext[1]=='N'||ext[1]=='B'||ext[1]=='G'||ext[1]=='P')&&
       (ext[2]=='m'||ext[2]=='M')){
        return PNM;
    }
    if((ext[0]=='j'||ext[0]=='J')&&
       (ext[1]=='p'||ext[1]=='P')&&
       ((strlen(ext)==3&&(ext[2]=='g'||ext[2]=='G'))||
        (strlen(ext)==4&&(ext[2]=='e'||ext[2]=='E')&&
                         (ext[3]=='g'||ext[3]=='G')))){
        return JPEG;
    }
    return UNKNOWN;
}

unsigned char *FileRead::read_image(const char *new_image,int *width,int *height){
#ifndef _MSC_VER
    // the progname is only used for reading the PNM format
    const char * const progname=(char*)(PROGNAME);
#endif
    switch(get_file_type(new_image)){
    case JPEG:
        fprintf(stderr,"input image has JPEG format\n");
        return jpgRead(new_image,width,height);
        break;
#ifndef _MSC_VER
    case PNM:
        fprintf(stderr,"input image has PNM format\n");
        return pnmRead(progname,new_image,width,height);
        break;
#endif
    default: // UNKNOWN
        fprintf(stderr,"%s: unknown file format\n",new_image);
        exit(-1);
    }
    return NULL;
}

unsigned char *FileRead::jpgRead(const char *texturePath,
                                  int *outImageWidth,
                                  int *outImageHeight){
    FILE *in_file;
    FOPEN_RO(in_file,texturePath)
    struct jpeg_decompress_struct cinfo;
    struct jpeg_error_mgr jerr;
    cinfo.err=jpeg_std_error(&jerr);
    JSAMPROW row_pointer[1];
    jpeg_create_decompress(&cinfo);
    jpeg_stdio_src(&cinfo,in_file);
    jpeg_read_header(&cinfo,0);
    jpeg_start_decompress(&cinfo);
    *outImageWidth=cinfo.output_width;
    *outImageHeight=cinfo.output_height;
    int depth=cinfo.num_components;
    int textureSize=(*outImageWidth)*(*outImageHeight)*depth;
    unsigned char *textureBytes=
            (unsigned char*)malloc(textureSize*sizeof(unsigned char));
    unsigned long scanline=*outImageHeight;
    row_pointer[0]=(unsigned char*)malloc((*outImageWidth)*
                                          depth*
                                          sizeof(unsigned char));
    while(cinfo.output_scanline<(unsigned)*outImageHeight){
        --scanline;
        jpeg_read_scanlines(&cinfo,row_pointer,1);
        for(int i=0;i<(*outImageWidth)*depth;++i){
            textureBytes[scanline*(*outImageWidth)*depth+i]=row_pointer[0][i];
        }
    }
    fclose(in_file);
    jpeg_finish_decompress(&cinfo);
    jpeg_destroy_decompress(&cinfo);
    return textureBytes;
}

#ifndef _MSC_VER
unsigned char *FileRead::pnmRead(const char * const progname,
                                 const char *texturePath,
                                 int *outImageWidth,
                                 int *outImageHeight){
    struct pam inpam;
    pm_init(progname, 0);
    FILE *in_file;
    FOPEN_RO(in_file,texturePath)
#ifdef PAM_STRUCT_SIZE
    pnm_readpaminit(in_file,&inpam,PAM_STRUCT_SIZE(tuple_type));
#else
    pnm_readpaminit(in_file,&inpam,sizeof(struct pam));
#endif
    *outImageWidth=inpam.width;
    *outImageHeight=inpam.height;
    int textureSize=
            (*outImageWidth)*
            (*outImageHeight)*
            inpam.depth*
            inpam.bytes_per_sample;
    unsigned char *textureBytes=
            (unsigned char*)malloc(textureSize*sizeof(unsigned char));
    tuple *tuplerow=pnm_allocpamrow(&inpam);
    for(int row=0;row<*outImageHeight;row++) {
        int column;
        pnm_readpamrow(&inpam,tuplerow);
        for (column=0;column<*outImageWidth;++column) {
            unsigned int plane;
            for(plane=0;plane<inpam.depth;++plane) {
                textureBytes[((*outImageHeight)-row-1)*3*(*outImageWidth)+3*column+plane]=
                        tuplerow[column][plane];
            }
        }
    }
    pnm_freepamrow(tuplerow);
    pm_close(in_file);
    return textureBytes;
}
#endif
