#include <windows.h> 
#include <stdio.h>
#include <conio.h>
#include <tchar.h>
#include "cg.hpp"

#define SHADERDEBUG
#define USE_SHADERS

VertexShader::VertexShader(char *filename)
{
    int namelength = strlen(filename);
    name = new char[namelength+1];
    strncpy(name, filename, namelength);
    name[namelength] = 0;


#ifdef SHADERDEBUG
    dmsMsg("creating new vertex shader from file %s\n", name);
#endif

    program = cgCreateProgramFromFile(cg.getContext(), CG_SOURCE, filename, cg.getVertexProfile(), "main", NULL);
    if (!cgIsProgramCompiled(program))
        cgCompileProgram(program);

   if(program==NULL){
#ifdef SHADERDEBUG
    dmsMsg("failed to compile program file %s\n", name);
#endif
	}

	cgGLEnableProfile(cg.getVertexProfile());
	cgGLLoadProgram(program);
}

VertexShader::VertexShader(CGcontext &cont, CGprofile &vertprofile, char *filename)
{
    int namelength = strlen(filename);
    name = new char[namelength+1];
    strncpy(name, filename, namelength);
    name[namelength] = 0;

#ifdef SHADERDEBUG
    dmsMsg("creating new vertex shader from file %s\n", name);
#endif

    program = cgCreateProgramFromFile(cont, CG_SOURCE, filename, vertprofile, "main", NULL);
    if (!cgIsProgramCompiled(program))
        cgCompileProgram(program);

   if(program==NULL){
#ifdef SHADERDEBUG
    dmsMsg("failed to compile program file %s\n", name);
#endif
	}

    cgGLEnableProfile(cg.getVertexProfile());
    cgGLLoadProgram(program);
}
VertexShader::~VertexShader()
{
    if (name)
        delete [] name;
}

void VertexShader::setConstants()
{
}

FragmentShader::FragmentShader(char *filename)
{
    int namelength = strlen(filename);
    name = new char[namelength+1];
    strncpy(name, filename, namelength);
    name[namelength] = 0;

#ifdef SHADERDEBUG
    dmsMsg("creating new fragment shader from file %s\n", name);
#endif

    program = cgCreateProgramFromFile(cg.getContext(), CG_SOURCE, filename, cg.getFragmentProfile(), NULL, NULL);
    if (!cgIsProgramCompiled(program))
        cgCompileProgram(program);
   
	if(program==NULL){
#ifdef SHADERDEBUG
    dmsMsg("failed to compile program file %s\n", name);
#endif
	}

    cgGLEnableProfile(cg.getFragmentProfile());
    cgGLLoadProgram(program);
}
FragmentShader::FragmentShader(CGcontext &cont, CGprofile &fragprofile, char *filename)
{
    int namelength = strlen(filename);
    name = new char[namelength+1];
    strncpy(name, filename, namelength);
    name[namelength] = 0;

#ifdef SHADERDEBUG
    dmsMsg("creating new fragment shader from file %s\n", name);
#endif

    program = cgCreateProgramFromFile(cont, CG_SOURCE, filename, fragprofile, NULL, NULL);
    if (!cgIsProgramCompiled(program))
        cgCompileProgram(program);

   if(program==NULL){
#ifdef SHADERDEBUG
    dmsMsg("failed to compile program file %s\n", name);
#endif
	}

    cgGLEnableProfile(cg.getFragmentProfile());
    cgGLLoadProgram(program);
}
void FragmentShader::setConstants()
{
}
FragmentShader::~FragmentShader()
{
    if (name)
        delete [] name;
}

char *VertexShader::getName()
{
    return name;
}

