#include "MGML_IMAGE.h"
//////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////      IMAGE         ///////////////////////////////////////// 
//////////////////////////////////////////////////////////////////////////////////////////////////



int IMAGES::IMAGE::tosTexId = 0;

IMAGES::IMAGE::IMAGE()
{
	QuadrObj=gluNewQuadric();
	TexId=tosTexId;
	tosTexId++;
	data =  0;
	first_load = true;
}

IMAGES::IMAGE::~IMAGE()
{
	delete [] data;
}


IMAGES::IMAGE& IMAGES::IMAGE::operator=(const IMAGES::IMAGE& rhs)
{
	_width = rhs.width();
	_height = rhs.height();
	first_load = rhs.first_load;
	QuadrObj = rhs.QuadrObj;
	TexId = rhs.TexId;
	gfname = rhs.gfname;

	if(rhs.getConstBuffer() == 0)
		data = 0;
	else
	{
		data = new unsigned char [_width * _height * sizeof(int)];
		memcpy(data,rhs.getConstBuffer(),sizeof(unsigned char)*_width*_height*sizeof(int));
	}

	return *this;
}


//=======================================================
// установить другой размер текстуры
//=======================================================
void IMAGES::IMAGE::setSize(unsigned int w,unsigned int h) 
{
	  _width = w;
	  _height = h;
	  delete [] data;
	  data = new unsigned char [_width * _height * sizeof(int)];
}

//=================================================================
//
//=================================================================
void IMAGES::IMAGE::CopyFromBuffer(const unsigned char* buff,unsigned int size)
{
	unsigned int my_size = sizeof(unsigned int)*_width*_height;
	if(my_size!=size)
		throw MGML_ERROR::Out_Of_Range("IMAGE::CopyFromBuffer incorrect size given");

	memcpy(data,buff,size);
}

//=================================================================
//
//=================================================================
unsigned int IMAGES::IMAGE::uInt16Number(unsigned char buf[2])
{
  return (buf[1] << 8) | buf[0];
}
//=================================================================
//
//=================================================================
unsigned int IMAGES::IMAGE::uInt32Number(unsigned char buf[4])
{
  unsigned numb = buf[3];
  numb = (numb << 8) | buf[2];
  numb = (numb << 8) | buf[1];
  numb = (numb << 8) | buf[0];
  return numb;
}
//=================================================================
//
//=================================================================
int IMAGES::IMAGE::ReadFileHeader(FILE* f, int* bitmap_pos) 
{
  unsigned char header[BMP_SIZE_FILEHEADER];
  size_t numb = 0;  
  int offset = 0;

  if (fseek(f, 0, SEEK_SET))
	return 0;
 
  numb = fread(header, BMP_SIZE_FILEHEADER, 1, f);
  if (numb != 1)
    return 0;

  if (header[0] != 'B' || header[1] != 'M') 
    return 0;
  
  offset = uInt32Number(header + 10);
  
  numb = fread(header, 4, 1, f);
  if (numb != 1)
    return 0;
  
  if (uInt32Number(header) != 40)
    return 0;
 
  *bitmap_pos  = offset;
  return 1;
}
//=================================================================
//
//=================================================================
int IMAGES::IMAGE::LoadBMP(const char* file)
{
  FILE* f; 
  int bitmap_pos;
  unsigned char buf[40]; 
  size_t numb;
  int x_res;
  int y_res;
  int n_bits;
  int compression;
  int size_image;
  int n_used_colors;

  gfname = file; // запомним имя файла
  /* открываем файл */
  f = fopen(file, "rb");
  if (!f) throw MGML_ERROR::File_Error("Can not open " + gfname);
  /* читаем заголовок */
  if (!ReadFileHeader(f, &bitmap_pos))
  {
    fclose(f);
    throw MGML_ERROR::Image_File_Error("Can not ReadFileHeader, perhaps the file " + gfname + " was damaged" );
  }
  if (fseek(f, BMP_SIZE_FILEHEADER, SEEK_SET))
  {
    fclose(f);
    throw MGML_ERROR::Image_File_Error("Can not ReadFileHeader, perhaps the file " + gfname + " has NO PIXELS or damaged" );
  }
  numb = fread(buf, 40, 1, f);
  if (numb != 1)
  {
    fclose(f);
    throw MGML_ERROR::Image_File_Error("Can not ReadFileHeader, perhaps the file " + gfname + " was damaged" );
  }
  x_res = (int)uInt32Number(buf + 4);
  y_res = (int)uInt32Number(buf + 8);
  n_bits          = (int)uInt16Number(buf + 14);
  compression     = (int)uInt32Number(buf + 16);
  size_image      = (int)uInt32Number(buf + 20);
  n_used_colors   = (int)uInt32Number(buf + 32);
  
  /* читаем только полноцветные файлы */
  if (n_bits == BMP_COLOR_BITS_24)
  {
    int rgb_size;
    unsigned char* rgb;
    int y;
    unsigned char* line;
    int rest_4;

    if (bitmap_pos != BMP_SIZE_FILEHEADER + BMP_SIZE_INFOHEADER)
    {
      fclose(f);
      throw MGML_ERROR::Image_File_Error("The file " + gfname + " have NO PIXELS or was damaged" );
    }
    if (fseek(f, bitmap_pos, SEEK_SET))
    {
      fclose(f);
      throw MGML_ERROR::Image_File_Error("The file " + gfname + " have NO PIXELS or was damaged" );
    }
    rgb_size = 3 * x_res;
    rest_4 = rgb_size % 4;
    if (rest_4 > 0)
      rgb_size += 4 - rest_4;
    this->_width = x_res;
    this->_height = y_res;
	delete [] data;
    this->data = new unsigned char [x_res * y_res * 3];
    if (this->data == NULL)
      return 0;
    rgb = (unsigned char*)malloc(rgb_size);
    /* заполняем данные из файла */
    for (y = 0; y < y_res; y++)
    {
      size_t numb = 0;
      int x = 0;
      numb = fread(rgb, rgb_size, 1, f);
      if (numb != 1)
      {
        fclose(f);
        free(rgb);
        throw MGML_ERROR::Image_File_Error("The file " + gfname + " was damaged");
      }
      numb = 0;
      line = this->data + x_res * 3 * y;
      for (x = 0; x < x_res; x++)
      {
        line[2] = rgb[numb++];
        line[1] = rgb[numb++];
        line[0]= rgb[numb++];
        line += 3;
      }
    }
    fclose(f);
    free(rgb);
  }
  else
    throw MGML_ERROR::Image_File_Error("bmp files must have 24bp format, can not load " + gfname);

  convertToRGBA32();

  return 1;
}


