#include "image.h"
#include "pnm.h"

Image::Image()
: width(0)
, height(0)
, buffer(NULL)
, pixel_size(0)
{}

Image::~Image()
{
  delete [] buffer;
}

Image::Image(unsigned int w, unsigned int h, pnm::Format type)
{
  this->width = w;
  this->height = h;
  this->type = type;

  switch (this->type)
  {
    case pnm::Format::PGM:
    {
      this->pixel_size = 1;
    }
    break;

    case pnm::Format::PPM:
    {
      this->pixel_size = 4;
    }
    break;
  }

  buffer = new pvalue_t[width * height * pixel_size];
}

Image::Image(string const& path)
: Image()
{
  ifstream is(path);
  if (!is.is_open())
  {
    cerr << "Cannot open file '" << path << "'" << endl;
    abort();
  }

  if (!pnm::read_header(is, this->type, this->width, this->height))
  {
    cerr << "Invalid header." << endl;
    abort();
  }

  switch (this->type)
  {
    case pnm::Format::PGM:
    {
      this->pixel_size = 1;
    }
    break;

    case pnm::Format::PPM:
    {
      this->pixel_size = 4;
    }
    break;
  }


  if (!this->read_body(is))
  {
    delete buffer;
    buffer = nullptr;

    cerr << "Invalid header." << endl;
    abort();
  }
}

bool Image::save(string const& path) const
{
  ofstream os(path);
  if (!os.is_open())
  {
    cerr << "Cannot open file '" << path << "'" << endl;
    return false;
  }
  pnm::write_header(os, this->type, this->width, this->height);
  this->write_body(os);
  return true;
}

bool Image::read_body(std::ifstream& istr)
{
  unsigned int const nb_pixels = width * height;
  buffer = new pvalue_t[nb_pixels * pixel_size];

  pvalue_t* pixel = buffer;
  for (unsigned int i = 0; i < nb_pixels; ++i)
  {
    switch (this->type)
    {
      case pnm::Format::PGM:
      {
        pixel[0] = istr.get();
      }
      break;

      case pnm::Format::PPM:
      {
        pixel[0] = istr.get();
        pixel[1] = istr.get();
        pixel[2] = istr.get();
      }
      break;
    }

    pixel += pixel_size;
  }

  return true;
}

bool Image::write_body(std::ofstream& ostr) const
{
  unsigned int const nb_pixels = width * height;
  pvalue_t* pixel = buffer;
  for (unsigned int i = 0; i < nb_pixels; ++i)
  {
    switch (this->type)
    {
      case pnm::Format::PGM:
      {
        ostr << (char) pixel[0];
      }
      break;

      case pnm::Format::PPM:
      {
        ostr << (char) pixel[0];
        ostr << (char) pixel[1];
        ostr << (char) pixel[2];
      }
      break;
    }

    pixel += pixel_size;
  }

  return true;
}
