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

  Program:   Visualization Toolkit
  Module:    vtkDirect3DRenderWindow.cxx

  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.

=========================================================================*/
#include "vtkDirect3DRenderWindow.h"

// include the Direct3D Library file
#pragma comment (lib, "d3d11.lib")

#include "vtkDirect3DHelper.h"

#include <cassert>

#include "vtkCommand.h"
#include "vtkFloatArray.h"
#include "vtkNew.h"
#include "vtkObjectFactory.h"
//#include "vtkDirect3DActor.h"
//#include "vtkDirect3DBufferObject.h"
//#include "vtkDirect3DCamera.h"
#include "vtkLight.h"
//#include "vtkDirect3DProperty.h"
#include "vtkDirect3DRenderer.h"
#include "vtkDirect3DShaderCache.h"
#include "vtkDirect3DShaderProgram.h"
//#include "vtkDirect3DVertexArrayObject.h"
#include "vtkOutputWindow.h"
#include "vtkRendererCollection.h"
#include "vtkStdString.h"
#include "vtkStringOutputWindow.h"
//#include "vtkTextureObject.h"
#include "vtkUnsignedCharArray.h"
#include "vtkRenderWindowInteractor.h"

//#include "vtkTextureObjectVS.h"  // a pass through shader

#include <sstream>
using std::ostringstream;


static int vtkDirect3DRenderWindowGlobalMaximumNumberOfMultiSamples = 8;

vtkStandardNewMacro(vtkDirect3DRenderWindow);

const char* defaultWindowName = "Visualization Toolkit - Direct3D";

// ----------------------------------------------------------------------------
void vtkDirect3DRenderWindow::SetGlobalMaximumNumberOfMultiSamples(int val)
{
  if (val == vtkDirect3DRenderWindowGlobalMaximumNumberOfMultiSamples)
  {
    return;
  }
  vtkDirect3DRenderWindowGlobalMaximumNumberOfMultiSamples = val;
}

// ----------------------------------------------------------------------------
int vtkDirect3DRenderWindow::GetGlobalMaximumNumberOfMultiSamples()
{
  return vtkDirect3DRenderWindowGlobalMaximumNumberOfMultiSamples;
}

//----------------------------------------------------------------------------
const char *vtkDirect3DRenderWindow::GetRenderingBackend()
{
  return "Direct3D";
}

// ----------------------------------------------------------------------------
vtkDirect3DRenderWindow::vtkDirect3DRenderWindow()
{
  this->Initialized = false;
  this->DriverType = D3D_DRIVER_TYPE_NULL;
  this->ApplicationInstance =  NULL;
  this->WindowId = 0;

  this->D3dDevice = nullptr;
  this->D3dDevice1 = nullptr;
  this->ImmediateContext = nullptr;
  this->ImmediateContext1 = nullptr;
  this->SwapChain = nullptr;
  this->SwapChain1 = nullptr;
  this->RenderTargetView = nullptr;
  this->DepthStencil = nullptr;
  this->DepthStencilView = nullptr;

  this->ShaderCache = vtkDirect3DShaderCache::New();
  this->ShaderCache->SetContext(this);

  this->TextureUnitManager = 0;

  this->MultiSamples = vtkDirect3DRenderWindowGlobalMaximumNumberOfMultiSamples;
  delete [] this->WindowName;
  this->WindowName = new char[strlen(defaultWindowName) + 1];
  strcpy(this->WindowName, defaultWindowName);

  this->OffScreenUseFrameBuffer = 0;
  this->FrameBufferObject = 0;
  this->HardwareBufferSize[0] = 0;
  this->HardwareBufferSize[1] = 0;
  this->HardwareOffScreenBuffersBind = false;

  this->DrawPixelsTextureObject = NULL;

  this->OwnContext = 1;
  this->MaximumHardwareLineWidth = 1.0;

  this->Direct3DSupportTested = false;
  this->Direct3DSupportResult = 0;
  this->Direct3DSupportMessage = "Not tested yet";
}

// free up memory & close the window
// ----------------------------------------------------------------------------
vtkDirect3DRenderWindow::~vtkDirect3DRenderWindow()
{
  this->Finalize();
  this->ShaderCache->UnRegister(this);

  vtkRenderer *ren;
  vtkCollectionSimpleIterator rit;
  this->Renderers->InitTraversal(rit);
  while ( (ren = this->Renderers->GetNextRenderer(rit)) )
  {
    ren->SetRenderWindow(NULL);
  }

}

#include <directxcolors.h>

using namespace DirectX;

// Begin the rendering process.
void vtkDirect3DRenderWindow::Start(void)
{
  // if the renderer has not been initialized, do so now
  if (!this->D3dDevice)
  {
    this->Initialize();
  }

  // set the current window
  this->MakeCurrent();
}

// Get the size of the whole screen.
int *vtkDirect3DRenderWindow::GetScreenSize(void)
{
  HDC hDC = ::GetDC(NULL);
  if (hDC)
  {
    // This technique yields the screen size of the primary monitor
    // only in a multi-monitor configuration...
    this->Size[0] = ::GetDeviceCaps(hDC, HORZRES);
    this->Size[1] = ::GetDeviceCaps(hDC, VERTRES);
    ::ReleaseDC(NULL, hDC);
  }
  else
  {
    // This technique gets the "work area" (the whole screen except
    // for the bit covered by the Windows task bar) -- use it as a
    // fallback if there's an error calling GetDC.
    RECT rect;
    SystemParametersInfo(SPI_GETWORKAREA, 0, &rect, 0);

    this->Size[0] = rect.right - rect.left;
    this->Size[1] = rect.bottom - rect.top;
  }

  return this->Size;
}

int vtkDirect3DRenderWindow::GetEventPending()
{
  MSG msg;
  if (PeekMessage(&msg,this->WindowId,WM_MOUSEFIRST,WM_MOUSELAST,PM_NOREMOVE))
  {
    if (msg.message == WM_MOUSEMOVE)
    {
      PeekMessage(&msg,this->WindowId,WM_MOUSEFIRST,WM_MOUSELAST,PM_REMOVE);
    }
    if ((msg.message == WM_LBUTTONDOWN) ||
        (msg.message == WM_RBUTTONDOWN) ||
        (msg.message == WM_MBUTTONDOWN) ||
        (msg.message == WM_MOUSEWHEEL))
    {
      return 1;
    }
  }

  return 0;
}

void vtkDirect3DRenderWindow::DestroyWindow()
{
  this->ReleaseGraphicsResources(this);

  if (this->WindowId)
  {
    ReleaseDC(this->WindowId, GetDC(this->WindowId));
    // can't set WindowId=NULL, needed for DestroyWindow

    // clear the extra data before calling destroy
    vtkSetWindowLong(this->WindowId,sizeof(vtkLONG),(vtkLONG)0);
    ::DestroyWindow(this->WindowId); // windows api
    this->WindowId=0;
  }
}


