/* LICENSE:
  =========================================================================
    CMPack'04 Source Code Release for OPEN-R SDK 1.1.5-r2 for ERS7
    Copyright (C) 2004 Multirobot Lab [Project Head: Manuela Veloso]
    School of Computer Science, Carnegie Mellon University
    All rights reserved.
  ========================================================================= */

#include "PPMImage.h"
#include "RGBTriplet.h"

#include <stdio.h>
#include <string.h>

void PPMImage::freeBuffer(void) {
  if(Data!=NULL)
    delete[] Data;

  Data = NULL;
  Width = Height = 0;
}

PPMImage::PPMImage(void) {
  Data = NULL;
  Width = Height = 0;
  Filename = NULL;
}

PPMImage::PPMImage(char *filename) {
  Data = NULL;
  Width = Height = 0;

  // load will allocate buffers
  load(filename);

  //  setFilename(filename);
}

PPMImage::PPMImage(int width, int height) {
  Width = width;
  Height = height;
  Data = new RGBTriplet[Width*Height];
  Filename = NULL;
}

bool PPMImage::load(char *filename) {

  char buf[256];
  int tmp;

  // If we've got data, ditch it.
  freeBuffer();

  FILE *infile = fopen(filename, "rb");
  if(infile==NULL) {
    printf("Unable to open %s\n",
	   filename);
    return false;
  }
  
  fgets(buf, 255, infile);
  if(strcmp(buf, "P6\n")) {
    printf("Invalid magic number %s in %s\n",
	   buf, filename);
    fclose(infile);
    return false;
  }

  fgets(buf, 255, infile);
  if(sscanf(buf, "%d %d", &Width, &Height)!=2) {
    printf("Unable to read width and height.\n");
    fclose(infile);
    Width = Height = 0;
    return false;
  }

  if(Width<=0 || Height<=0) {
    printf("Invalid size (%d, %d)\n", Width, Height);
    fclose(infile);
    Width = Height = 0;
    return false;
  }

  fgets(buf, 255, infile);
  if(sscanf(buf, "%d", &tmp)!=1) {
    printf("Unable to read MAXCOLOR.\n");
    fclose(infile);
    Width = Height = 0;
    return false;
  }

  if(tmp!=NumColors) {
    printf("Invalid MAXCOLOR (we're cheesy).\n");
    fclose(infile);
    Width = Height = 0;
    return false;
  }

  Data = new RGBTriplet[Width*Height];

  for(int i=0; i<Width*Height; i++) {
    tmp = fread(&Data[i].red, 1, 1, infile);
    tmp += fread(&Data[i].green, 1, 1, infile);
    tmp += fread(&Data[i].blue, 1, 1, infile);

    if(tmp!=3) {
      printf("Premature EOF\n");
      freeBuffer();
      fclose(infile);
      return false;
    }
  }

  fclose(infile);

  //  setFilename(filename);

  return true;
}

bool PPMImage::save(char *filename) {

  FILE *outfile = fopen(filename, "wb");

  if(outfile==NULL) {
    printf("Unable to open %s\n", filename);
    return false;
  }

  if(Width<=0 || Height<=0 ||
     Data==NULL) {
    printf("Invalid Width, Height, or Data\n");
    return false;
  }

  fprintf(outfile, "P6\n%d %d\n%d\n", Width, Height, NumColors);

  // Does C++ do structure padding? This has burnt
  // me in the past, so let's assume that's the case.
  // (and pray for buffers somewhere else). I'd also
  // like to run this on x86 and MIPS at some point, so
  // perhaps I should be paranoid about byte order and
  // what-not.
  for(int i=0; i<Width*Height; i++) {
    fwrite(&Data[i].red, 1, 1, outfile);
    fwrite(&Data[i].green, 1, 1, outfile);
    fwrite(&Data[i].blue, 1, 1, outfile);
  }

  fclose(outfile);
  
  return true;
}

RGBTriplet PPMImage::get(int x, int y) {
  if(x < 0 || x >= Width ||
     y < 0 || y >= Height) {
    printf("(%d %d) isn't in the image\n", x, y);
    return RGBTriplet(0, 0, 0);
  }

  return Data[y*Width + x];
}

void PPMImage::set(int x, int y, RGBTriplet color) {
  if(x < 0 || x >= Width ||
     y < 0 || y >= Height) {
    printf("(%d %d) isn't in the image\n", x, y);
    return;
  }

  Data[y*Width + x] = color;
}

void PPMImage::replace(RGBTriplet replace_me,
		       RGBTriplet new_color){

  for(int i=0; i<Width*Height; i++)
    if(Data[i]==replace_me)
      Data[i] = new_color;
}

// Not pretty.
void PPMImage::copyInto(int x, int y, PPMImage &other) {
  for(int cx=0; cx<other.Width; cx++)
    for(int cy=0; cy<other.Height; cy++) {
      set(x+cx, y+cy, other.get(cx, cy));
    }
}

void PPMImage::copyInto(int x, int y, PPMImage &other,
			int start_x, int start_y, 
			int width, int height) {
  for(int cx=start_x; cx<start_x + width; cx++)
    for(int cy=start_y; cy<start_y + height; cy++) {
      set(x+cx - start_x, y+cy - start_y, other.get(cx, cy));
    }
}

// void PPMImage::getFilename(void) {
//   return Filename;
// }


// void PPMImage::setFilename(char *filename) {

//   if(Filename!=NULL)
//     delete[] Filename;

//   Filename = new char[strlen(filename) + 1];
//   strcpy(Filename, filename);
// }
