/*=========================================================================

  Program:   Visualization Toolkit

  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

=========================================================================*/
/**
 * @class   vtkDirect3DShaderProgram
 * @brief   a glsl shader program
 *
 * This class contains the vertex, fragment, geometry shaders that combine to make a shader program
*/

#ifndef vtkDirect3DShaderProgram_h
#define vtkDirect3DShaderProgram_h

#include "vtkRenderingDirect3DModule.h" // for export macro
#include "vtkObject.h"

#include <windows.h>  // used for ivars
#include <d3d11_1.h>  // used for ivars

#include <string> // For member variables.
#include <map>    // For member variables.

class vtkDirect3DShader;
class vtkMatrix3x3;
class vtkMatrix4x4;
//class VertexArrayObject;
class vtkWindow;
class vtkDirect3DRenderWindow;

/**
 * @brief The ShaderProgram uses one or more Shader objects.
 *
 * This class creates a Vertex or Fragment shader, that can be attached to a
 * ShaderProgram in order to render geometry etc.
 */

class VTKRENDERINGDIRECT3D_EXPORT vtkDirect3DShaderProgram : public vtkObject
{
public:
  static vtkDirect3DShaderProgram *New();
  vtkTypeMacro(vtkDirect3DShaderProgram, vtkObject);
  void PrintSelf(ostream& os, vtkIndent indent);

  //@{
  /**
   * Get the vertex shader for this program
   */
  vtkGetObjectMacro(VertexShader, vtkDirect3DShader);
  void SetVertexShader(vtkDirect3DShader*);
  //@}

  //@{
  /**
   * Get the fragment shader for this program
   */
  vtkGetObjectMacro(FragmentShader, vtkDirect3DShader);
  void SetFragmentShader(vtkDirect3DShader*);
  //@}

  //@{
  /**
   * Get the geometry shader for this program
   */
  vtkGetObjectMacro(GeometryShader, vtkDirect3DShader);
  void SetGeometryShader(vtkDirect3DShader*);
  //@}

  //@{
  /**
   * Set/Get flag for if this program is compiled
   */
  vtkGetMacro(Compiled, bool);
  vtkSetMacro(Compiled, bool);
  vtkBooleanMacro(Compiled, bool);
  //@}

  /**
   * Set/Get the md5 hash of this program
   */
  std::string GetMD5Hash() const { return this->MD5Hash; }
  void SetMD5Hash(const std::string &hash) { this->MD5Hash = hash; }


  /** Options for attribute normalization. */
  enum NormalizeOption {
    /// The values range across the limits of the numeric type.
    /// This option instructs the rendering engine to normalize them to
    /// the range [0.0, 1.0] for unsigned types, and [-1.0, 1.0] for signed
    /// types.
    /// For example, unsigned char values will be mapped so that 0 = 0.0,
    /// and 255 = 1.0.
    /// The resulting floating point numbers will be passed into
    /// the shader program.
    Normalize,
    /// The values should be used as-is. Do not perform any normalization.
    NoNormalize
  };


  /**
   * Check if the program is currently bound, or not.
   * @return True if the program is bound, false otherwise.
   */
  bool isBound() const { return this->Bound; }

  /**
   * release any graphics resources this class is using.
   */
  void ReleaseGraphicsResources(vtkWindow *win);

  /** Get the error message (empty if none) for the shader program. */
  std::string GetError() const { return Error; }

  // How many outputs does this program produce
  // only valid for Direct3D 3.2 or later
  vtkSetMacro(NumberOfOutputs,unsigned int);

  /**
   * perform in place string substitutions, indicate if a substitution was done
   * this is useful for building up shader strings which typically involve
   * lots of string substitutions. Return true if a substitution was done.
   */
  static bool Substitute(
    std::string &source,
    const std::string &search,
    const std::string &replace,
    bool all = true);

  // maps of std::string are super slow when calling find
  // with a string literal or const char * as find
  // forces construction/copy/destruction of a
  // std::sting copy of the const char *
  // In spite of the doubters this can really be a
  // huge CPU hog.
  struct cmp_str
  {
     bool operator()(const char *a, const char *b) const
     {
        return strcmp(a, b) < 0;
     }
  };

  ID3DBlob* GetVertexShaderBlob() {
    return this->VertexShaderBlob; }

  //@{
  /**
   * Get/Set the context. This does not increase the reference count of the
   * context to avoid reference loops.
   */
  void SetContext(vtkDirect3DRenderWindow *val) {this->Context = val; }
  vtkDirect3DRenderWindow *GetContext() {
      return this->Context; }
  //@}

  void BindConstantBuffers();

protected:
  vtkDirect3DShaderProgram();
  ~vtkDirect3DShaderProgram();

  /***************************************************************
   * The following functions are only for use by the shader cache
   * which is why they are protected and that class is a friend
   * you need to use the shader cache to compile/link/bind your shader
   * do not try to do it yourself as it will screw up the cache
   ***************************************************************/
  friend class vtkDirect3DShaderCache;

  ID3D11VertexShader* VertexShaderHandle;
  ID3DBlob* VertexShaderBlob;
  ID3D11PixelShader*  FragmentShaderHandle;
  ID3DBlob* FragmentShaderBlob;

  vtkDirect3DShader* VertexShader;
  vtkDirect3DShader*  FragmentShader;
  vtkDirect3DShader*  GeometryShader;

  /**
   * Attach the supplied shader to this program.
   * @note A maximum of one Vertex shader and one Fragment shader can be
   * attached to a shader program.
   * @return true on success.
   */
  bool AttachShader(const vtkDirect3DShader *shader);

  /** Detach the supplied shader from this program.
   * @note A maximum of one Vertex shader and one Fragment shader can be
   * attached to a shader program.
   * @return true on success.
   */
  bool DetachShader(const vtkDirect3DShader *shader);

  /**
   * Compile this shader program and attached shaders
   */
  virtual int CompileShader();

  /**
   * Attempt to link the shader program.
   * @return false on failure. Query error to get the reason.
   * @note The shaders attached to the program must have been compiled.
   */
  bool Link();

  /**
   * Bind the program in order to use it. If the program has not been linked
   * then link() will be called.
   */
  bool Bind();

  /** Releases the shader program from the current context. */
  void Release();

  /************* end **************************************/


  // hash of the shader program
  std::string MD5Hash;

  bool SetAttributeArrayInternal(const char *name, void *buffer,
                                 int type, int tupleSize,
                                 NormalizeOption normalize);
  bool Linked;
  bool Bound;
  bool Compiled;
  vtkDirect3DRenderWindow * Context;

  // for glsl 1.5 or later, how many outputs
  // does this shader create
  // they will be bound in order to
  // fragOutput0 fragOutput1 etc...
  unsigned int NumberOfOutputs;

  std::string Error;

  // since we are using const char * arrays we have to
  // free our memory :-)
  void ClearMaps();
  std::map<const char *, int, cmp_str> AttributeLocs;
  std::map<const char *, int, cmp_str> UniformLocs;

//  friend class VertexArrayObject;

private:
  int FindAttributeArray(const char *name);
  int FindUniform(const char *name);

  vtkDirect3DShaderProgram(const vtkDirect3DShaderProgram&) VTK_DELETE_FUNCTION;
  void operator=(const vtkDirect3DShaderProgram&) VTK_DELETE_FUNCTION;

};


#endif