//--------------------------------------------------------------------------------------
// Create Direct3D device and swap chain
//--------------------------------------------------------------------------------------
HRESULT vtkDirect3DRenderWindow::InitDevice()
{
    HRESULT hr = S_OK;

    UINT width = this->Size[0];
    UINT height = this->Size[1];

    UINT createDeviceFlags = 0;
#ifdef _DEBUG
    createDeviceFlags |= D3D11_CREATE_DEVICE_DEBUG;
#endif

    D3D_DRIVER_TYPE driverTypes[] =
    {
        D3D_DRIVER_TYPE_HARDWARE,
        D3D_DRIVER_TYPE_WARP,
        D3D_DRIVER_TYPE_REFERENCE,
    };
    UINT numDriverTypes = ARRAYSIZE( driverTypes );

    D3D_FEATURE_LEVEL featureLevels[] =
    {
        D3D_FEATURE_LEVEL_11_1,
        D3D_FEATURE_LEVEL_11_0,
        D3D_FEATURE_LEVEL_10_1,
        D3D_FEATURE_LEVEL_10_0,
    };
    UINT numFeatureLevels = ARRAYSIZE( featureLevels );

    for( UINT driverTypeIndex = 0; driverTypeIndex < numDriverTypes; driverTypeIndex++ )
    {
      this->DriverType = driverTypes[driverTypeIndex];
      hr = D3D11CreateDevice( nullptr, this->DriverType, nullptr, createDeviceFlags, featureLevels, numFeatureLevels,
                              D3D11_SDK_VERSION, &this->D3dDevice,
                              &this->FeatureLevel, &this->ImmediateContext );

      if ( hr == E_INVALIDARG )
      {
        // DirectX 11.0 platforms will not recognize D3D_FEATURE_LEVEL_11_1 so we need to retry without it
        hr = D3D11CreateDevice( nullptr, this->DriverType,
          nullptr, createDeviceFlags, &featureLevels[1], numFeatureLevels - 1,
          D3D11_SDK_VERSION, &this->D3dDevice, &this->FeatureLevel, &this->ImmediateContext );
      }

      if( SUCCEEDED( hr ) )
      {
        break;
      }
    }
    if( FAILED( hr ) )
    {
      return hr;
    }

    // Obtain DXGI factory from device (since we used nullptr for pAdapter above)
    IDXGIFactory1* dxgiFactory = nullptr;
    {
        IDXGIDevice* dxgiDevice = nullptr;
        hr = this->D3dDevice->QueryInterface( __uuidof(IDXGIDevice),
          reinterpret_cast<void**>(&dxgiDevice) );
        if (SUCCEEDED(hr))
        {
          IDXGIAdapter* adapter = nullptr;
          hr = dxgiDevice->GetAdapter(&adapter);
          if (SUCCEEDED(hr))
          {
            hr = adapter->GetParent( __uuidof(IDXGIFactory1),
              reinterpret_cast<void**>(&dxgiFactory) );
            adapter->Release();
          }
          dxgiDevice->Release();
        }
    }
    if (FAILED(hr))
    {
      return hr;
    }

    // Create swap chain
    IDXGIFactory2* dxgiFactory2 = nullptr;
    hr = dxgiFactory->QueryInterface( __uuidof(IDXGIFactory2), reinterpret_cast<void**>(&dxgiFactory2) );
    if ( dxgiFactory2 )
    {
      // DirectX 11.1 or later
      hr = this->D3dDevice->QueryInterface( __uuidof(ID3D11Device1),
        reinterpret_cast<void**>(&this->D3dDevice1) );
      if (SUCCEEDED(hr))
      {
        (void) this->ImmediateContext->QueryInterface( __uuidof(ID3D11DeviceContext1),
          reinterpret_cast<void**>(&this->ImmediateContext1) );
      }

      DXGI_SWAP_CHAIN_DESC1 sd;
      ZeroMemory(&sd, sizeof(sd));
      sd.Width = width;
      sd.Height = height;
      sd.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
      sd.SampleDesc.Count = 1;
      sd.SampleDesc.Quality = 0;
      sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
      sd.BufferCount = 1;

      hr = dxgiFactory2->CreateSwapChainForHwnd( this->D3dDevice,
      this->WindowId, &sd, nullptr, nullptr, &this->SwapChain1 );
      if (SUCCEEDED(hr))
      {
        hr = this->SwapChain1->QueryInterface( __uuidof(IDXGISwapChain),
          reinterpret_cast<void**>(&this->SwapChain) );
      }
      dxgiFactory2->Release();
    }
    else
    {
      // DirectX 11.0 systems
      DXGI_SWAP_CHAIN_DESC sd;
      ZeroMemory(&sd, sizeof(sd));
      sd.BufferCount = 1;
      sd.BufferDesc.Width = width;
      sd.BufferDesc.Height = height;
      sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
      sd.BufferDesc.RefreshRate.Numerator = 60;
      sd.BufferDesc.RefreshRate.Denominator = 1;
      sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
      sd.OutputWindow = this->WindowId;
      sd.SampleDesc.Count = 1;
      sd.SampleDesc.Quality = 0;
      sd.Windowed = TRUE;

      hr = dxgiFactory->CreateSwapChain( this->D3dDevice, &sd, &this->SwapChain );
    }

    // Note this tutorial doesn't handle full-screen swapchains so we block the ALT+ENTER shortcut
    dxgiFactory->MakeWindowAssociation( this->WindowId, DXGI_MWA_NO_ALT_ENTER );

    dxgiFactory->Release();

    if (FAILED(hr))
    {
      return hr;
    }


    // Create a render target view
    ID3D11Texture2D* pBackBuffer = nullptr;
    hr = this->SwapChain->GetBuffer( 0, __uuidof( ID3D11Texture2D ),
      reinterpret_cast<void**>( &pBackBuffer ) );
    if( FAILED( hr ) )
    {
      return hr;
    }

    hr = this->D3dDevice->CreateRenderTargetView( pBackBuffer, nullptr,
      &this->RenderTargetView );
    pBackBuffer->Release();
    if( FAILED( hr ) )
    {
      return hr;
    }

    // Create depth stencil texture
    D3D11_TEXTURE2D_DESC descDepth;
    ZeroMemory( &descDepth, sizeof(descDepth) );
    descDepth.Width = width;
    descDepth.Height = height;
    descDepth.MipLevels = 1;
    descDepth.ArraySize = 1;
    descDepth.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
    descDepth.SampleDesc.Count = 1;
    descDepth.SampleDesc.Quality = 0;
    descDepth.Usage = D3D11_USAGE_DEFAULT;
    descDepth.BindFlags = D3D11_BIND_DEPTH_STENCIL;
    descDepth.CPUAccessFlags = 0;
    descDepth.MiscFlags = 0;
    hr = this->D3dDevice->CreateTexture2D(
      &descDepth, nullptr, &this->DepthStencil );
    if( FAILED( hr ) )
    {
      return hr;
    }

    // Create the depth stencil view
    D3D11_DEPTH_STENCIL_VIEW_DESC descDSV;
    ZeroMemory( &descDSV, sizeof(descDSV) );
    descDSV.Format = descDepth.Format;
    descDSV.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
    descDSV.Texture2D.MipSlice = 0;
    hr = this->D3dDevice->CreateDepthStencilView(
      this->DepthStencil, &descDSV, &this->DepthStencilView );
    if( FAILED( hr ) )
    {
      return hr;
    }

    this->ImmediateContext->OMSetRenderTargets(
      1, &this->RenderTargetView, this->DepthStencilView );

    D3D11_RASTERIZER_DESC rasterDesc;
    rasterDesc.AntialiasedLineEnable = false;
    rasterDesc.CullMode = D3D11_CULL_NONE;
    rasterDesc.DepthBias = 0;
    rasterDesc.DepthBiasClamp = 0.0f;
    rasterDesc.DepthClipEnable = true;
    rasterDesc.FillMode = D3D11_FILL_SOLID;
    rasterDesc.FrontCounterClockwise = false;
    rasterDesc.MultisampleEnable = false;
    rasterDesc.ScissorEnable = false;
    rasterDesc.SlopeScaledDepthBias = 0.0f;

    hr = this->D3dDevice->CreateRasterizerState(
      &rasterDesc, &this->RasterizerState);
    if(FAILED(hr))
    {
      return false;
    }
    this->ImmediateContext->RSSetState(this->RasterizerState);

    return S_OK;
}


