// Filename: cgShader.cxx
// Created by:  sshodhan (10Jul04)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) 2001 - 2004, Disney Enterprises, Inc.  All rights reserved
//
// All use of this software is subject to the terms of the Panda 3d
// Software license.  You should have received a copy of this license
// along with this source code; you will also find a current copy of
// the license at http://etc.cmu.edu/panda3d/docs/license/ .
//
// To contact the maintainers of this program write to
// panda3d-general@lists.sourceforge.net .
//
////////////////////////////////////////////////////////////////////

#include "pandabase.h"

#ifdef HAVE_CG

#include "cgShader.h"
#include "config_effects.h"
TypeHandle CgShader::_type_handle;
#include <Cg/cg.h>


  
////////////////////////////////////////////////////////////////////
//     Function: CgShader::init_cg
//       Access: Published
//  Description: Create a cgContext. Note that this is completely
//               different from Panda's CgShaderContext and its
//               derived GLCgShaderContext (or Dx9CgShaderContext)
//               This is a Cg API specific context.
////////////////////////////////////////////////////////////////////
bool CgShader::
init_cg() {
  // Create a new context for our Cg Program(s)
  cgContext = cgCreateContext();
  
  if (cgContext == NULL) {
    cerr << "COULD NOT CREATE CG CONTEXT" << "\n" ;
    return false;
  }

  return true;
}

