//------------------------------------------------------------
// $Id: pnmReader.cc,v 1.1.1.1 1999/11/25 06:26:09 yamauchi Exp $
//------------------------------------------------------------
//	(M\pi)^2 read one pnm file
//	1998年9月21日(月)  
//	Copyright (C) 1997--1998 YAMAUCHI Hitoshi 山内 斉
// 	pnm file を読み，PNMReader class に入れて返す
//
// int readOnePNMFile(const char *pPNMFilename, PNMReader *pPNMbuf)
//	@param pPNMFilename	読み込むファイル名
//	@param pPNMbuf		pnm ファイルのメモリイメージ(確保してから渡す)
//	@return fail 0 else succeed
//------------------------------------------------------------
#include <iostream.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include "pnmReader.h"

#ifdef USE_MPI
#  include "mpi2exit.h"
#endif
//------------------------------
// ppm file のヘッダを読む
//	return PPM type
//------------------------------
// PPM type 
// 	PPM ASCII 	Dither (depth = 1)	P1
// 	PPM ASCII 	Gray 			P2
// 	PPM ASCII 	Full 			P3
// 	PPM RAW 	Dither (depth = 1)	P4 (packed?)
// 	PPM RAW 	Gray 			P5
// 	PPM RAW 	Full 			P6
//------------------------------
static void errExitReadHeadder(const char *pMes)
{
    cerr << "Error! : readHeadder::fail to read ppm file headder." << endl;
    cerr << pMes << endl;
#ifdef USE_MPI
    commMpi2_exit(1);
#else
    exit(1);
#endif
}

static int readPNMHeadder(FILE *pfp, int *pSizeX, int *pSizeY, int *pDepth, 
			  char pComBuf[][PNMREADER_MAX_LINELEN])
{
    assert(pfp     != NULL);
    assert(pComBuf != NULL);
    
    // check this is PPM file.
    char buff[PNMREADER_MAX_LINELEN];
    int type = -1;
    if(fgets(buff, PNMREADER_MAX_LINELEN, pfp) == NULL)
    {
	errExitReadHeadder("Can not read 1st line");
    }else
    {
	if(buff[0] != 'P')
	{
	    errExitReadHeadder("This is not PPM file.");
	}
	switch(buff[1])
	{
	case '1':	case '4':	
	    errExitReadHeadder("P1, P4 format is not implemented.");
	    break;
	case '2':	case '3':	case '5':	case '6':
	    type = buff[1] - '0';
	    break;
	default:
	    errExitReadHeadder("Is it a PPM file? 1st line must be P[1-6].");
	    break;
	}
    }
    
    // get and store comment line 
    int  line = 0;
    while(fgets(buff, PNMREADER_MAX_LINELEN, pfp) != NULL)
    {
	if(buff[0] != '#')
	{
	    break;
	}else
	{
	    strncpy(pComBuf[line], buff, PNMREADER_MAX_LINELEN);
	    line++;
	}
    }
    
    // get size : comment の次は必ずサイズを仮定(手ぬきか)
    if(sscanf(buff, "%d %d", pSizeX, pSizeY) != 2)
    {
	errExitReadHeadder("Can not get image size. \n"
			   "I assume that next of comment end is "
			   "always size line. But it isn't.");
    }
    
    // get depth
    if(fgets(buff, PNMREADER_MAX_LINELEN, pfp) == NULL)    
    {
	errExitReadHeadder("Can not read image depth line.");		
    }else
    {
	if(sscanf(buff, "%d", pDepth) != 1)	
	{
	    errExitReadHeadder("Can not get image depth.\n"
			       "I assume that next of size line is "
			       "always depth line. But it isn't.");
	}
    }
    return(type);
}