void VertexShader::bind()
{
    cgGLBindProgram(program);
}
VertexShader *CG::getVertexShader(char *name)
{

#ifdef SHADERDEBUG
    dmsMsg("getVertexShader()\n");
#endif

    vertexshaders->goToStart();

#ifdef SHADERDEBUG
    dmsMsg("shadereita = %d\n", vertexshaders->getNodeCount());
#endif

    do
    {
        VertexShader *s2 = (VertexShader *)vertexshaders->getCurrent();

#ifdef SHADERDEBUG
        dmsMsg("s2->name = %s\n", s2->getName());
#endif

        if (!strcmp(name, s2->getName()))
            return s2;

    } while (vertexshaders->goForward());

    char message[255];
    sprintf(message, "cannot find vertex shader %s\0", name);
	MessageBox(dmsGetHWND(), message, "Fatal error", MB_OK);
    exit(1);

}
void FragmentShader::bind()
{
    cgGLBindProgram(program);
}
char *FragmentShader::getName()
{
    return name;
}
FragmentShader *CG::getFragmentShader(char *name)
{
#ifdef SHADERDEBUG
   dmsMsg("getFragmentShader()\n");
#endif
    fragmentshaders->goToStart();

#ifdef SHADERDEBUG
    dmsMsg("shadereita = %d\n", fragmentshaders->getNodeCount());
#endif
    do
    {
        FragmentShader *s2 = (FragmentShader *)fragmentshaders->getCurrent();

#ifdef SHADERDEBUG
        dmsMsg("s2->name = %s\n", s2->getName());
#endif
        if (!strcmp(name, s2->getName()))
            return s2;

    } while (fragmentshaders->goForward());
    char message[255];
    sprintf(message, "cannot find fragment shader %s\0", name);
	MessageBox(dmsGetHWND(), message, "Fatal error", MB_OK);
    exit(1);
}

void CG::loadAllShaders()
{
    //vaihdetaan hakemistoa

   WIN32_FIND_DATA FindFileData;
   TCHAR Buffer[MAX_PATH];
   TCHAR Buffer2[MAX_PATH];
   DWORD dwRet;
   DWORD dwRet2;

   dwRet = GetCurrentDirectory(MAX_PATH, Buffer);

   if( dwRet == 0 )
   {
#ifdef SHADERDEBUG
      dmsMsg("GetCurrentDirectory failed\n");
#endif
      return;
   }
   if(dwRet > MAX_PATH)
   {
#ifdef SHADERDEBUG
      dmsMsg("GetCurrentDirectory failed\n");
#endif
      return;
   }
   if( !SetCurrentDirectory("shaders"))
   {
#ifdef SHADERDEBUG
      dmsMsg("SetCurrentDirectory failed\n");
#endif
      return;
   }
   //lataa tässä kaikki

   dwRet2 = GetCurrentDirectory(MAX_PATH, Buffer2);

   HANDLE hFind = INVALID_HANDLE_VALUE;
   char DirSpec[MAX_PATH + 1];  // directory specification
   DWORD dwError;

   strncpy (DirSpec, Buffer2, strlen(Buffer2)+1);
   strncat (DirSpec, "\\*", 3);

   hFind = FindFirstFile(DirSpec, &FindFileData);

   if (hFind == INVALID_HANDLE_VALUE) 
   {
      return;
   } 
   else 
   {
       //loopataan kaikki tiedostot läpi
       //v-alkuiset ovat vertexshadereita, f-alkuiset fragmentshadereita
       do
       {
           if (FindFileData.cFileName[0] == 'f')
           {
#ifdef SHADERDEBUG
               dmsMsg("fragment shader %s\n", FindFileData.cFileName);
#endif
               FragmentShader *f = new FragmentShader(context, fragmentProfile, FindFileData.cFileName);
               fragmentshaders->add(f);
           }
           if (FindFileData.cFileName[0] == 'v')
           {
#ifdef SHADERDEBUG
               dmsMsg("vertex shader %s\n", FindFileData.cFileName);
#endif
               VertexShader *v = new VertexShader(context, vertexProfile, FindFileData.cFileName);
               vertexshaders->add(v);
           }

       } while (FindNextFile(hFind, &FindFileData) != 0);
    
      dwError = GetLastError();
      FindClose(hFind);
      if (dwError != ERROR_NO_MORE_FILES) 
      {
#ifdef SHADERDEBUG
         dmsMsg("FindNextFile error. Error is %u\n", dwError);
#endif
         return;
      }
   }


   if( !SetCurrentDirectory(Buffer) )
   {
#ifdef SHADERDEBUG
      dmsMsg(TEXT("SetCurrentDirectory failed (%d)\n"), GetLastError());
#endif
      return;
   }

   cgGLDisableProfile(vertexProfile);
   cgGLDisableProfile(fragmentProfile);
   dmsMsg("loadAllShaders() finished\n");
}