// End the rendering process and display the image.
void vtkDirect3DRenderWindow::Frame(void)
{
  this->MakeCurrent();
  if (!this->AbortRender && this->DoubleBuffer && this->SwapBuffers)
  {
    // If this check is not enforced, we crash in offscreen rendering
    if (this->D3dDevice)
    {
      this->SwapChain->Present( 0, 0 );
      vtkDebugMacro(<< " SwapBuffers\n");
    }
  }
}



//--------------------------------------------------------------------------------------
// Clean up the objects we've created
//--------------------------------------------------------------------------------------
void vtkDirect3DRenderWindow::Finalize()
{
  this->DestroyWindow();

  if (this->ImmediateContext )
  {
    this->ImmediateContext->ClearState();
  }

  if ( this->RenderTargetView )
  {
    this->RenderTargetView->Release();
    this->RenderTargetView = nullptr;
  }

  if ( this->SwapChain1 )
  {
    this->SwapChain1->Release();
    this->SwapChain1 = nullptr;
  }
  if ( this->SwapChain )
  {
    this->SwapChain->Release();
    this->SwapChain = nullptr;
  }
  if ( this->ImmediateContext1 )
  {
    this->ImmediateContext1->Release();
    this->ImmediateContext1 = nullptr;
  }
  if ( this->ImmediateContext )
  {
    this->ImmediateContext->Release();
    this->ImmediateContext = nullptr;
  }
  if (this->DepthStencil)
  {
    this->DepthStencil->Release();
    this->DepthStencil = nullptr;
  }

  if (this->DepthStencilView)
  {
    this->DepthStencilView->Release();
    this->DepthStencilView = nullptr;
  }

#ifdef _DEBUG
  ID3D11Debug *debugDev;
  this->D3dDevice->QueryInterface(
   __uuidof(ID3D11Debug), reinterpret_cast<void**>(&debugDev));

  debugDev->ReportLiveDeviceObjects(D3D11_RLDO_DETAIL);
#endif

  if ( this->D3dDevice1 )
  {
    this->D3dDevice1->Release();
    this->D3dDevice1 = nullptr;
  }
  if ( this->D3dDevice )
  {
    this->D3dDevice->Release();
    this->D3dDevice = nullptr;
  }
}

LRESULT vtkDirect3DRenderWindow::MessageProc(HWND hWnd, UINT message,
                                             WPARAM wParam, LPARAM lParam)
{
  switch (message)
  {
    case WM_CREATE:
    {
    // nothing to be done here, opengl is initilized after the call to
    // create now
    return 0;
    }
    case WM_DESTROY:
      this->Finalize();
      return 0;
    case WM_SIZE:
      /* track window size changes */
      if (this->D3dDevice)
      {
        this->SetSize((int) LOWORD(lParam),(int) HIWORD(lParam));
        return 0;
      }
    case WM_PAINT:
    {
    PAINTSTRUCT ps;
    BeginPaint(hWnd, &ps);
    if (this->D3dDevice)
    {
      this->Render();
    }
    EndPaint(hWnd, &ps);
    return 0;
    }
    break;
    case WM_ERASEBKGND:
      return TRUE;
    case WM_SETCURSOR:
      if (HTCLIENT == LOWORD(lParam))
      {
        this->SetCurrentCursor(this->GetCurrentCursor());
        return TRUE;
      }
      break;
    default:
      this->InvokeEvent(vtkCommand::RenderWindowMessageEvent, &message);
      break;
  }
  return DefWindowProc(hWnd, message, wParam, lParam);
}


LRESULT APIENTRY vtkDirect3DRenderWindow::WndProc(HWND hWnd, UINT message,
                                                     WPARAM wParam,
                                                     LPARAM lParam)
{
  LRESULT res;

  vtkDirect3DRenderWindow *me =
    reinterpret_cast<vtkDirect3DRenderWindow *>(vtkGetWindowLong(hWnd,sizeof(vtkLONG)));

  if (me && me->GetReferenceCount()>0)
  {
    me->Register(me);
    res = me->MessageProc(hWnd, message, wParam, lParam);
    me->UnRegister(me);
  }
  else
  {
    res = DefWindowProc(hWnd, message, wParam, lParam);
  }

  return res;
}

// ----------------------------------------------------------------------------
void AdjustWindowRectForBorders(HWND hwnd, DWORD style, const int x, const int y,
                                const int width, const int height, RECT &r)
{
  if (!style && hwnd)
  {
    style = GetWindowLong(hwnd, GWL_STYLE);
  }
  r.left = x;
  r.top = y;
  r.right = r.left + width;
  r.bottom = r.top + height;
  BOOL result = AdjustWindowRect(&r, style, FALSE);
  if (!result)
  {
    vtkGenericWarningMacro("AdjustWindowRect failed, error: "
      << GetLastError());
  }
}

