//================================================================
//    ppmgraph.h
//    PPM形式で絵を描いてGIFで出力するためのライブラリ
//    Copyright (C) Naoki Watanabe. 1995-1999. All rights reserved.
//================================================================

#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>

//#define PATH_OF_PPMTOGIF "/usr/local/bin/ppmtogif"
#define PATH_OF_PPMTOGIF "/usr/X11R6/bin/ppmtogif"

//---- Structure for color and a pixel in VRAM.
struct Color{
  u_char R, G, B;
  Color( void ){
    R = G = B = 0;
  }
  Color( u_char _r, u_char _g, u_char _b ){
    R = _r;
    G = _g;
    B = _b;
  }
};

//---- Class for the memory of the image and associated manipulations.
class Vram
{
  u_int  width, height;
  Color* vram;
  Color  color;

  int    pid, pipefds[2];
public:
       Vram( u_int _width, u_int _height );
      ~Vram();
  void SetColor( u_char R, u_char G, u_char B );
  void DrawPoint( int x, int y );
  void DrawLine( int x1, int y1, int x2, int y2 );
  void DrawCircle( int xc, int yc, u_int r );
  int  PastePPM( int xo, int yo, char* fname );
};

//---- Constructor; Fork a ppmtogif process, allocate the memory.
Vram::Vram( u_int _width, u_int _height )
: width(_width), height(_height), color(0xff,0xff,0xff)
{
  pipe(pipefds);

  if( (pid=fork()) == 0 ){
    dup2( pipefds[0], 0 );
    close(pipefds[1]);
    close(2);

    write( 1, "Content-Type: image/gif\n\n", 25 );
    execl( PATH_OF_PPMTOGIF, "ppmtogif", 0 );
    exit(1);
  }else{
    close(pipefds[0]);
  }

  vram = new Color [width*height];
}

//---- Destructor; Send the data to the ppmtogif process, release the memory.
Vram::~Vram()
{
  char head[32];
  int size = sprintf( head, "P6\n%3d %3d\n255\n", width, height );

  write( pipefds[1], head, size );
  write( pipefds[1], vram, sizeof(Color)*width*height );

  close( pipefds[1] );

  delete [] vram;

  waitpid( pid, 0, 0 );
}

//---- Change the current drawing color.
void Vram::SetColor( u_char R, u_char G, u_char B )
{
  color = Color(R,G,B);
}

//---- Draw a dot on the VRAM.
void Vram::DrawPoint( int x, int y )
{
  if( x<0 || (int)width<=x || y<0 || (int)height<=y ) return;
  vram[y*width+x] = color;
}

//---- Draw a segment on the VRAM.
void Vram::DrawLine( int x1, int y1, int x2, int y2 )
{
  int dx=x2-x1, dy=y2-y1, sx=1, sy=1;

  if( dx<0 ){
    dx *= -1;    sx *= -1;
  }
  if( dy<0 ){
    dy *= -1;    sy *= -1;
  }

  DrawPoint( x1, y1 );

  if( dx>dy ){
    for( int i=dx, de=i/2; i; i-- ){
      x1 += sx;
      de += dy;
      if( de>dx ){
	de -= dx;
	y1 += sy;
      }
      DrawPoint( x1, y1 );
    }
  }else{
    for( int i=dy, de=i/2; i; i-- ){
      y1 += sy;
      de += dx;
      if( de>dy ){
	de -= dy;
	x1 += sx;
      }
      DrawPoint( x1, y1 );
    }
  }
}

//---- Draw a circle on the VRAM.
void Vram::DrawCircle( int xc, int yc, u_int r )
{
  int i=0, j=r, de=1-2*r;

  while( i<=j ){
    DrawPoint( xc+i, yc+j );    DrawPoint( xc-i, yc+j );
    DrawPoint( xc+j, yc+i );    DrawPoint( xc-j, yc+i );
    DrawPoint( xc+i, yc-j );    DrawPoint( xc-i, yc-j );
    DrawPoint( xc+j, yc-i );    DrawPoint( xc-j, yc-i );

    if( de<0 ){
      de += 4*i+6;
      i++;
    }else{
      de += 4*(i-j)+10;
      i++; j--;
    }
  }
}

//---- Paste a ppm file onto the VRAM.
int Vram::PastePPM( int xo, int yo, char* fname )
{
  FILE* fptr;
  char  buf[64];
  int dx, dy;

  if( (fptr = fopen( fname, "r" ))==NULL ) return 1;

  fgets( buf, 64, fptr ); // read P6

  do{ fgets( buf, 64, fptr ); }while( buf[0] =='#' ); // Skip comments
  sscanf( buf, "%d %d", &dx, &dy ); // read width and height

  fgets( buf, 64, fptr ); // read maximam bright

  for( int j=0; j<dy; j++ ){
    fread( &vram[(yo+j)*width+xo], 1, sizeof(Color)*dx, fptr );
  }

  fclose(fptr);

  return 0;
}