////////////////////////////////////////////////////////////////////
//     Function: CgShader::load_shaders
//       Access: Published
//  Description: this is called after the instantiating a 
//           CgShaderContext because we need the profiles
//           which are initiated by a GL or DX specific
//           CgShaderContext.
////////////////////////////////////////////////////////////////////
bool CgShader::
load_shaders(){
  
  // Load And Compile the shaders from file
  cgVertexProgram = cgCreateProgramFromFile(cgContext, 
    CG_SOURCE, _vertex_shader.c_str(), cgVertexProfile, "main", 0);
  cgFragmentProgram = cgCreateProgramFromFile(cgContext, 
    CG_SOURCE, _fragment_shader.c_str(), cgFragmentProfile, "main", 0);
  
  // Validate Success
  if (cgVertexProgram == NULL) {
    // We Need To Determine What Went Wrong
    CGerror Error = cgGetError();
    printf("VERTEX SHADER ERROR %s",cgGetErrorString(Error));
    return false;
  }

  if (cgFragmentProgram == NULL) {
    // We Need To Determine What Went Wrong
    CGerror Error = cgGetError();
    printf("PIXEL SHADER ERROR %s",cgGetErrorString(Error));
    return false;
  }


  // Now that our programs are loaded we can actually
  // access their handles with cgGetNamedParameter
  // The python programmer specifies parameter names and 
  // handle names when he calles add_param 
  // So lets go through all the handles and associate them with
  // CGparameter objects

  HANDLES::const_iterator param_iter; // Use this to go through all params
    
  // Matrix handles
  // Vertex
  for (param_iter = _vertex_matrix_handles.begin();
    param_iter !=_vertex_matrix_handles.end(); param_iter++) {
    p = cgGetNamedParameter(cgVertexProgram, param_iter->second.c_str());
    CGPARAMETER::value_type vert_param(param_iter->first, p);
    _vertex_matrix_params.insert(vert_param);
  }
  // Fragment
   for (param_iter = _fragment_matrix_handles.begin();
    param_iter != _fragment_matrix_handles.end(); param_iter++) {
    p = cgGetNamedParameter(cgFragmentProgram, param_iter->second.c_str());
    CGPARAMETER::value_type frag_param(param_iter->first, p);
    _fragment_matrix_params.insert(frag_param);

  }


  // Texture handles
  // Vertex
  for (param_iter = _vertex_texture_handles.begin();
    param_iter != _vertex_texture_handles.end(); param_iter++) {
    p = cgGetNamedParameter(cgVertexProgram, param_iter->second.c_str());
    CGPARAMETER::value_type vert_param(param_iter->first, p);
    _vertex_texture_params.insert(vert_param);
  }
  //Fragment
  for (param_iter = _fragment_texture_handles.begin();
    param_iter != _fragment_texture_handles.end(); param_iter++) {
    p = cgGetNamedParameter(cgFragmentProgram, param_iter->second.c_str());
    CGPARAMETER::value_type frag_param(param_iter->first, p);
    _fragment_texture_params.insert(frag_param);
  }

  // 1F handles
  // Vertex
  for (param_iter = _vertex_1f_handles.begin();
    param_iter != _vertex_1f_handles.end(); param_iter++) {
    p = cgGetNamedParameter(cgVertexProgram, param_iter->second.c_str());
    CGPARAMETER::value_type vert_param(param_iter->first, p);
    _vertex_1f_params.insert(vert_param);
  }
  //Fragment
  for (param_iter = _fragment_1f_handles.begin();
    param_iter != _fragment_1f_handles.end(); param_iter++) {
    p = cgGetNamedParameter(cgFragmentProgram, param_iter->second.c_str());
    CGPARAMETER::value_type frag_param(param_iter->first, p);
    _fragment_1f_params.insert(frag_param);
  }


  // 2F handles
  // Vertex
  for (param_iter = _vertex_2f_handles.begin();
    param_iter != _vertex_2f_handles.end(); param_iter++) {
    p = cgGetNamedParameter(cgVertexProgram, param_iter->second.c_str());
    CGPARAMETER::value_type vert_param(param_iter->first, p);
    _vertex_2f_params.insert(vert_param);
  }
  //Fragment
  for (param_iter = _fragment_2f_handles.begin();
    param_iter != _fragment_2f_handles.end(); param_iter++) {
    p = cgGetNamedParameter(cgFragmentProgram, param_iter->second.c_str());
    CGPARAMETER::value_type frag_param(param_iter->first, p);
    _fragment_2f_params.insert(frag_param);
  }

  // 3F handles
  // Vertex
  for (param_iter = _vertex_3f_handles.begin();
    param_iter != _vertex_3f_handles.end(); param_iter++) {
    p = cgGetNamedParameter(cgVertexProgram, param_iter->second.c_str());
    CGPARAMETER::value_type vert_param(param_iter->first, p);
    _vertex_3f_params.insert(vert_param);
  }
  //Fragment
  for (param_iter = _fragment_3f_handles.begin();
    param_iter != _fragment_3f_handles.end(); param_iter++) {
    p = cgGetNamedParameter(cgFragmentProgram, param_iter->second.c_str());
    CGPARAMETER::value_type frag_param(param_iter->first, p);
    _fragment_3f_params.insert(frag_param);
  }

  // 4F handles
  // Vertex
  for (param_iter = _vertex_4f_handles.begin();
    param_iter != _vertex_4f_handles.end(); param_iter++) {
    p = cgGetNamedParameter(cgVertexProgram, param_iter->second.c_str());
    CGPARAMETER::value_type vert_param(param_iter->first, p);
    _vertex_4f_params.insert(vert_param);
  }
  //Fragment
  for (param_iter = _fragment_4f_handles.begin();
    param_iter != _fragment_4f_handles.end(); param_iter++) {
    p = cgGetNamedParameter(cgFragmentProgram, param_iter->second.c_str());
    CGPARAMETER::value_type frag_param(param_iter->first, p);
    _fragment_4f_params.insert(frag_param);
  }

  // 1D handles
  // Vertex
  for (param_iter = _vertex_1d_handles.begin();
    param_iter != _vertex_1d_handles.end(); param_iter++) {
    p = cgGetNamedParameter(cgVertexProgram, param_iter->second.c_str());
    CGPARAMETER::value_type vert_param(param_iter->first, p);
    _vertex_1d_params.insert(vert_param);
  }
  //Fragment
  for (param_iter = _fragment_1d_handles.begin();
    param_iter != _fragment_1d_handles.end(); param_iter++) {
    p = cgGetNamedParameter(cgFragmentProgram, param_iter->second.c_str());
    CGPARAMETER::value_type frag_param(param_iter->first, p);
    _fragment_1d_params.insert(frag_param);
  }


  // 2D handles
  // Vertex
  for (param_iter = _vertex_2d_handles.begin();
    param_iter != _vertex_2d_handles.end(); param_iter++) {
    p = cgGetNamedParameter(cgVertexProgram, param_iter->second.c_str());
    CGPARAMETER::value_type vert_param(param_iter->first, p);
    _vertex_2d_params.insert(vert_param);
  }
  //Fragment
  for (param_iter = _fragment_2d_handles.begin();
    param_iter != _fragment_2d_handles.end(); param_iter++) {
    p = cgGetNamedParameter(cgFragmentProgram, param_iter->second.c_str());
    CGPARAMETER::value_type frag_param(param_iter->first, p);
    _fragment_2d_params.insert(frag_param);
  }

  // 3D handles
  // Vertex
  for (param_iter = _vertex_3d_handles.begin();
    param_iter !=  _vertex_3d_handles.end(); param_iter++) {
    p = cgGetNamedParameter(cgVertexProgram, param_iter->second.c_str());
    CGPARAMETER::value_type vert_param(param_iter->first, p);
    _vertex_3d_params.insert(vert_param);
  }
  //Fragment
  for (param_iter =  _fragment_3d_handles.begin();
    param_iter !=  _fragment_3d_handles.end(); param_iter++) {
    p = cgGetNamedParameter(cgFragmentProgram, param_iter->second.c_str());
    CGPARAMETER::value_type frag_param(param_iter->first, p);
    _fragment_3d_params.insert(frag_param);
  }

  // 4D handles
  // Vertex
  for (param_iter = _vertex_4d_handles.begin();
    param_iter != _vertex_4d_handles.end(); param_iter++) {
    p = cgGetNamedParameter(cgVertexProgram, param_iter->second.c_str());
    CGPARAMETER::value_type vert_param(param_iter->first, p);
    _vertex_4d_params.insert(vert_param);
  }
  //Fragment
  for (param_iter = _fragment_4d_handles.begin();
    param_iter != _fragment_4d_handles.end(); param_iter++) {
    p = cgGetNamedParameter(cgFragmentProgram, param_iter->second.c_str());
    CGPARAMETER::value_type frag_param(param_iter->first, p);
    _fragment_4d_params.insert(frag_param);
  }
  return true;
}