void vtkDirect3DRenderWindow::CreateAWindow()
{
  // Register class
  WNDCLASSEX wcex;
  wcex.cbSize = sizeof( WNDCLASSEX );
  wcex.style = CS_HREDRAW | CS_VREDRAW;
  wcex.lpfnWndProc = vtkDirect3DRenderWindow::WndProc;
  wcex.cbClsExtra = 0;
  wcex.cbWndExtra = 0;
  wcex.hInstance = this->ApplicationInstance;
  wcex.hIcon = LoadIcon( this->ApplicationInstance, ( LPCTSTR )IDI_APPLICATION );
  wcex.hCursor = LoadCursor( nullptr, IDC_ARROW );
  wcex.hbrBackground = ( HBRUSH )( COLOR_WINDOW + 1 );
  wcex.lpszMenuName = nullptr;
#ifdef UNICODE
  wcex.lpszClassName = L"vtkDirect3D";
#else
  wcex.lpszClassName = "vtkDirect3D";
#endif
  // vtk doesn't use the first extra vtkLONG's worth of bytes,
  // but app writers may want them, so we provide them. VTK
  // does use the second vtkLONG's worth of bytes of extra space.
  wcex.cbWndExtra = 2 * sizeof(vtkLONG);
  wcex.hIconSm = LoadIcon( wcex.hInstance, ( LPCTSTR )IDI_APPLICATION );
  if( !RegisterClassEx( &wcex ) )
  {
    vtkErrorMacro("Failed to create window class");
    return;
  }

#ifdef UNICODE
    wchar_t *wname = new wchar_t [mbstowcs(NULL, this->WindowName, 32000)+1];
    mbstowcs(wname, this->WindowName, 32000);
#endif

  // Create window
  int x = ((this->Position[0] >= 0) ? this->Position[0] : 5);
  int y = ((this->Position[1] >= 0) ? this->Position[1] : 5);
  int height = ((this->Size[1] > 0) ? this->Size[1] : 300);
  int width = ((this->Size[0] > 0) ? this->Size[0] : 300);

  DWORD style;
  if (this->Borders)
  {
    style = WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN;
  }
  else
  {
    style = WS_POPUP | WS_CLIPCHILDREN;
  }
  RECT r;
  AdjustWindowRectForBorders(0, style, x, y, width, height, r);
#ifdef UNICODE
  this->WindowId = CreateWindow( wcex.lpszClassName,
    wname,
    WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
    x, y, r.right - r.left, r.bottom - r.top,
    nullptr, nullptr, this->ApplicationInstance,
    nullptr );
#else
  this->WindowId = CreateWindow( wcex.lpszClassName,
    this->WindowName,
    WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
    x, y, r.right - r.left, r.bottom - r.top,
    nullptr, nullptr, this->ApplicationInstance,
    nullptr );
#endif
  if( !this->WindowId )
  {
    vtkErrorMacro("Failed to create window");
    return;
  }

  ShowWindow( this->WindowId, SW_SHOW);

  vtkSetWindowLong(this->WindowId,sizeof(vtkLONG),(intptr_t)this);
  this->Mapped = 1;
}

//--------------------------------------------------------------------------------------
// Register class and create window
//--------------------------------------------------------------------------------------
void vtkDirect3DRenderWindow::Initialize()
{
  this->ApplicationInstance = GetModuleHandle(NULL); /*AfxGetInstanceHandle();*/

  this->CreateAWindow();


  this->InitDevice();
}

// ----------------------------------------------------------------------------
void vtkDirect3DRenderWindow::ReleaseGraphicsResources(vtkRenderWindow *renWin)
{
  // release the registered resources
  vtkCollectionSimpleIterator rsit;
  this->Renderers->InitTraversal(rsit);
  vtkRenderer *aren;
  while ( (aren = this->Renderers->GetNextRenderer(rsit)) )
  {
    if (aren->GetRenderWindow() == this)
    {
      aren->ReleaseGraphicsResources(renWin);
    }
  }

  this->ShaderCache->ReleaseGraphicsResources(renWin);

  this->Initialized = false;
}

// ----------------------------------------------------------------------------
vtkMTimeType vtkDirect3DRenderWindow::GetContextCreationTime()
{
  return this->ContextCreationTime.GetMTime();
}

// ----------------------------------------------------------------------------
// Description:
// Return the Direct3D name of the back left buffer.
// It is GL_BACK_LEFT if GL is bound to the window-system-provided
// framebuffer. It is GL_COLOR_ATTACHMENT0_EXT if GL is bound to an
// application-created framebuffer object (GPU-based offscreen rendering)
// It is used by vtkDirect3DCamera.
unsigned int vtkDirect3DRenderWindow::GetBackLeftBuffer()
{
  return this->BackLeftBuffer;
}

// ----------------------------------------------------------------------------
// Description:
// Return the Direct3D name of the back right buffer.
// It is GL_BACK_RIGHT if GL is bound to the window-system-provided
// framebuffer. It is GL_COLOR_ATTACHMENT0_EXT+1 if GL is bound to an
// application-created framebuffer object (GPU-based offscreen rendering)
// It is used by vtkDirect3DCamera.
unsigned int vtkDirect3DRenderWindow::GetBackRightBuffer()
{
  return this->BackRightBuffer;
}

// ----------------------------------------------------------------------------
// Description:
// Return the Direct3D name of the front left buffer.
// It is GL_FRONT_LEFT if GL is bound to the window-system-provided
// framebuffer. It is GL_COLOR_ATTACHMENT0_EXT if GL is bound to an
// application-created framebuffer object (GPU-based offscreen rendering)
// It is used by vtkDirect3DCamera.
unsigned int vtkDirect3DRenderWindow::GetFrontLeftBuffer()
{
  return this->FrontLeftBuffer;
}

// ----------------------------------------------------------------------------
// Description:
// Return the Direct3D name of the front right buffer.
// It is GL_FRONT_RIGHT if GL is bound to the window-system-provided
// framebuffer. It is GL_COLOR_ATTACHMENT0_EXT+1 if GL is bound to an
// application-created framebuffer object (GPU-based offscreen rendering)
// It is used by vtkDirect3DCamera.
unsigned int vtkDirect3DRenderWindow::GetFrontRightBuffer()
{
  return this->FrontRightBuffer;
}

// ----------------------------------------------------------------------------
// Description:
// Return the Direct3D name of the back left buffer.
// It is GL_BACK if GL is bound to the window-system-provided
// framebuffer. It is GL_COLOR_ATTACHMENT0_EXT if GL is bound to an
// application-created framebuffer object (GPU-based offscreen rendering)
// It is used by vtkDirect3DCamera.
unsigned int vtkDirect3DRenderWindow::GetBackBuffer()
{
  return this->BackBuffer;
}

// ----------------------------------------------------------------------------
// Description:
// Return the Direct3D name of the front left buffer.
// It is GL_FRONT if GL is bound to the window-system-provided
// framebuffer. It is GL_COLOR_ATTACHMENT0_EXT if GL is bound to an
// application-created framebuffer object (GPU-based offscreen rendering)
// It is used by vtkDirect3DCamera.
unsigned int vtkDirect3DRenderWindow::GetFrontBuffer()
{
  return this->FrontBuffer;
}

