#pragma once
#include <string>
#include <fstream>
#include <GL/glew.h>
#include "ShaderError.h"
#include <SOIL2/SOIL2.h>

#define PI 3.14159

class utils{
	inline static std::string readShaderSource(const char *filePath){
        std::string content;
        std::ifstream fileStream(filePath, std::ios::in);
        std::string line = "";
        if(fileStream.fail()){
            std::cout<<"Could not open shader "<<filePath<<"!"<<std::endl;
            throw;
        }
        while (fileStream.good()) {
            getline(fileStream, line);
            content.append(line + "\n");
        }
        fileStream.close();
        return content;
    }
    inline static void CompileShader(GLuint shader,std::string type){
        GLint compiled;
        glCompileShader(shader);
        ErrorCheck::checkOpenGLError();
        glGetShaderiv(shader,GL_COMPILE_STATUS,&compiled);
        if(compiled!=1){
            std::cout<<type<<" Compilation Failed!"<<std::endl;
            ErrorCheck::printShaderLog(shader);
            throw;
        }
    }
    inline static void CreateShader(GLuint type,std::string shaderName,const char*source,GLuint vfProgram){
        if(source!=nullptr){
            GLuint vShader=glCreateShader(type);
            std::string shaderFile=readShaderSource(source);
            const char*vertShaderSrc=shaderFile.c_str();
            glShaderSource(vShader,1,&vertShaderSrc,NULL);
            CompileShader(vShader,shaderName);
            glAttachShader(vfProgram,vShader);
        }
    };
public:
    inline static GLuint createShaderProgram(const char*vertex,const char*fragment){
        return createShaderProgram(vertex,nullptr,nullptr,nullptr,fragment);
    }
    inline static GLuint createShaderProgram(const char*vertex,const char*geometry,const char*fragment){
        return createShaderProgram(vertex,nullptr,nullptr,geometry,fragment);
    }
    inline static GLuint createShaderProgram(const char*vertex,const char*tesselationCS,const char*tesselationES,const char*fragment){
        return createShaderProgram(vertex,tesselationCS,tesselationES,nullptr,fragment);
    }
    inline static GLuint createShaderProgram(const char*vertex,const char*tesselationCS,const char*tesselationES,const char*geometry,const char*fragment){
        
        GLuint vfProgram=glCreateProgram();

        CreateShader(GL_VERTEX_SHADER,"Vertex",vertex,vfProgram);
        CreateShader(GL_FRAGMENT_SHADER,"Fragment",fragment,vfProgram);
        CreateShader(GL_TESS_CONTROL_SHADER,"Tesselation Control",tesselationCS,vfProgram);
        CreateShader(GL_TESS_EVALUATION_SHADER,"Tesselation Evaluation",tesselationES,vfProgram);
        CreateShader(GL_GEOMETRY_SHADER,"Geometry",geometry,vfProgram);

        GLint linked;
        glLinkProgram(vfProgram);
        ErrorCheck::checkOpenGLError();
        glGetProgramiv(vfProgram,GL_LINK_STATUS,&linked);
        if(linked!=1){
            std::cout<<"Linking Failed!"<<std::endl;
            ErrorCheck::printProgramLog(vfProgram);
        }
        return vfProgram;
    }
    inline static float degToRad(float deg){
	    return deg*(PI/180);
    }

    inline static float radToDeg(float rad){
	    return rad*57.2957795130823208767;
    }

    inline static GLuint loadTexture(const char*file){
        GLuint textureID;
        textureID=SOIL_load_OGL_texture(file,SOIL_LOAD_AUTO,SOIL_CREATE_NEW_ID,SOIL_FLAG_INVERT_Y);
        if(!textureID){
            std::cout<<"Could not load texture file "<<file<<std::endl;
            throw;
        }
        return textureID;
    }
};