//=================================================================
//
//=================================================================
void IMAGES::IMAGE::convertToRGBA32()
{
	unsigned int* newbuff = new unsigned int [width()*height()];

	for(unsigned int i=0;i<width();i++)
		for(unsigned int j=0;j<height();j++)
			newbuff[i*height() + j] = MGML_GRAPHICS::RGB256_TO_UINT32(rgb_getPixel(i,j));
	

	delete [] data;
	data = (unsigned char*)newbuff;
}



//=================================================================
// #CHECK_THAT
//=================================================================
void IMAGES::IMAGE::LoadToGPU()
{
	if(first_load)
	{
		/* Выравнивание в *.bmp по байту  ?????????????? */
		glPixelStorei(GL_UNPACK_ALIGNMENT,1);
		/* Создание идентификатора для текстуры */
		glGenTextures(1,&TexId);
		/* Загрузка изображения в память */

		/* Начало описания свойств текстуры */
		glBindTexture (GL_TEXTURE_2D,TexId);
		/* Создание уровней детализации и инициализация текстуры */
		gluBuild2DMipmaps(GL_TEXTURE_2D,GL_RGBA,_width, _height,GL_RGBA,GL_UNSIGNED_BYTE,data);
		/* Разрешение наложения этой текстуры на quadric-объекты */   
		gluQuadricTexture (QuadrObj, GL_TRUE);
		/* Задание параметров текстуры */
		/* Повтор изображения по параметрическим осям s и t */
		glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
		glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
		/* Использовать интерполяцию при выборе точки на текстуре */
		glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		/* Совмещать текстуру и материал объекта */
		glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
		first_load = false;
	}
	else
	{
		 glBindTexture (GL_TEXTURE_2D,TexId);
		 gluBuild2DMipmaps(GL_TEXTURE_2D,GL_RGBA,_width, _height,GL_RGBA,GL_UNSIGNED_BYTE,data);
	}



}