////////////////////////////////////////////////////////////////////
//     Function: CgShader::Constructor
//       Access: Published
//  Description: Use to construct a new CgShader object.
//           Pass filenames for the vertex and fragment programs
////////////////////////////////////////////////////////////////////
CgShader::
CgShader(const string &name, const string &vertex_shader,
  const string &fragment_shader) {
  _name = name;
  _vertex_shader = vertex_shader;
  _fragment_shader = fragment_shader;
  init_cg();
}

  
////////////////////////////////////////////////////////////////////
//     Function: CgShader::Destructor
//       Access: Public
//  Description: We need to clean up the CGcontext
////////////////////////////////////////////////////////////////////
CgShader::
~CgShader() {
  cgDestroyContext(cgContext); 
}





////////////////////////////////////////////////////////////////////
//     Function: CgShader::add_param
//       Access: Published
//  Description: Add a new parameter
//           Specify its name, name in the shader program (handle name)
//           bind type, parameter type and whether its a vertex
//           or fragment parameter
////////////////////////////////////////////////////////////////////
void CgShader:: 
add_param(const string &pname, const string &handle_name,
  Param_Type t, Bind_Type b, bool vert_or_frag) {
  
/* We have only per-frame binding
  if (b == BONCE) {
    _bind_once.push_back(pname);
  } else if (b == BFRAME) {
    _bind_frame.push_back(pname);
  } else if (b == BVERTEX) {
    _bind_vertex.push_back(pname);
  }
*/

  // Add to the <parameter name, handle name> map 
  // Later, once we have loaded the cg programs
  // this handles map is traversed and associations
  // are made with the CGparameter objects
  // using the cgGetNamedParameter function
  if (t == P1F) {
    if (vert_or_frag) {
      HANDLES::value_type param_handle(pname, handle_name);
      _vertex_1f_handles.insert(param_handle);
    } else {
      HANDLES::value_type param_handle(pname, handle_name);
      _fragment_1f_handles.insert(param_handle);
    }
  }else if (t == P2F) {
    if (vert_or_frag) {
      HANDLES::value_type param_handle(pname, handle_name);
      _vertex_2f_handles.insert(param_handle);
    } else {
      HANDLES::value_type param_handle(pname, handle_name);
      _fragment_2f_handles.insert(param_handle);
    }
  }else if (t == P3F) {
    if (vert_or_frag) {
      HANDLES::value_type param_handle(pname, handle_name);
      _vertex_3f_handles.insert(param_handle);
    } else {
      HANDLES::value_type param_handle(pname, handle_name);
      _fragment_3f_handles.insert(param_handle);
    }
  }else if (t == P4F) {
    if (vert_or_frag) {
      HANDLES::value_type param_handle(pname, handle_name);
      _vertex_4f_handles.insert(param_handle);
    } else {
      HANDLES::value_type param_handle(pname, handle_name);
      _fragment_4f_handles.insert(param_handle);
    }
  }else if (t == P1D) {
    if (vert_or_frag) {
      HANDLES::value_type param_handle(pname, handle_name);
      _vertex_1d_handles.insert(param_handle);
    } else {
      HANDLES::value_type param_handle(pname, handle_name);
      _fragment_1d_handles.insert(param_handle);
    }
  }else if (t == P2D) {
    if (vert_or_frag) {
      HANDLES::value_type param_handle(pname, handle_name);
      _vertex_2d_handles.insert(param_handle);
    } else {
      HANDLES::value_type param_handle(pname, handle_name);
      _fragment_2d_handles.insert(param_handle);
    }
  }else if (t == P3D) {
    if (vert_or_frag) {
      HANDLES::value_type param_handle(pname, handle_name);
      _vertex_3d_handles.insert(param_handle);
    } else {
      HANDLES::value_type param_handle(pname, handle_name);
      _fragment_3d_handles.insert(param_handle);
    }
  }else if (t == P4D) {
    if (vert_or_frag) {
      HANDLES::value_type param_handle(pname, handle_name);
      _vertex_4d_handles.insert(param_handle);
    } else {
      HANDLES::value_type param_handle(pname, handle_name);
      _fragment_4d_handles.insert(param_handle);
    }
  } else if (t == PTEXTURE) {
    if (vert_or_frag) {
      HANDLES::value_type param_handle(pname, handle_name);
      _vertex_texture_handles.insert(param_handle);
    } else {
      HANDLES::value_type param_handle(pname, handle_name);
      _fragment_texture_handles.insert(param_handle);
    }    
  } else if (t == PMATRIX) {
    if (vert_or_frag) {
      HANDLES::value_type param_handle(pname, handle_name);
      _vertex_matrix_handles.insert(param_handle);
    } else {
      HANDLES::value_type param_handle(pname, handle_name);
      _fragment_matrix_handles.insert(param_handle);
    }    
  }
  
}

#endif  // HAVE_CG