//------------------------------------------------------------
// 各 reader (P5, P6)
//------------------------------------------------------------
// type P5 (PGM RAW : grayscale) の file を読む
//------------------------------
static int readPGMRawP5(FILE *pfp, PNMReader *pPNMbuf)
{
    int c;
    int grayval[3];		// RGB に同じ値を入れる
    for(int y = 0; y < pPNMbuf->getResolSizeY(); y++)
    {
	for(int x = 0; x < pPNMbuf->getResolSizeX(); x++)
	{
	    if((c = getc(pfp)) != EOF)
	    {
		// 全てに同じ値を入れておく．こうすることで save も容
		// 易になる 
		grayval[0] = grayval[1] = grayval[2] = c; 
		pPNMbuf->setRGBColors(x, y, grayval);
	    }else
	    {
		cerr << "Error! : unexpected EOF" << endl;
		return(0);	// NO  : 失敗
	    }
	}
    }
    return(1);			// YES : 成功
}

//------------------------------
// type P6 (PPM RAW : full color) の file を読む
//------------------------------
static int readPPMRawP6(FILE *pfp, PNMReader *pPNMbuf)
{
    int c;
    int color[3];		// 3 は Red, Green, Blue
    for(int y = 0; y < pPNMbuf->getResolSizeY(); y++)
    {
	for(int x = 0; x < pPNMbuf->getResolSizeX(); x++)
	{
	    for(int rgb = 0; rgb < 3; rgb++)
	    {
		if((c = getc(pfp)) != EOF)
		{
		    color[rgb] = c;
		}else
		{
		    cerr << "Error! : unexpected EOF" << endl;		
		    return(0);	// NO  : 読み込み失敗
		}
	    }
	    pPNMbuf->setRGBColors(x, y, color);
	}
    }
    return(1);			// YES : 読み込み成功
}


//------------------------------------------------------------
// pnm file を一つ読む
//------------------------------------------------------------
int readOnePNMFile(const char *pPNMFilename, PNMReader *pPNMbuf)
{
    assert((pPNMFilename != NULL) && (pPNMFilename[0] != '\0'));
    assert(pPNMbuf != NULL);

    // open file
    FILE *pfp = fopen(pPNMFilename, "r");
    if(pfp == NULL)
    {
	cerr << "Error! : pnm file [" << pPNMFilename << "] not found" << endl;
	return(0);
    }
    
    // read headder 
    int sizeX, sizeY, depth;
    sizeX = sizeY = depth = -1;
    char commentBuf[PNMREADER_MAX_COMMENTS_LINES][PNMREADER_MAX_LINELEN];
    int type = readPNMHeadder(pfp, &sizeX, &sizeY, &depth, commentBuf);

    pPNMbuf->init(sizeX, sizeY, type, (double)depth);
    for(int i = 0; i < PNMREADER_MAX_COMMENTS_LINES; i++)
    {
	if(commentBuf[i][0] == '#')
	{
	    pPNMbuf->setComments(commentBuf[i]);
	}
    }	

    // read contents 
    int readOK = 0; // 0 is fail
    switch(type)
    {
    case 1:			// 
    case 2:			// PGM ASCII : P2 (Grayscale)
    case 3:			// PPM ASCII : P3 (Full Color)
    case 4:			// 
	cerr << "Error! : pnmReader :  Not implemented for read P"
	    << type << ", I implemented P5 and P6 format." << endl; 
	break;

    case 5:			// PGM RAW   : P5 (Grayscale)
	readOK = readPGMRawP5(pfp, pPNMbuf);
	break;

    case 6:			// PPM RAW   : P6 (Full Color)
	readOK = readPPMRawP6(pfp, pPNMbuf);
	break;

    default:
	errExitReadHeadder("Error! : Internal Error : readOnePNMFile");
	break;
    }
    fclose(pfp);
    return(readOK);
}


//------------------------------------------------------------
// for readOnePNMFile test 
//------------------------------------------------------------
#ifdef TEST_READPNM
int main(int argc, char *argv[])
{
    if(argc != 3)
    {
	cerr << "Usage: readpnm pnmfilename outputtype" << endl;
	cerr << "output type is 5 or 6, that means P5(grayscale) or "
		"P6 (color)format" << endl;
	exit(1);
    }

    PNMReader pnm_r;
    readOnePNMFile(argv[1], &pnm_r);
    pnm_r.print();
    int savetype = argv[2][0] - '0';
    pnm_r.printAsP(savetype);	
    return(0);
}
// g++ -g -Wall -I../mpi2dataReader -DTEST_READPNM pnmReader.cc myString.cc -o readpnm
#endif
