/** \defgroup Utilities Utility classes */

/** @addtogroup Utilities
    @{
*/

/** @file CImage.cpp
    \brief This file contains the implementation of the CImage class,
    a class representing an image.
*/

#include <fstream>
#include <iostream>
#include <string>

#include "popassert.h"
#include "CImage.h"


// The following structure must be packed that way.
#pragma pack( 1 )


/**
   \brief This structure is one of the bmp headers.
*/
struct bmpinfoheader
{
  unsigned long biSize;
  long biWidth;
  long biHeight;
  unsigned short biPlanes;
  unsigned short biBitCount;
  unsigned long biCompression;
  unsigned long biSizeImage;
  long biXPelsPerMeter;
  long biYPelsPerMeter;
  unsigned long biClrUsed;
  unsigned long biClrImportant;
};

/**
   \brief This structure is another bmp header.
*/
struct bmpfileheader
{
  unsigned short bfType;
  unsigned long bfSize;
  unsigned short bfReserved1;
  unsigned short bfReserved2;
  unsigned long bfOffBits;
};

#pragma pack( )


/**
   It uses the BMP file format.  It returns -1 if the file couldn't
   be opened and -2 if an error occured while writing to the file.
*/
int CImage::WriteBmpFile(const std::string& FileName) const
{
  bmpinfoheader bmih;  // Structure containing a header
  bmpfileheader bmfh;  // Structure containing a header

  unsigned int extrabytes, bytesize; 

  // Used to add bytes so that width is a multiple of 4
  extrabytes = (4 - (m_Width * 3) % 4) % 4;

  // Size of the image
  bytesize = (m_Width * 3 + extrabytes) * m_Height;

  // Info needed by BMP header
  bmfh.bfType = ('M' << 8) + 'B';
  bmfh.bfSize = 0;
  bmfh.bfReserved1 = 0;
  bmfh.bfReserved2 = 0;
  bmfh.bfOffBits = sizeof(bmpfileheader) + sizeof(bmpinfoheader);

  bmih.biSize = sizeof(bmpinfoheader);
  bmih.biWidth = m_Width;
  bmih.biHeight = m_Height;
  bmih.biPlanes = 1; // Must be that
  bmih.biBitCount = 24; // 3 * sizeof(char), in bits
  bmih.biCompression = 0L; // Correspond to BI_RGB
  bmih.biSizeImage = bytesize;
  bmih.biXPelsPerMeter = 0;
  bmih.biYPelsPerMeter = 0;
  bmih.biClrUsed = 0;
  bmih.biClrImportant = 0;


  // File writing
  std::ofstream File;

  File.open(FileName.c_str(), std::ios::binary);
  if (File.fail())
    {
      return -1;
    }

  // Writing of the headers
  File.write( (char *) &bmfh, sizeof(bmpfileheader));
  File.write( (char *) &bmih, sizeof(bmpinfoheader));


  // We allocate memory for a phony pointer to complete line so they
  // hold a multiple of 4 bytes
  char *pTemp = new char[extrabytes];

  char ColorTemp[3];

  // (b, g, r) colors are written line by line, from the lower left corner
  // to the upper right corner

  for (int j = m_Height; j > 0; --j)
    {
      for (int i = 0; i < m_Width; ++i)
        {
          ColorTemp[0] = char (255 * (*this)(i, j-1, 2));
          ColorTemp[1] = char (255 * (*this)(i, j-1, 1));
          ColorTemp[2] = char (255 * (*this)(i, j-1, 0));
          File.write( (char *) &ColorTemp, 3*sizeof(char));
        }
      File.write( (char *) &pTemp, extrabytes*sizeof(char));
    }

  delete []pTemp;

  bool ReturnValue = !File.fail();

  File.close();

  ReturnValue = ReturnValue && !File.fail();

  return ( (ReturnValue) ? 0 : -2 );
}

/** @} */