// Update system if needed due to stereo rendering.
void vtkDirect3DRenderWindow::StereoUpdate(void)
{
  // if stereo is on and it wasn't before
  if (this->StereoRender && (!this->StereoStatus))
  {
    switch (this->StereoType)
    {
      case VTK_STEREO_CRYSTAL_EYES:
        // not clear this is supposed to be empty,
        // but it has been that way forever.
        break;
      case VTK_STEREO_RED_BLUE:
        this->StereoStatus = 1;
        break;
      case VTK_STEREO_ANAGLYPH:
        this->StereoStatus = 1;
        break;
      case VTK_STEREO_DRESDEN:
        this->StereoStatus = 1;
        break;
      case VTK_STEREO_INTERLACED:
        this->StereoStatus = 1;
        break;
      case VTK_STEREO_CHECKERBOARD:
        this->StereoStatus = 1;
        break;
      case VTK_STEREO_SPLITVIEWPORT_HORIZONTAL:
        this->StereoStatus = 1;
        break;
    }
  }
  else if ((!this->StereoRender) && this->StereoStatus)
  {
    switch (this->StereoType)
    {
      case VTK_STEREO_CRYSTAL_EYES:
        this->StereoStatus = 0;
        break;
      case VTK_STEREO_RED_BLUE:
        this->StereoStatus = 0;
        break;
      case VTK_STEREO_ANAGLYPH:
        this->StereoStatus = 0;
        break;
      case VTK_STEREO_DRESDEN:
        this->StereoStatus = 0;
        break;
      case VTK_STEREO_INTERLACED:
        this->StereoStatus = 0;
        break;
      case VTK_STEREO_CHECKERBOARD:
        this->StereoStatus = 0;
        break;
      case VTK_STEREO_SPLITVIEWPORT_HORIZONTAL:
        this->StereoStatus = 0;
        break;
    }
  }
}

void vtkDirect3DRenderWindow::SetSize(int a[2])
{
  this->SetSize(a[0], a[1]);
}

void vtkDirect3DRenderWindow::SetSize(int x, int y)
{
  static int resizing = 0;
  if ((this->Size[0] != x) || (this->Size[1] != y))
  {
    this->Superclass::SetSize(x, y);

    if (this->Interactor)
    {
      this->Interactor->SetSize(x, y);
    }

  if (this->Mapped)
    {
      if (!resizing)
      {
        resizing = 1;

        RECT r;
        AdjustWindowRectForBorders(this->WindowId, 0, 0, 0, x, y, r);
        SetWindowPos(this->WindowId, HWND_TOP, 0, 0,
                     r.right - r.left,
                     r.bottom - r.top,
                     SWP_NOMOVE | SWP_NOZORDER);

        this->ImmediateContext->OMSetRenderTargets(0, 0, 0);

        // Release all outstanding references to the swap chain's buffers.
        this->RenderTargetView->Release();
        this->DepthStencilView->Release();
        this->DepthStencil->Release();

        HRESULT hr;
        // Preserve the existing buffer count and format.
        // Automatically choose the width and height to match the client rect for HWNDs.
        hr = this->SwapChain->ResizeBuffers(1,
          this->Size[0], this->Size[1],
          DXGI_FORMAT_UNKNOWN,
          0);

        // Perform error handling here!

        // Get buffer and create a render-target-view.
        ID3D11Texture2D* pBuffer;
        hr = this->SwapChain->GetBuffer(0, __uuidof( ID3D11Texture2D),
                                     (void**) &pBuffer );
        // Perform error handling here!

        hr = this->D3dDevice->CreateRenderTargetView(pBuffer, NULL,
                                                 &this->RenderTargetView);
        // Perform error handling here!
        pBuffer->Release();

        // Create depth stencil texture
        D3D11_TEXTURE2D_DESC descDepth;
        ZeroMemory( &descDepth, sizeof(descDepth) );
        descDepth.Width = this->Size[0];
        descDepth.Height = this->Size[1];
        descDepth.MipLevels = 1;
        descDepth.ArraySize = 1;
        descDepth.Format = DXGI_FORMAT_D24_UNORM_S8_UINT;
        descDepth.SampleDesc.Count = 1;
        descDepth.SampleDesc.Quality = 0;
        descDepth.Usage = D3D11_USAGE_DEFAULT;
        descDepth.BindFlags = D3D11_BIND_DEPTH_STENCIL;
        descDepth.CPUAccessFlags = 0;
        descDepth.MiscFlags = 0;
        hr = this->D3dDevice->CreateTexture2D(
          &descDepth, nullptr, &this->DepthStencil );
        if( FAILED( hr ) )
        {
          return;
        }

        // Create the depth stencil view
        D3D11_DEPTH_STENCIL_VIEW_DESC descDSV;
        ZeroMemory( &descDSV, sizeof(descDSV) );
        descDSV.Format = descDepth.Format;
        descDSV.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
        descDSV.Texture2D.MipSlice = 0;
        hr = this->D3dDevice->CreateDepthStencilView(
          this->DepthStencil, &descDSV, &this->DepthStencilView );
        if( FAILED( hr ) )
        {
          return;
        }

        this->ImmediateContext->OMSetRenderTargets(
          1, &this->RenderTargetView, this->DepthStencilView );

        resizing = 0;
      }
    }
  }
}


void vtkDirect3DRenderWindow::PrintSelf(ostream& os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os,indent);
}

unsigned char* vtkDirect3DRenderWindow::GetPixelData(int x1, int y1,
                                                   int x2, int y2,
                                                   int front)
{
  int     y_low, y_hi;
  int     x_low, x_hi;

  if (y1 < y2)
  {
    y_low = y1;
    y_hi  = y2;
  }
  else
  {
    y_low = y2;
    y_hi  = y1;
  }

  if (x1 < x2)
  {
    x_low = x1;
    x_hi  = x2;
  }
  else
  {
    x_low = x2;
    x_hi  = x1;
  }

  unsigned char *data =
    new unsigned char[(x_hi - x_low + 1)*(y_hi - y_low + 1)*3];
  this->GetPixelData(x1, y1, x2, y2, front, data);
  return data;
}

int vtkDirect3DRenderWindow::GetPixelData(int x1, int y1,
                                        int x2, int y2,
                                        int front,
                                        vtkUnsignedCharArray* data)
{
  int     y_low, y_hi;
  int     x_low, x_hi;

  if (y1 < y2)
  {
    y_low = y1;
    y_hi  = y2;
  }
  else
  {
    y_low = y2;
    y_hi  = y1;
  }

  if (x1 < x2)
  {
    x_low = x1;
    x_hi  = x2;
  }
  else
  {
    x_low = x2;
    x_hi  = x1;
  }

  int width  = abs(x_hi - x_low) + 1;
  int height = abs(y_hi - y_low) + 1;
  int size = 3*width*height;

  if ( data->GetMaxId()+1 != size)
  {
    vtkDebugMacro("Resizing array.");
    data->SetNumberOfComponents(3);
    data->SetNumberOfValues(size);
  }
  return this->GetPixelData(x1, y1, x2, y2, front, data->GetPointer(0));

}

int vtkDirect3DRenderWindow::GetPixelData(int x1, int y1,
                                        int x2, int y2,
                                        int front, unsigned char* data)
{
  int     y_low, y_hi;
  int     x_low, x_hi;

  // set the current window
  this->MakeCurrent();

  if (y1 < y2)
  {
    y_low = y1;
    y_hi  = y2;
  }
  else
  {
    y_low = y2;
    y_hi  = y1;
  }

  if (x1 < x2)
  {
    x_low = x1;
    x_hi  = x2;
  }
  else
  {
    x_low = x2;
    x_hi  = x1;
  }

  if (front)
  {
//    glReadBuffer(static_cast<GLenum>(this->GetFrontLeftBuffer()));
  }
  else
  {
//    glReadBuffer(static_cast<GLenum>(this->GetBackLeftBuffer()));
  }

//  glDisable( GL_SCISSOR_TEST );

  // Calling pack alignment ensures that we can grab the any size window
//  glPixelStorei( GL_PACK_ALIGNMENT, 1 );
//  glReadPixels(x_low, y_low, x_hi-x_low+1, y_hi-y_low+1, GL_RGB,
 //              GL_UNSIGNED_BYTE, data);

//  if (glGetError() != GL_NO_ERROR)
  {
 //   return VTK_ERROR;
  }
 // else
  {
    return VTK_OK;
  }

}

