# pragma once

#include <vector>
#include <string>
#include <iostream>
#include <fstream>
//#include <glut.h>
#include <windows.h>


#undef  ASSERT
#ifdef  NDEBUG
	#define ASSERT(_expression) ((void)0)
#else
	#define ASSERT(_expression) if(!(_expression))	throw MGML_ERROR::Assertion_Failed(__FILE__,__LINE__)
#endif


namespace MGML_ERROR
{

 static std::ofstream err_stream("Err.txt");

 // Да, возможно это не красиво, но зато более безопасно,
 // чем ф-я с произвольным количеством аргументов

 template <class A>
 void Print_Error(A a) { err_stream << a;}

 template <class A,class B>
 void Print_Error(A a,B b) { err_stream << a << b;}

 template <class A,class B,class C>
 void Print_Error(A a,B b,C c) { err_stream << a << b << c;}

 template <class A,class B,class C,class D>
 void Print_Error(A a,B b,C c,D d) { err_stream << a << b << c << d;}

 template <class A,class B,class C,class D,class E>
 void Print_Error(A a,B b,C c,D d,E e) { err_stream << a << b << c << d << e;}

 template <class A,class B,class C,class D,class E,class F>
 void Print_Error(A a,B b,C c,D d,E e,F f) { err_stream << a << b << c << d << e << f;}

 template <class A,class B,class C,class D,class E,class F,class G>
 void Print_Error(A a,B b,C c,D d,E e,F f,G g) { err_stream << a << b << c << d << e << f << g;}

 template <class A,class B,class C,class D,class E,class F,class G,class H>
 void Print_Error(A a,B b,C c,D d,E e,F f,G g,H h) { err_stream << a << b << c << d << e << f << g << h;}

 template <class A,class B,class C,class D,class E,class F,class G,class H, class J>
 void Print_Error(A a,B b,C c,D d,E e,F f,G g,H h,J j) { err_stream << a << b << c << d << e << f << g << h << j;}

 template <class A,class B,class C,class D,class E,class F,class G,class H, class J,class K>
 void Print_Error(A a,B b,C c,D d,E e,F f,G g,H h,J j,K k) { err_stream << a << b << c << d << e << f << g << h << j << k;}



 static void ALERT(const std::string& err_msg)
 {
	 err_stream.flush();
#ifdef UNICODE
	 MessageBox(NULL,(LPCWSTR)err_msg.c_str(),(LPCWSTR)"ALERT!",MB_ICONERROR);
#else
	 MessageBox(NULL,(LPCSTR)err_msg.c_str(),(LPCSTR)"ALERT!",MB_ICONERROR);
#endif
 }

 static void ALERT(const std::string& err_msg1,const std::string& err_msg2)
 {
	 err_stream.flush();
#ifdef UNICODE
	 MessageBox(NULL,(LPCWSTR)err_msg2.c_str(),(LPCWSTR)err_msg1.c_str(),MB_ICONERROR);
#else
	 MessageBox(NULL,(LPCSTR)err_msg2.c_str(),(LPCSTR)err_msg1.c_str(),MB_ICONERROR);
#endif
 }

 static std::string ToString(int i)
 {
	char buff[128];
	_itoa_s(i,buff,10);
	return std::string(buff);
 }


 static std::string ToString(unsigned int i)
 {
	char buff[128];
	_itoa_s(i,buff,10);
	return std::string(buff);
 }



 static std::string NotEnoughMemory(bool videoMem = false)
 {
  int choice = (rand()*GetTickCount())%100;
  if(videoMem)
  {
      if(choice > 50)
        return std::string("Required more minerals!");
      else
        return std::string("Required more vespren gas!");
  }
  else
  {
    if(choice > 66)
      return std::string("Additional supply deports required!");
    else if(choice > 33)
      return std::string("You must construct additional pylons!");
    else
      return std::string("Spawn more overlords!");
  }
 }

 /*static void show_percentage(const char* info,float percent)
 {
   char Title[128];
   char buff[128];
   strcpy(Title,info);
   sprintf(buff, "%f",percent);
   strcat(Title,buff);
   glutSetWindowTitle(Title);
 }

 static void show_percentage(const char* info,int percent)
 {
   char Title[128];
   char buff[128];
   strcpy(Title,info);
   sprintf(buff, "%d",percent);
   strcat(Title,buff);
   glutSetWindowTitle(Title);
 }

 static void show_numbers(int * arr,int N)
 {
	 char Title[128];
	 char buff[128];

	 strcpy(buff,"");
	 strcpy(Title,"");
	 for(int i=0;i<N;i++)
	 {
		 sprintf(buff, "%d, ",arr[i]);
		 strcat(Title,buff);
	 }
	 glutSetWindowTitle(Title);
 }

 static void show_numbers(int a)
 {
	 int arr[1] = {a};
	 show_numbers(arr,1);
 }


 static void show_numbers(int a,int b)
 {
	 int arr[2] = {a,b};
	 show_numbers(arr,2);
 }

 static void show_numbers(int a,int b,int c)
 {
	 int arr[3] = {a,b,c};
	 show_numbers(arr,3);
 }

 static void show_numbers(int a,int b,int c,int d)
 {
	 int arr[4] = {a,b,c,d};
	 show_numbers(arr,4);
 }*/

///////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////


