// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef GPU_COMMAND_BUFFER_SERVICE_SHADER_MANAGER_H_
#define GPU_COMMAND_BUFFER_SERVICE_SHADER_MANAGER_H_

#include <string>
#include "base/basictypes.h"
#include "base/containers/hash_tables.h"
#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "gpu/command_buffer/service/gl_utils.h"
#include "gpu/command_buffer/service/shader_translator.h"
#include "gpu/gpu_export.h"

namespace gpu {
namespace gles2 {

// This is used to keep the source code for a shader. This is because in order
// to emluate GLES2 the shaders will have to be re-written before passed to
// the underlying OpenGL. But, when the user calls glGetShaderSource they
// should get the source they passed in, not the re-written source.
class GPU_EXPORT Shader : public base::RefCounted<Shader> {
 public:
  enum TranslatedShaderSourceType {
    kANGLE,
    kGL,  // GL or GLES
  };

  enum ShaderState {
    kShaderStateWaiting,
    kShaderStateCompileRequested,
    kShaderStateCompiled, // Signifies compile happened, not valid compile.
  };

  static const int kUndefinedShaderVersion = -1;

  void RequestCompile(scoped_refptr<ShaderTranslatorInterface> translator,
                      TranslatedShaderSourceType type);

  void DoCompile();

  ShaderState shader_state() const {
    return shader_state_;
  }

  GLuint service_id() const {
    return marked_for_deletion_ ? 0 : service_id_;
  }

  GLenum shader_type() const {
    return shader_type_;
  }

  int shader_version() const {
    return shader_version_;
  }

  const std::string& source() const {
    return source_;
  }

  void set_source(const std::string& source) {
    source_ = source;
  }

  const std::string& translated_source() const {
    return translated_source_;
  }

  std::string last_compiled_source() const {
    return last_compiled_source_;
  }

  std::string last_compiled_signature() const {
    if (translator_.get()) {
      return last_compiled_source_ +
             translator_->GetStringForOptionsThatWouldAffectCompilation();
    }
    return last_compiled_source_;
  }

  const sh::Attribute* GetAttribInfo(const std::string& name) const;
  const sh::Uniform* GetUniformInfo(const std::string& name) const;
  const sh::Varying* GetVaryingInfo(const std::string& name) const;

  // If the original_name is not found, return NULL.
  const std::string* GetAttribMappedName(
      const std::string& original_name) const;

  // If the hashed_name is not found, return NULL.
  const std::string* GetOriginalNameFromHashedName(
      const std::string& hashed_name) const;

  const std::string& log_info() const {
    return log_info_;
  }

  bool valid() const {
    return shader_state_ == kShaderStateCompiled && valid_;
  }

  bool IsDeleted() const {
    return marked_for_deletion_;
  }

  bool InUse() const {
    DCHECK_GE(use_count_, 0);
    return use_count_ != 0;
  }

  // Used by program cache.
  const AttributeMap& attrib_map() const {
    return attrib_map_;
  }

  // Used by program cache.
  const UniformMap& uniform_map() const {
    return uniform_map_;
  }

  // Used by program cache.
  const VaryingMap& varying_map() const {
    return varying_map_;
  }

  // Used by program cache.
  void set_attrib_map(const AttributeMap& attrib_map) {
    // copied because cache might be cleared
    attrib_map_ = AttributeMap(attrib_map);
  }

  // Used by program cache.
  void set_uniform_map(const UniformMap& uniform_map) {
    // copied because cache might be cleared
    uniform_map_ = UniformMap(uniform_map);
  }

  // Used by program cache.
  void set_varying_map(const VaryingMap& varying_map) {
    // copied because cache might be cleared
    varying_map_ = VaryingMap(varying_map);
  }

 private:
  friend class base::RefCounted<Shader>;
  friend class ShaderManager;

  Shader(GLuint service_id, GLenum shader_type);
  ~Shader();

  // Must be called only if we currently own the context. Forces the deletion
  // of the underlying shader service id.
  void Destroy();

  void IncUseCount();
  void DecUseCount();
  void MarkForDeletion();
  void DeleteServiceID();

  int use_count_;

  // The current state of the shader.
  ShaderState shader_state_;

  // The shader has been marked for deletion.
  bool marked_for_deletion_;

  // The shader this Shader is tracking.
  GLuint service_id_;

  // Type of shader - GL_VERTEX_SHADER or GL_FRAGMENT_SHADER.
  GLenum shader_type_;

  // Version of the shader. Can be kUndefinedShaderVersion or version returned
  // by ANGLE.
  int shader_version_;

  // Translated source type when shader was last requested to be compiled.
  TranslatedShaderSourceType source_type_;

  // Translator to use, set when shader was last requested to be compiled.
  scoped_refptr<ShaderTranslatorInterface> translator_;

  // True if compilation succeeded.
  bool valid_;

  // The shader source as passed to glShaderSource.
  std::string source_;

  // The source the last compile used.
  std::string last_compiled_source_;

  // The translated shader source.
  std::string translated_source_;

  // The shader translation log.
  std::string log_info_;

  // The type info when the shader was last compiled.
  AttributeMap attrib_map_;
  UniformMap uniform_map_;
  VaryingMap varying_map_;

  // The name hashing info when the shader was last compiled.
  NameMap name_map_;
};

// Tracks the Shaders.
//
// NOTE: To support shared resources an instance of this class will
// need to be shared by multiple GLES2Decoders.
class GPU_EXPORT ShaderManager {
 public:
  ShaderManager();
  ~ShaderManager();

  // Must call before destruction.
  void Destroy(bool have_context);

  // Creates a shader for the given shader ID.
  Shader* CreateShader(
      GLuint client_id,
      GLuint service_id,
      GLenum shader_type);

  // Gets an existing shader info for the given shader ID. Returns NULL if none
  // exists.
  Shader* GetShader(GLuint client_id);

  // Gets a client id for a given service id.
  bool GetClientId(GLuint service_id, GLuint* client_id) const;

  void Delete(Shader* shader);

  // Mark a shader as used
  void UseShader(Shader* shader);

  // Unmark a shader as used. If it has been deleted and is not used
  // then we free the shader.
  void UnuseShader(Shader* shader);

  // Check if a Shader is owned by this ShaderManager.
  bool IsOwned(Shader* shader);

 private:
  friend class Shader;

  // Info for each shader by service side shader Id.
  typedef base::hash_map<GLuint, scoped_refptr<Shader> > ShaderMap;
  ShaderMap shaders_;

  void RemoveShader(Shader* shader);

  DISALLOW_COPY_AND_ASSIGN(ShaderManager);
};

}  // namespace gles2
}  // namespace gpu

#endif  // GPU_COMMAND_BUFFER_SERVICE_SHADER_MANAGER_H_