int vtkDirect3DRenderWindow::SetPixelData(int x1, int y1, int x2, int y2,
                                        vtkUnsignedCharArray *data, int front)
{
  int     y_low, y_hi;
  int     x_low, x_hi;

  if (y1 < y2)
  {

    y_low = y1;
    y_hi  = y2;
  }
  else
  {
    y_low = y2;
    y_hi  = y1;
  }

  if (x1 < x2)
  {
    x_low = x1;
    x_hi  = x2;
  }
  else
  {
    x_low = x2;
    x_hi  = x1;
  }

  int width  = abs(x_hi - x_low) + 1;
  int height = abs(y_hi - y_low) + 1;
  int size = 3*width*height;

  if ( data->GetMaxId()+1 != size)
  {
    vtkErrorMacro("Buffer is of wrong size.");
    return VTK_ERROR;
  }
  return this->SetPixelData(x1, y1, x2, y2, data->GetPointer(0), front);

}


// draw (and stretch as needed) the data to the current viewport
void vtkDirect3DRenderWindow::DrawPixels(
  int srcWidth, int srcHeight, int numComponents, int dataType, void *data)
{
  // glDisable( GL_SCISSOR_TEST );
  // glDisable(GL_DEPTH_TEST);
  // if (!this->DrawPixelsTextureObject)
  // {
  //   this->DrawPixelsTextureObject = vtkTextureObject::New();
  // }
  // else
  // {
  //   this->DrawPixelsTextureObject->ReleaseGraphicsResources(this);
  // }
  // this->DrawPixelsTextureObject->SetContext(this);
  // this->DrawPixelsTextureObject->Create2DFromRaw(srcWidth, srcHeight,
  //       numComponents, dataType, data);
  // this->DrawPixelsTextureObject->CopyToFrameBuffer(NULL, NULL);
}

// very generic call to draw pixel data to a region of the window
void vtkDirect3DRenderWindow::DrawPixels(
  int dstXmin, int dstYmin, int dstXmax, int dstYmax,
  int srcXmin, int srcYmin, int srcXmax, int srcYmax,
  int srcWidth, int srcHeight, int numComponents, int dataType, void *data)
{
  // glDisable( GL_SCISSOR_TEST );
  // glDisable(GL_DEPTH_TEST);
  // if (!this->DrawPixelsTextureObject)
  // {
  //   this->DrawPixelsTextureObject = vtkTextureObject::New();
  // }
  // else
  // {
  //   this->DrawPixelsTextureObject->ReleaseGraphicsResources(this);
  // }
  // this->DrawPixelsTextureObject->SetContext(this);
  // this->DrawPixelsTextureObject->Create2DFromRaw(srcWidth, srcHeight,
  //       numComponents, dataType, data);
  // this->DrawPixelsTextureObject->CopyToFrameBuffer(
  //     srcXmin, srcYmin, srcXmax, srcYmax,
  //     dstXmin, dstYmin, dstXmax, dstYmax,
  //     this->GetSize()[0], this->GetSize()[1],
  //     NULL, NULL);
}

// less generic verison, old API
void vtkDirect3DRenderWindow::DrawPixels(int x1, int y1, int x2, int y2, int numComponents, int dataType, void *data)
{
  int     y_low, y_hi;
  int     x_low, x_hi;

  if (y1 < y2)
  {
    y_low = y1;
    y_hi  = y2;
  }
  else
  {
    y_low = y2;
    y_hi  = y1;
  }

  if (x1 < x2)
  {
    x_low = x1;
    x_hi  = x2;
  }
  else
  {
    x_low = x2;
    x_hi  = x1;
  }

  int width = x_hi-x_low+1;
  int height = y_hi-y_low+1;

  // call the more generic version
  this->DrawPixels(x_low, y_low, x_hi, y_hi,
    0, 0, width-1, height-1, width, height, numComponents, dataType, data);
}

int vtkDirect3DRenderWindow::SetPixelData(int x1, int y1, int x2, int y2,
                                        unsigned char *data, int front)
{
//   // set the current window
//   this->MakeCurrent();

//   GLint buffer;

// #ifdef GL_DRAW_BUFFER
//   glGetIntegerv(GL_DRAW_BUFFER, &buffer);
// #endif

//   if (front)
//   {
//     glDrawBuffer(this->GetFrontBuffer());
//   }
//   else
//   {
//     glDrawBuffer(this->GetBackBuffer());
//   }

//   this->DrawPixels(x1, y1, x2, y2, 3, VTK_UNSIGNED_CHAR, data);

//   glDrawBuffer(buffer);

//   if (glGetError() != GL_NO_ERROR)
//   {
//     return VTK_ERROR;
//   }
//   else
//   {
     return VTK_OK;
//   }
}

float* vtkDirect3DRenderWindow::GetRGBAPixelData(int x1, int y1, int x2, int y2,
                                               int front)
{

  int     y_low, y_hi;
  int     x_low, x_hi;
  int     width, height;

  if (y1 < y2)
  {
    y_low = y1;
    y_hi  = y2;
  }
  else
  {
    y_low = y2;
    y_hi  = y1;
  }

  if (x1 < x2)
  {
    x_low = x1;
    x_hi  = x2;
  }
  else
  {
    x_low = x2;
    x_hi  = x1;
  }

  width  = abs(x_hi - x_low) + 1;
  height = abs(y_hi - y_low) + 1;

  float *data = new float[ (width*height*4) ];
  this->GetRGBAPixelData(x1, y1, x2, y2, front, data);

  return data;

}

int vtkDirect3DRenderWindow::GetRGBAPixelData(int x1, int y1, int x2, int y2,
                                            int front, vtkFloatArray* data)
{
  int     y_low, y_hi;
  int     x_low, x_hi;
  int     width, height;

  if (y1 < y2)
  {
    y_low = y1;
    y_hi  = y2;
  }
  else
  {
    y_low = y2;
    y_hi  = y1;
  }

  if (x1 < x2)
  {
    x_low = x1;
    x_hi  = x2;
  }
  else
  {
    x_low = x2;
    x_hi  = x1;
  }

  width  = abs(x_hi - x_low) + 1;
  height = abs(y_hi - y_low) + 1;

  int size = 4*width*height;

  if ( data->GetMaxId()+1 != size)
  {
    vtkDebugMacro("Resizing array.");
    data->SetNumberOfComponents(4);
    data->SetNumberOfValues(size);
  }
  return this->GetRGBAPixelData(x1, y1, x2, y2, front, data->GetPointer(0));

}

