#include "m3dt.h"
#include "m3derror.h"
#include <iostream>
#include <unistd.h>
#include <stdlib.h>
#include <string>
#include <map>
#include "image_info.h"
#include <GL/glew.h>
#include <GL/glfw.h>
#include <vector>

//Выходной формат файла
M3D::M3DT m3dt_file;

void load_jpg( std::string fname, Image_Info *image_info);
void load_png( std::string fname, Image_Info *image_info);

void load_file( std::string fname, Image_Info *image_info)
{
    std::string extension = fname.substr(fname.find_last_of(".")+1);
    std::cerr<<"extension:\""<<extension<<"\""<<std::endl;
    if(extension=="jpg" || extension == "jpeg")
        return load_jpg( fname, image_info);
    else if(extension == "png")
	return load_png( fname, image_info);
    else 
    {
        std::cerr<<"Unknown texture type:"<<extension<<std::endl;
        exit ( EXIT_FAILURE );
    }
    
}
int main ( int argc, char *argv[] )
{
	try{
	if ( argc < 5 )
	{
		std::cout<<"Usage: "<<argv[0]<<" -o output_file -i input_file [-l N lod_M_file] -f <output_format>"<<std::endl;
		std::cout<<"\tOutput format possible values:"<<std::endl;
		std::cout<<"\t\t0\tuncompressed image"<<std::endl; 
		std::cout<<"\t\t1\tDXT compressed image"<<std::endl; 
		return 1;
	}
	int opt, n;
	std::string ofile;
	std::string ifile;
	std::map<int, std::string> lod_files;
	int output_format;
	while ( ( opt = getopt ( argc, argv, "oilf:" ) ) != -1 )
	{
		switch ( opt )
		{
			case 'o':
				ofile = argv[optind];
				break;
			case 'i':
				ifile = argv[optind];
				break;
			case 'l':
				n = atoi ( optarg );
				lod_files.insert ( std::make_pair ( n, argv[optind] ) );
				break;
			case 'f':
				output_format = atoi ( optarg );
				break;
			default: /* '?' */
				std::cout<<"Usage: "<<argv[0]<<" -o output_file -i input_file [-l N lod_M_file] -f <output_format>"<<std::endl;
				std::cout<<"\tOutput format possible values:"<<std::endl;
				std::cout<<"\t\t0\tuncompressed image"<<std::endl; 
				std::cout<<"\t\t1\tDXT compressed image"<<std::endl; 
				return 1;
		}
	}
	std::cout<<"Converting \""<<ifile<<"\" to \""<<ofile<<"\" using lod-files:"<<std::endl;
	std::map<int, std::string>::iterator it;
	for ( it = lod_files.begin(); it!= lod_files.end(); ++it )
	{
		std::cout<<"Lod "<<it->first<<": "<<it->second<<std::endl;
	}
	if(output_format)
		std::cout<<"Output format is compressed."<<std::endl;
	else
		std::cout<<"Output format is uncompressed."<<std::endl;
	
	if(output_format) //проинициализируем ГЛ/ Он нужен для сжатия текстур
	{
		if ( glfwInit() != GL_TRUE ) return false;
		int ret;
		ret = glfwOpenWindow ( 10, 10, 8, 8, 8, 8, 0, 0, GLFW_WINDOW );
		if ( ret != GL_TRUE ) return false;
		GLenum err = glewInit();
		if ( err != GLEW_OK ) THROW ( ( char* ) glewGetErrorString ( err ) );
	}
        
	M3D::M3DT_Header output_header;
	output_header.lod_number = lod_files.size();
	std::vector<M3D::M3DT_Lod> m3dt_lods;
	//Загрузим все LOD-файлы и основной файл
	std::vector<Image_Info> lods;
	//Загрузим основной файл
	Image_Info image_info;
	load_file(ifile, &image_info);
	lods.push_back(image_info);
	
	//Загрузим все LOD-файлы
	for ( it = lod_files.begin(); it!= lod_files.end(); ++it )
	{
		Image_Info tmp_image;
		load_file(it->second, &tmp_image);
		lods.push_back(tmp_image);
	}
	
	//Заполним заголовок данными
	switch (output_format)
	{
		case 0://uncompressed image
		{
			output_header.is_comressed = false;
			output_header.internal_format = (image_info.bpp==3)?GL_RGB:GL_RGBA;
			output_header.type = GL_UNSIGNED_BYTE;
			break;
		}
		case 1://compressed image
		{
			output_header.is_comressed = true;
			output_header.internal_format = (image_info.bpp==3)?GL_COMPRESSED_RGB_S3TC_DXT1_EXT:GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
			output_header.type = GL_UNSIGNED_BYTE;
			break;
		}
	}
	m3dt_file.set_header_( &output_header);
	//Преобразуем данные
	int lod_n=0;
	for(std::vector<Image_Info>::iterator it = lods.begin(); it!=lods.end(); ++it)
	{
		switch (output_format)
		{
			case 0://uncompressed. Сразу можем сформировать выходные данные
			{
				M3D::M3DT_Lod_Data lod_data;
				M3D::M3DT_Lod lod;
				lod_data.data_pointer = it->data;
				
				lod.width = it->width;
				lod.height = it->height;
				lod.line_size = it->line_size;
				lod.data_size = it->height * it->line_size;
				m3dt_lods.push_back(lod);
				lod_data.lod_info = &(m3dt_lods.back());
				m3dt_file.set_lod_data( lod_n, lod_data );
				break;
			}
			case 1://Сперва созжмем данные а потом сформируем выходной массив
			{
				GLuint texid;
				glGenTextures( 1, &texid );
				glBindTexture(GL_TEXTURE_2D, texid);
				glTexImage2D(GL_TEXTURE_2D, 0,
					     (image_info.bpp==3)?
						GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
						GL_COMPRESSED_RGBA_S3TC_DXT5_EXT,
					     it->width, it->height, 0, 
					     (image_info.bpp==3)?GL_RGB:GL_RGBA, 
					     GL_UNSIGNED_BYTE, it->data);
				//Проверим - смогли ли мы сжать текстуру
				int compressed;
				glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_COMPRESSED_ARB, &compressed);
				if(compressed != GL_TRUE)
				{
					THROW("Cannot compress texture");
				}
				
				//Вытащим текстуру
				int32_t compressed_size;
				glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, 
						&compressed_size); 
				char* img = new char[compressed_size]; 
 				glGetCompressedTexImage(GL_TEXTURE_2D, 0, img); 
				
				//Сохраним текстуру
				M3D::M3DT_Lod_Data lod_data;
				M3D::M3DT_Lod lod;
				lod_data.data_pointer = img;
				
				int ls = compressed_size/it->height;
				lod.width = it->width;
				lod.height = it->height;
				lod.line_size = ls;
				lod.data_size = compressed_size;
				m3dt_lods.push_back(lod);
				lod_data.lod_info = &(m3dt_lods.back());
				m3dt_file.set_lod_data( lod_n, lod_data );

				break;
			}
		}
		++lod_n;
	}
	
	m3dt_file.save( ofile );

	/*	
	glBindTexture(GL_TEXTURE_2D, compressed_decal_map); 
	glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGB_ARB, width, height, 
		     0, GL_RGB, GL_UNSIGNED_BYTE, pixels); 
 
	glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_COMPRESSED_ARB, &compressed); 
	*/
	/* if the compression has been successful */ 
/*	if (compressed == GL_TRUE) 
	{ 
		glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, 
					 &internalformat); 
 
		glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_COMPRESSED_IMAGE_SIZE_ARB, 
					 &compressed_size); 
 
		img = (unsigned char *)malloc(compressed_size * sizeof(unsigned char)); 
 
		glGetCompressedTexImageARB(GL_TEXTURE_2D, 0, img); 
 
		SaveTexture(width, height, compressed_size, img, internalFormat, 0); 
	} 
*/       
} 
	catch(M3DError error)
	{
		std::cerr<<"====ERROR===="<<std::endl;
		std::cerr<<error.get_file()<<":"<<error.get_line()<<" in function \""<<error.get_function()<<"\""<<std::endl;
		std::cerr<<error.get_reason()<<std::endl;
		std::cerr<<"============="<<std::endl;
	}       
	return 0;
}
