#include <stdio.h>
#include <stdlib.h>
#include "pnm_lib.h"

#define LS_HEADER

//-----------------------------------------------------------------
// PNM画像の読込み
//-----------------------------------------------------------------
PIXEL* read_pnm(char* ifile,PNMInfo* pi)
{
    char buffer[BUFSIZ];
    PIXEL* pdata;
    int SizeImage;
    // ビットマップファイルヘッダの読み込み
    FILE* fp=fopen(ifile,"rb");
    if(fp==NULL) {
        fprintf(stderr,"No such file or directory.\n");
        return NULL;
    }
    // マジックナンバー
    fgets(buffer,BUFSIZ,fp);
    pi->itype = atoi(buffer+1);
    if(buffer[0]!='P' || pi->itype < 0 || pi->itype > 6){
        fprintf(stderr,"pnm: format error\n");
        return NULL;
    }
    // サイズ情報
    pi->iw = pi->ih = 0;
    while(pi->iw==0 || pi->ih==0){
        fgets(buffer,BUFSIZ,fp);
        if(buffer[0]!='#'){
            sscanf(buffer,"%d %d",&pi->iw,&pi->ih);
        }
    }
    // 最大濃度数
    if(pi->itype!=PNM_P1 && pi->itype!=PNM_P4){
        pi->imax = 0;
        while(pi->imax==0){
            fgets(buffer,BUFSIZ,fp);
            if(buffer[0] != '#'){
                sscanf(buffer,"%d",&pi->imax);
            }
        }
    }
    else pi->imax = 1;
    // 画像サイズ
    if(pi->itype==PNM_P3 || pi->itype==PNM_P6){
        SizeImage=(pi->iw*pi->ih)*3;
    } else {
        SizeImage=(pi->iw*pi->ih);
    }
#ifdef LS_HEADER
    disp_header(*pi);
#endif
    // 画像データの確保
    if(!(pdata = (PIXEL*)calloc(sizeof(PIXEL),SizeImage))){
        fprintf(stderr,"cannot alloc memory..\n");
        fclose(fp);
        return NULL;
    }
    // 画像データの取得
    if(pi->itype==PNM_P3 || pi->itype==PNM_P6){
        PNM_Cimgread(&fp,*pi,pdata);
    } else {
        PNM_Gimgread(&fp,*pi,pdata);
    }
    // 後片付け
    fclose(fp);
    return pdata;
}
//-----------------------------------------------------------------
// PPM: BMP形式へのデータ変換
//-----------------------------------------------------------------
PIXEL* ppm2bmp(PNMInfo pi,PIXEL* pdata)
{
    PIXEL* bdata;
    int i,x,y,size,line,add;
    // 1ラインあたりのデータ数: 4byte 境界にあわせる
    line  = pi.iw * 3;
    if((line%4)!=0) line=((line/4)+1)*4;
    add   = line-pi.iw*3;
    // 画像サイズ
    size  = pi.ih * line;
    bdata = (PIXEL*)calloc(size,sizeof(PIXEL));
    // 画像の値のコピー
    for(y=0;y<pi.ih;y++){
        for(x=0;x<pi.iw;x++){
            bdata[((pi.ih-y-1)*line+x*3) + 0] = pdata[(y*pi.iw+x)*3 + 2];
            bdata[((pi.ih-y-1)*line+x*3) + 1] = pdata[(y*pi.iw+x)*3 + 1];
            bdata[((pi.ih-y-1)*line+x*3) + 2] = pdata[(y*pi.iw+x)*3 + 0];
        }
        for(i=0;i<add;i++)
            bdata[((pi.ih-y-1)*line+x*3) + i] = 0;
    }
    return bdata;
}
//-----------------------------------------------------------------
// PGM: BMP形式へのデータ変換
//-----------------------------------------------------------------
PIXEL* pgm2bmp(PNMInfo pi,PIXEL* pdata)
{
    PIXEL* bdata;
    int i,x,y,size,line,add;
    // 1ラインあたりのデータ数: 4PIXEL 境界にあわせる
    line  = pi.iw;
    if((line%4)!=0) line=((line/4)+1)*4;
    add   = line-pi.iw;
    // 画像サイズ
    size  = pi.ih * line;
    bdata = (PIXEL*)calloc(size,sizeof(PIXEL));
    // 画像の値のコピー
    for(y=0;y<pi.ih;y++){
        for(x=0;x<pi.iw;x++)
            bdata[((pi.ih-y-1)*line+x)]   = pdata[y*pi.iw+x];
        for(i=0;i<add;i++)
            bdata[((pi.ih-y-1)*line+x)+i] = 0;
    }
    return bdata;
}
//-----------------------------------------------------------------
// 画像情報の表示
//-----------------------------------------------------------------
void disp_header(PNMInfo pi)
{
    fprintf(stderr,"--------------------\n");
    fprintf(stderr,"  image type : P%d\n",pi.itype);
    fprintf(stderr," image width : %d\n",pi.iw);
    fprintf(stderr,"image height : %d\n",pi.ih);
    fprintf(stderr,"image bcount : %d\n",pi.imax);
    fprintf(stderr,"--------------------\n");
}
//-----------------------------------------------------------------
// 画像部の読込み[グレイスケール]
//-----------------------------------------------------------------
void PNM_Gimgread(FILE** fp,PNMInfo pi,PIXEL* pdata)
{
    int x,y,c;
    PIXEL* p=pdata;
    if(pi.itype == PNM_P1 || pi.itype == PNM_P2){
        for(y=0;y<pi.ih;y++){
            for(x=0;x<pi.iw;x++){
                fscanf(*fp,"%d ",&c); *p++=(PIXEL)c;
            }
        }
    } else {
        fread(p,sizeof(PIXEL),pi.iw*pi.ih,*fp);
    }
}
//-----------------------------------------------------------------
// 画像部の読込み[カラー]
//-----------------------------------------------------------------
void PNM_Cimgread(FILE** fp,PNMInfo pi,PIXEL* pdata)
{
    int x,y,c;
    PIXEL* p=pdata;
    if(pi.itype == PNM_P3){
        for(y=0;y<pi.ih;y++){
            for(x=0;x<pi.iw;x++){
                fscanf(*fp,"%d ",&c); *p++=(PIXEL)c;
                fscanf(*fp,"%d ",&c); *p++=(PIXEL)c;
                fscanf(*fp,"%d ",&c); *p++=(PIXEL)c;
            }
        }
    } else {
        fread(p,sizeof(CPIXEL),pi.iw*pi.ih,*fp);
    }
}
//-----------------------------------------------------------------
// PNM画像の書き出し
//-----------------------------------------------------------------
void write_pnm(char* ofile,PNMInfo pi,PIXEL* pdata)
{
    FILE* fp=fopen(ofile,"wb+");
    if(fp == NULL) return;
    PNM_Headout(&fp,pi);
    if(pi.itype==PNM_P3 || pi.itype==PNM_P6){
        PNM_Cimgout(&fp,pi,pdata);
    } else {
        PNM_Gimgout(&fp,pi,pdata);
    }
    fclose(fp);
}
//-----------------------------------------------------------------
// ヘッダ部の書き出し
//-----------------------------------------------------------------
void PNM_Headout(FILE** fp,PNMInfo pi)
{
    switch(pi.itype){
    case PNM_P1: fprintf(*fp,"P1\n"); break;
    case PNM_P2: fprintf(*fp,"P2\n"); break;
    case PNM_P3: fprintf(*fp,"P3\n"); break;
    case PNM_P4: fprintf(*fp,"P4\n"); break;
    case PNM_P5: fprintf(*fp,"P5\n"); break;
    case PNM_P6: fprintf(*fp,"P6\n"); break;
    }
    fprintf(*fp,"%d %d\n",pi.iw,pi.ih);
    if(pi.itype != PNM_P1 && pi.itype != PNM_P4){
        fprintf(*fp,"%d\n",pi.imax);
    }
}
//-----------------------------------------------------------------
// 画像部の書き出し[グレイスケール]
//-----------------------------------------------------------------
void PNM_Gimgout(FILE** fp,PNMInfo pi,PIXEL* pdata)
{
    int x,y;
    PIXEL* p=pdata;
    if(pi.itype == PNM_P1 || pi.itype == PNM_P2){
        for(y=0;y<pi.ih;y++){
            for(x=0;x<pi.iw;x++){
                fprintf(*fp,"%d ",*p++);
            }
            fprintf(*fp,"\n");
        }
    } else {
        fwrite(p,sizeof(PIXEL),pi.iw*pi.ih,*fp);
    }
}
//-----------------------------------------------------------------
// 画像部の書き出し[カラー]
//-----------------------------------------------------------------
void PNM_Cimgout(FILE** fp,PNMInfo pi,PIXEL* pdata)
{
    int x,y;
    PIXEL* p=pdata;
    if(pi.itype == PNM_P3){
        for(y=0;y<pi.ih;y++){
            for(x=0;x<pi.iw;x++){
                fprintf(*fp,"%d ",*p++);
                fprintf(*fp,"%d ",*p++);
                fprintf(*fp,"%d ",*p++);
            }
            fprintf(*fp,"\n");
        }
    } else {
        fwrite(p,sizeof(CPIXEL),pi.iw*pi.ih,*fp);
    }
}
//-----------------------------------------------------------------
// 画像値の設定
//-----------------------------------------------------------------
void pnmSetPixel(PNMInfo pi,PIXEL* pdata,int x,int y,PIXEL p)
{
    pdata[y*pi.iw+x] = p;
}
void pnmGetPixel(PNMInfo pi,PIXEL* pdata,int x,int y,PIXEL* p)
{
    *p = pdata[y*pi.iw+x];
}
void pnmSetRGB(PNMInfo pi,PIXEL* pdata,int x,int y,CPIXEL p)
{
    pdata[y*pi.iw*3+x*3+0] = p.r;
    pdata[y*pi.iw*3+x*3+1] = p.g;
    pdata[y*pi.iw*3+x*3+2] = p.b;
}
void pnmGetRGB(PNMInfo pi,PIXEL* pdata,int x,int y,CPIXEL* p)
{
    p->r = pdata[y*pi.iw*3+x*3+0];
    p->g = pdata[y*pi.iw*3+x*3+1];
    p->b = pdata[y*pi.iw*3+x*3+2];
}