Lista2 *CG::getVertexShaderList()
{
    return vertexshaders;
}
Lista2 *CG::getFragmentShaderList()
{
    return fragmentshaders;
}


CGcontext CG::getContext()
{
    return context;
}
CGprofile CG::getVertexProfile()
{
    return vertexProfile;
}
CGprofile CG::getFragmentProfile()
{
    return fragmentProfile;

}


void CG::init()
{
#ifdef USE_SHADERS
    int i=0;
    dmsMsg("CG::init()\n");
    vertexshaders = new Lista2();
    fragmentshaders = new Lista2();

    context = cgCreateContext();

    if (cgGLIsProfileSupported(CG_PROFILE_ARBVP1))
        vertexProfile = CG_PROFILE_ARBVP1;
    else {
        // try VP30
        if (cgGLIsProfileSupported(CG_PROFILE_VP30))
            vertexProfile = CG_PROFILE_VP30;
        else {
    		MessageBox(dmsGetHWND(), "Vertex shader 2.0 or better required", "Fatal error", MB_OK);
            exit(1);
        }
    }

    if (cgGLIsProfileSupported(CG_PROFILE_ARBFP1))
        fragmentProfile = CG_PROFILE_ARBFP1;
    else {
        // try FP30
        if (cgGLIsProfileSupported(CG_PROFILE_FP30))
            fragmentProfile = CG_PROFILE_FP30;
        else {
    		MessageBox(dmsGetHWND(), "Pixel shader 2.0 or better required", "Fatal error", MB_OK);
            exit(1);
        }
    }

	cgGLSetOptimalOptions(vertexProfile);
	cgGLSetOptimalOptions(fragmentProfile);


	// Initialize shaderholder
	this->shaders = new ShaderHolder();
	this->shaders->init();

    dmsMsg("CG::init() loading shaders\n");
/*
    loadAllShaders();
    vertexshaders->goToStart();
    fragmentshaders->goToStart();

    int vertexshadercount = vertexshaders->getNodeCount();
    int fragmentshadercount = fragmentshaders->getNodeCount();

    dmsMsg("Loaded %d vertex shaders\n", vertexshadercount);
    for (i=0;i<vertexshadercount;i++)
    {
        dmsMsg("    %s\n", ((VertexShader *)vertexshaders->getCurrent())->getName());
        vertexshaders->goForward();
    }
    dmsMsg("Loaded %d fragment shaders\n", fragmentshadercount);
    for (i=0;i<fragmentshadercount;i++)
    {
        dmsMsg("    %s\n", ((VertexShader *)fragmentshaders->getCurrent())->getName());
        fragmentshaders->goForward();
    }
*/
    dmsMsg("CG::init() complete\n");
#endif
}
void CG::enableVertexShader()
{
#ifdef USE_SHADERS
    cgGLEnableProfile(vertexProfile);
#endif
}
void CG::enableFragmentShader()
{
#ifdef USE_SHADERS
    cgGLEnableProfile(fragmentProfile);
#endif
}
void CG::disableVertexShader()
{
#ifdef USE_SHADERS
    cgGLDisableProfile(vertexProfile);
#endif
}
void CG::disableFragmentShader()
{
#ifdef USE_SHADERS
    cgGLDisableProfile(fragmentProfile);
#endif
}

void CG::deinit()
{
#ifdef USE_SHADERS
	if(shaders)  {
		shaders->deinit();
		delete shaders;
	}
    delete vertexshaders;
    delete fragmentshaders;
#endif
}