int vtkDirect3DRenderWindow::GetRGBAPixelData(int x1, int y1, int x2, int y2,
                                            int front, float* data)
{
  int     y_low, y_hi;
  int     x_low, x_hi;
  int     width, height;

  // set the current window
  this->MakeCurrent();

  if (y1 < y2)
  {
    y_low = y1;
    y_hi  = y2;
  }
  else
  {
    y_low = y2;
    y_hi  = y1;
  }

  if (x1 < x2)
  {
    x_low = x1;
    x_hi  = x2;
  }
  else
  {
    x_low = x2;
    x_hi  = x1;
  }

  if (front)
  {
//    glReadBuffer(static_cast<GLenum>(this->GetFrontLeftBuffer()));
  }
  else
  {
//    glReadBuffer(static_cast<GLenum>(this->GetBackLeftBuffer()));
  }

  width  = abs(x_hi - x_low) + 1;
  height = abs(y_hi - y_low) + 1;


  // Turn of texturing in case it is on - some drivers have a problem
  // getting / setting pixels with texturing enabled.
  // glDisable( GL_TEXTURE_2D );

  // glPixelStorei( GL_PACK_ALIGNMENT, 1 );
  // glReadPixels( x_low, y_low, width, height, GL_RGBA, GL_FLOAT, data);

  // if (glGetError() != GL_NO_ERROR)
  // {
  //   return VTK_ERROR;
  // }
  // else
  // {
    return VTK_OK;
//  }
}

void vtkDirect3DRenderWindow::ReleaseRGBAPixelData(float *data)
{
  delete[] data;
}

int vtkDirect3DRenderWindow::SetRGBAPixelData(int x1, int y1, int x2, int y2,
                                            vtkFloatArray *data, int front,
                                            int blend)
{
  int     y_low, y_hi;
  int     x_low, x_hi;
  int     width, height;

  if (y1 < y2)
  {
    y_low = y1;
    y_hi  = y2;
  }
  else
  {
    y_low = y2;
    y_hi  = y1;
  }

  if (x1 < x2)
  {
    x_low = x1;
    x_hi  = x2;
  }
  else
  {
    x_low = x2;
    x_hi  = x1;
  }

  width  = abs(x_hi-x_low) + 1;
  height = abs(y_hi-y_low) + 1;

  int size = 4*width*height;
  if ( data->GetMaxId()+1 != size )
  {
    vtkErrorMacro("Buffer is of wrong size.");
    return VTK_ERROR;
  }

  return this->SetRGBAPixelData(x1, y1, x2, y2, data->GetPointer(0), front,
                                blend);
}

int vtkDirect3DRenderWindow::SetRGBAPixelData(int x1, int y1, int x2, int y2,
                                            float *data, int front, int blend)
{
  // set the current window
  this->MakeCurrent();


//   GLint buffer;
// #ifdef GL_DRAW_BUFFER
//   glGetIntegerv(GL_DRAW_BUFFER, &buffer);
// #endif

//   if (front)
//   {
//     glDrawBuffer(this->GetFrontBuffer());
//   }
//   else
//   {
//     glDrawBuffer(this->GetBackBuffer());
//   }

//   if (!blend)
//   {
//     glDisable(GL_BLEND);
//     this->DrawPixels(x1, y1, x2, y2, 4, VTK_FLOAT, data); // TODO replace dprecated function
//     glEnable(GL_BLEND);
//   }
//   else
//   {
//     this->DrawPixels(x1, y1, x2, y2, 4, VTK_FLOAT, data);
//   }

//   glDrawBuffer(buffer);

//   if (glGetError() != GL_NO_ERROR)
//   {
//     return VTK_ERROR;
//   }
//   else
//   {
     return VTK_OK;
//   }
}

unsigned char *vtkDirect3DRenderWindow::GetRGBACharPixelData(int x1, int y1,
                                                           int x2, int y2,
                                                           int front)
{
  int     y_low, y_hi;
  int     x_low, x_hi;
  int     width, height;

  if (y1 < y2)
  {
    y_low = y1;
    y_hi  = y2;
  }
  else
  {
    y_low = y2;
    y_hi  = y1;
  }


  if (x1 < x2)
  {
    x_low = x1;
    x_hi  = x2;
  }
  else
  {
    x_low = x2;
    x_hi  = x1;
  }

  width  = abs(x_hi - x_low) + 1;
  height = abs(y_hi - y_low) + 1;

  unsigned char *data = new unsigned char[ (width*height)*4 ];
  this->GetRGBACharPixelData(x1, y1, x2, y2, front, data);

  return data;
}

int vtkDirect3DRenderWindow::GetRGBACharPixelData(int x1, int y1,
                                                int x2, int y2,
                                                int front,
                                                vtkUnsignedCharArray* data)
{
  int     y_low, y_hi;
  int     x_low, x_hi;

  if (y1 < y2)
  {
    y_low = y1;
    y_hi  = y2;
  }
  else
  {
    y_low = y2;
    y_hi  = y1;
  }

  if (x1 < x2)
  {
    x_low = x1;
    x_hi  = x2;
  }
  else
  {
    x_low = x2;
    x_hi  = x1;
  }

  int width  = abs(x_hi - x_low) + 1;
  int height = abs(y_hi - y_low) + 1;
  int size = 4*width*height;

  if ( data->GetMaxId()+1 != size)
  {
    vtkDebugMacro("Resizing array.");
    data->SetNumberOfComponents(4);
    data->SetNumberOfValues(size);
  }
  return this->GetRGBACharPixelData(x1, y1, x2, y2, front,
                                    data->GetPointer(0));
}

int vtkDirect3DRenderWindow::GetRGBACharPixelData(int x1, int y1,
                                                int x2, int y2,
                                                int front,
                                                unsigned char* data)
{
  int     y_low, y_hi;
  int     x_low, x_hi;
  int     width, height;


  // set the current window
  this->MakeCurrent();


  if (y1 < y2)
  {
    y_low = y1;
    y_hi  = y2;
  }
  else
  {
    y_low = y2;
    y_hi  = y1;
  }


  if (x1 < x2)
  {
    x_low = x1;
    x_hi  = x2;
  }
  else
  {
    x_low = x2;
    x_hi  = x1;
  }


  // if (front)
  // {
  //   glReadBuffer(static_cast<GLenum>(this->GetFrontLeftBuffer()));
  // }
  // else
  // {
  //   glReadBuffer(static_cast<GLenum>(this->GetBackLeftBuffer()));
  // }

  // width  = abs(x_hi - x_low) + 1;
  // height = abs(y_hi - y_low) + 1;

  // glDisable( GL_SCISSOR_TEST );

  // glReadPixels( x_low, y_low, width, height, GL_RGBA, GL_UNSIGNED_BYTE,
  //               data);

  // if (glGetError() != GL_NO_ERROR)
  // {
  //   return VTK_ERROR;
  // }
  // else
  // {
    return VTK_OK;
//  }

}


