//============================================================================
//  Copyright (c) Kitware, Inc.
//  All rights reserved.
//  See LICENSE.txt 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.
//
//  Copyright 2019 National Technology & Engineering Solutions of Sandia, LLC (NTESS).
//  Copyright 2019 UT-Battelle, LLC.
//  Copyright 2019 Los Alamos National Security.
//
//  Under the terms of Contract DE-NA0003525 with NTESS,
//  the U.S. Government retains certain rights in this software.
//
//  Under the terms of Contract DE-AC52-06NA25396 with Los Alamos National
//  Laboratory (LANL), the U.S. Government retains certain rights in
//  this software.
//============================================================================

#include <vtkm/interop/TransferToOpenGL.h>

#include <vtkm/cont/ErrorBadValue.h>
#include <vtkm/cont/TryExecute.h>

#include <vtkm/interop/internal/TransferToOpenGL.h>

namespace
{

struct InteropStorage : VTKM_DEFAULT_STORAGE_LIST_TAG
{
};

struct DoTransferToOpenGL
{
  // We know the array type, but not what device to run on. Try for a specific device.
  template <typename ValueType, typename StorageTag>
  VTKM_CONT void operator()(const vtkm::cont::ArrayHandle<ValueType, StorageTag>& handle,
                            vtkm::interop::BufferState& state) const
  {
    vtkm::cont::DeviceAdapterId devId = handle.GetDeviceAdapterId();
    bool success = vtkm::cont::TryExecuteOnDevice(devId, DoTransferToOpenGL{}, handle, state);
    if (!success)
    {
      //Generally we are here because the devId is undefined
      //or for some reason the last executed device is now disabled
      success = vtkm::cont::TryExecute(DoTransferToOpenGL{}, handle, state);
    }
    if (!success)
    {
      throw vtkm::cont::ErrorBadValue("Unknown device id.");
    }
  }

  template <typename DeviceAdapterTag, typename ValueType, typename StorageTag>
  VTKM_CONT bool operator()(DeviceAdapterTag,
                            const vtkm::cont::ArrayHandle<ValueType, StorageTag>& handle,
                            vtkm::interop::BufferState& state) const
  {
    vtkm::interop::internal::TransferToOpenGL<ValueType, DeviceAdapterTag> toGL(state);
    toGL.Transfer(handle);
    return true;
  }
};

} // anonymous namespace

namespace vtkm
{
namespace interop
{

VTKM_CONT
void TransferToOpenGL(const vtkm::cont::VariantArrayHandleBase<InteropTypes>& array,
                      vtkm::interop::BufferState& state)
{
  array.CastAndCall(InteropStorage(), DoTransferToOpenGL{}, state);
}
}
} // namespace vtkm::interop