 class Error: public std::runtime_error            // базовый класс предназначеный для try - catch механизма.
 {
 public:
	 Error() : std::runtime_error("RUN-TIME ERROR!") {}
	 Error(const std::string& err): std::runtime_error(err)
	 {
		 ALERT("Error: ",err);
		 err_msg = err;
	 }
	 virtual ~Error(){};

	 const std::string& GetMsg() const 
		{return err_msg;}

 protected:
	 std::string err_msg;
 };


 class Assertion_Failed: public Error
 {
	public:

		Assertion_Failed(const std::string& file, int line)
		{
#pragma warning(disable:4996) // Заткнись
			char tmp[64];
			itoa(line,tmp,10);
			std::string tstr(tmp);
			ALERT("Assertion Failed", file + " Line: " + tstr);
			exit(-1);
		}
		~Assertion_Failed(){}
 };



 class File_Error: public Error
 {
 public:
   File_Error(){}
   File_Error(const std::string& err) 
   { 
	   ALERT("File_Error: ",err);
	   err_msg = err;
   }
   virtual ~File_Error(){};

 };


 class Image_File_Error: public File_Error
 {
 public:
   Image_File_Error(){}
   Image_File_Error(const std::string& err)
   {
	   ALERT("Image_File_Error: ",err);
	   err_msg = err;
   }
   virtual ~Image_File_Error(){};
 };


 class Hal_Error: public Error
 {
public:
   Hal_Error(){}
   Hal_Error(const std::string& err) 
   { 
	   ALERT("Hal_Error: ",err);
	   err_msg = err;
   }
   virtual ~Hal_Error(){};
 };

 class OpenGL_Error: public Hal_Error
 {
 public:
   
   OpenGL_Error(const std::string& err) 
   { 
	   ALERT("OpenGL_Error: ",err);
	   err_msg = err;
   }

 /*  OpenGL_Error(GLenum error)
   {
	  switch(error)
	  {
		case GL_INVALID_ENUM:
		  ALERT("OpenGL_Error: ","GL_INVALID_ENUM: mode was not an accepted value.\n");
		  break;
		case GL_INVALID_VALUE:
		  ALERT("OpenGL_Error: ","INVALID VALUE.\n");
		  break;
		case GL_INVALID_OPERATION:
		  ALERT("OpenGL_Error: ","INVALID OPERATION.\n");
		 break;
		case GL_STACK_OVERFLOW:
		  ALERT("OpenGL_Error: ","STACK OVERRFLOW! \n");
		 break;
		case GL_STACK_UNDERFLOW:
		  ALERT("OpenGL_Error: ","STACK UNDERFLOW! \n");
		 break;
		case GL_OUT_OF_MEMORY:
          ALERT("OpenGL_Error: ","OUT OF MEMORY! \n");
		 break;
		case GL_NO_ERROR:
		 ALERT("OpenGL_Error: ","There is no OpenGL error \n");
		 break;
		default:
		 ALERT("OpenGL_Error: ","Unexpected OpenGL error! \n");
		 break;
	  };
   }*/
   virtual ~OpenGL_Error(){};
 };



 class L3DS_Error: public File_Error
 {
 public:
   L3DS_Error(){}
   L3DS_Error(const std::string& err) 
   {
	   ALERT("L3DS_Error: ",err);
	   err_msg = err;
   }
   virtual ~L3DS_Error(){};
 };


 class Memory_Error: public Error, std::bad_alloc
 {
 public:
  using Error::what;

	Memory_Error(): std::bad_alloc("Memory_Error: ") {}
	Memory_Error(const std::string& err): std::bad_alloc(err.c_str()) 
	{
		ALERT("Memory_Error: ",err);
		err_msg = err;
	}
  
  virtual ~Memory_Error(){};

 };



 class Out_Of_Memory: public Memory_Error
 {
 public:

   Out_Of_Memory(): Memory_Error("Out_Of_Memory: ") {}
	 Out_Of_Memory(const std::string& err)
	 {
		ALERT("You must construct additional pylons: ", err);
		err_msg = "You must construct additional pylons: " + err;
	 } 
  
   virtual ~Out_Of_Memory(){};
 };


 class Out_Of_Range: public Error,std::out_of_range
 {
 public:
	 Out_Of_Range() : std::out_of_range("std::out_of_range") {}
	 Out_Of_Range(const std::string& err) : std::out_of_range(err.c_str()) 
	 {
		 ALERT("Out_Of_Range: ",err);
		 err_msg = err;
	 }
	 virtual ~Out_Of_Range(){};

 };

 class Zero_Division: public Error
 {
 public:
   Zero_Division(){}
   Zero_Division(const std::string&err) 
   {
	   //ALERT("Zero_Division: ",err);
	   err_msg = err;
   }
   virtual ~Zero_Division(){};

 };





};