int vtkDirect3DRenderWindow::SetRGBACharPixelData(int x1,int y1,int x2,int y2,
                                                vtkUnsignedCharArray *data,
                                                int front, int blend)
{
  int     y_low, y_hi;
  int     x_low, x_hi;
  int     width, height;

  if (y1 < y2)
  {
    y_low = y1;
    y_hi  = y2;
  }
  else
  {
    y_low = y2;
    y_hi  = y1;
  }

  if (x1 < x2)
  {
    x_low = x1;
    x_hi  = x2;
  }
  else
  {
    x_low = x2;
    x_hi  = x1;
  }

  width  = abs(x_hi-x_low) + 1;
  height = abs(y_hi-y_low) + 1;

  int size = 4*width*height;
  if ( data->GetMaxId()+1 != size )
  {
    vtkErrorMacro("Buffer is of wrong size. It is " << data->GetMaxId()+1
                  << ", it should be: " << size);
    return VTK_ERROR;
  }

  return this->SetRGBACharPixelData(x1, y1, x2, y2, data->GetPointer(0),
                                    front, blend);

}

int vtkDirect3DRenderWindow::SetRGBACharPixelData(int x1, int y1, int x2,
                                                int y2, unsigned char *data,
                                                int front, int blend)
{
  // set the current window
  this->MakeCurrent();



//   GLint buffer;
// #ifdef GL_DRAW_BUFFER
//   glGetIntegerv(GL_DRAW_BUFFER, &buffer);
// #endif

//   if (front)
//   {
//     glDrawBuffer(this->GetFrontBuffer());
//   }
//   else
//   {
//     glDrawBuffer(this->GetBackBuffer());
//   }


//   // Disable writing on the z-buffer.
//   glDepthMask(GL_FALSE);
//   glDisable(GL_DEPTH_TEST);

//   if (!blend)
//   {
//     glDisable(GL_BLEND);
//     this->DrawPixels(x1,y1,x2,y2,4, VTK_UNSIGNED_CHAR, data);
//     glEnable(GL_BLEND);
//   }
//   else
//   {
//     this->DrawPixels(x1,y1,x2,y2,4, VTK_UNSIGNED_CHAR, data);
//   }

//   // Renenable writing on the z-buffer.
//   glDepthMask(GL_TRUE);
//   glEnable(GL_DEPTH_TEST);

//   glDrawBuffer(buffer);

//   if (glGetError() != GL_NO_ERROR)
//   {
//     return VTK_ERROR;
//   }
//   else
//   {
    return VTK_OK;
//  }
}


int vtkDirect3DRenderWindow::GetZbufferData( int x1, int y1, int x2, int y2,
                                           float* z_data )
{
  int             y_low;
  int             x_low;
  int             width, height;

  // set the current window
  this->MakeCurrent();

  if (y1 < y2)
  {
    y_low = y1;
  }
  else
  {
    y_low = y2;
  }

  if (x1 < x2)
  {
    x_low = x1;
  }
  else
  {
    x_low = x2;
  }

  width =  abs(x2 - x1)+1;
  height = abs(y2 - y1)+1;

  // // Turn of texturing in case it is on - some drivers have a problem
  // // getting / setting pixels with texturing enabled.
  // glDisable( GL_SCISSOR_TEST );
  // glPixelStorei( GL_PACK_ALIGNMENT, 1 );

  // glReadPixels( x_low, y_low,
  //               width, height,
  //               GL_DEPTH_COMPONENT, GL_FLOAT,
  //               z_data );

  // if (glGetError() != GL_NO_ERROR)
  // {
  //   return VTK_ERROR;
  // }
  // else
  // {
    return VTK_OK;
  // }
}

float *vtkDirect3DRenderWindow::GetZbufferData( int x1, int y1, int x2, int y2  )
{
  float           *z_data;

  int             width, height;
  width =  abs(x2 - x1)+1;
  height = abs(y2 - y1)+1;

  z_data = new float[width*height];
  this->GetZbufferData(x1, y1, x2, y2, z_data);

  return z_data;
}

int vtkDirect3DRenderWindow::GetZbufferData( int x1, int y1, int x2, int y2,
                                           vtkFloatArray *buffer )
{
  int  width, height;
  width =  abs(x2 - x1)+1;
  height = abs(y2 - y1)+1;
  int size = width*height;
  if ( buffer->GetMaxId()+1 != size)
  {
    vtkDebugMacro("Resizing array.");
    buffer->SetNumberOfComponents(1);
    buffer->SetNumberOfValues(size);
  }
  return this->GetZbufferData(x1, y1, x2, y2, buffer->GetPointer(0));
}

int vtkDirect3DRenderWindow::SetZbufferData( int x1, int y1, int x2, int y2,
                                           vtkFloatArray *buffer )
{
  int width, height;
  width =  abs(x2 - x1)+1;
  height = abs(y2 - y1)+1;
  int size = width*height;
  if ( buffer->GetMaxId()+1 != size )
  {
    vtkErrorMacro("Buffer is of wrong size.");
    return VTK_ERROR;
  }
  return this->SetZbufferData(x1, y1, x2, y2, buffer->GetPointer(0));
}

int vtkDirect3DRenderWindow::SetZbufferData( int x1, int y1,
                                           int x2, int y2,
                                           float *buffer )
{
//  glDrawBuffer(this->GetBackBuffer());
  // glDisable( GL_SCISSOR_TEST );
  // glEnable(GL_DEPTH_TEST);
  // glDepthFunc(GL_ALWAYS);
  // glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
  // if (!this->DrawPixelsTextureObject)
  // {
  //   this->DrawPixelsTextureObject = vtkTextureObject::New();
  // }
  // else
  // {
  //   this->DrawPixelsTextureObject->ReleaseGraphicsResources(this);
  // }
  // this->DrawPixelsTextureObject->SetContext(this);
  // this->DrawPixelsTextureObject->CreateDepthFromRaw(x2-x1+1, y2-y1+1,
  //       vtkTextureObject::Float32, VTK_FLOAT, buffer);

  // // compile and bind it if needed
  // vtkShaderProgram *program =
  //   this->GetShaderCache()->ReadyShaderProgram(
  //     vtkTextureObjectVS,
  //     "//VTK::System::Dec\n"
  //     "varying vec2 tcoordVC;\n"
  //     "uniform sampler2D source;\n"
  //     "//VTK::Output::Dec\n"
  //     "void main(void) {\n"
  //     "  gl_FragDepth = texture2D(source,tcoordVC).r; }\n",
  //     "");
  // vtkDirect3DVertexArrayObject *VAO = vtkDirect3DVertexArrayObject::New();

  // // bind and activate this texture
  // this->DrawPixelsTextureObject->Activate();
  // program->SetUniformi("source",
  //   this->DrawPixelsTextureObject->GetTextureUnit());

  // this->DrawPixelsTextureObject->CopyToFrameBuffer(
  //   0, 0, x2-x1, y2-y1,
  //   x1, y1, x2, y2,
  //   this->GetSize()[0], this->GetSize()[1],
  //   program, VAO);
  // this->DrawPixelsTextureObject->Deactivate();
  // VAO->Delete();
  // glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
  // glDepthFunc(GL_LEQUAL);

  return VTK_OK;
}
