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

  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.

=========================================================================*/
#ifndef vtkDirect3DBufferObject_h
#define vtkDirect3DBufferObject_h

#include "vtkRenderingDirect3DModule.h" // for export macro
#include "vtkObject.h"
#include <vector> // used for method args

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

class vtkCellArray;
class vtkDataArray;
class vtkPoints;
class vtkDirect3DRenderWindow;

/**
 * @brief Direct3D buffer object
 *
 * Direct3D buffer object to store index, geometry and/or attribute data on the
 * GPU.
 */

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

  /**
   * Upload data to the buffer object. The BufferObject::type() must match
   * @a type or be uninitialized.
   *
   * The T type must have tightly packed values of T::value_type accessible by
   * reference via T::operator[]. Additionally, the standard size() and empty()
   * methods must be implemented. The std::vector class is an example of such a
   * supported containers.
   */
  template <class T>
  bool Upload(const T &array);

  // non vector version
  template <class T>
  bool Upload(const T *array, size_t numElements);

  /**
   * Bind the buffer object ready for rendering.
   * @note Only one ARRAY_BUFFER and one ELEMENT_ARRAY_BUFFER may be bound at
   * any time.
   */
  virtual bool Bind();

  /**
   * Release the buffer. This should be done after rendering is complete.
   */
  bool Release();


  // Description:
  // Release any graphics resources that are being consumed by this class.
  void ReleaseGraphicsResources();

  /**
   * Return a string describing errors.
   */
  std::string GetError() const { return Error; }

  //@{
  /**
   * 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();
  //@}

  ID3D11Buffer* GetBuffer() {
    return this->Buffer; }

protected:
  vtkDirect3DBufferObject();
  ~vtkDirect3DBufferObject();
  std::string Error;
  ID3D11Buffer* Buffer;
  vtkDirect3DRenderWindow *Context;

  virtual bool UploadInternal(const void * /*buffer*/, size_t /* size */) {
    return false; }

  virtual bool UploadInternalWithBinding(
    const void *buffer, size_t sizem, D3D11_BIND_FLAG bflag);

private:
  vtkDirect3DBufferObject(const vtkDirect3DBufferObject&) VTK_DELETE_FUNCTION;
  void operator=(const vtkDirect3DBufferObject&) VTK_DELETE_FUNCTION;
};

template <class T>
inline bool vtkDirect3DBufferObject::Upload(
  const T &array)
{
  if (array.empty())
  {
    this->Error = "Refusing to upload empty array.";
    return false;
  }

  return this->UploadInternal(&array[0],
            array.size() * sizeof(typename T::value_type));
}

template <class T>
inline bool vtkDirect3DBufferObject::Upload(
  const T *array, size_t numElements)
{
  if (!array)
  {
    this->Error = "Refusing to upload empty array.";
    return false;
  }
  return this->UploadInternal(array,
                              numElements * sizeof(T));
}

#endif
