/*
 * Copyright (c) Advanced Micro Devices, Inc., or its affiliates.
 *
 * SPDX-License-Identifier: MIT
 */

#ifndef SAMPLER_HPP_
#define SAMPLER_HPP_

#include "top.hpp"
#include "platform/object.hpp"
#include "device/device.hpp"

namespace amd {

//! Abstraction layer sampler class
class Sampler : public RuntimeObject {
 public:
  typedef std::unordered_map<Device const*, device::Sampler*> DeviceSamplers;

  //! \note the sampler states must match the compiler's defines.
  //! See amd_ocl_sys_predef.c
  enum State {
    StateNormalizedCoordsFalse = 0x00,
    StateNormalizedCoordsTrue = 0x01,
    StateNormalizedCoordsMask = (StateNormalizedCoordsFalse | StateNormalizedCoordsTrue),
    StateFilterNearest = 0x10,
    StateFilterLinear = 0x20,
    StateFilterMask = (StateFilterNearest | StateFilterLinear)
  };

 private:
  Context& context_;               //!< OpenCL context associated with this sampler
  uint32_t state_;                 //!< Sampler state
  uint mipFilter_;                 //!< mip filter
  float minLod_;                   //!< min level of detail
  float maxLod_;                   //!< max level of detail
  DeviceSamplers deviceSamplers_;  //!< Container for the device samplers
  uint addressMode_[3];            //!< address modes in X, Y and Z

 public:
  Sampler(Context& context,    //!< context for OCL
          bool normCoords,     //!< normalized coordinates
          uint addrMode,       //!< adressing mode
          uint filterMode,     //!< filter mode
          uint mipFilterMode,  //!< mip filter mode
          float minLod,        //!< min level of detail
          float maxLod         //!< max level of detail
          )
      : context_(context),
        mipFilter_(mipFilterMode),
        minLod_(minLod),
        maxLod_(maxLod) {  // Packs the sampler state into uint32_t for kernel execution
    state_ = 0;
    for (int i = 0; i < 3; i++) addressMode_[i] = addrMode;

    // Set normalized state
    if (normCoords) {
      state_ |= StateNormalizedCoordsTrue;
    } else {
      state_ |= StateNormalizedCoordsFalse;
    }

    // Program the sampler filter mode
    if (filterMode == CL_FILTER_LINEAR) {
      state_ |= StateFilterLinear;
    } else {
      state_ |= StateFilterNearest;
    }
  }

  Sampler(Context& context,        //!< context for Hip
          bool normCoords,         //!< normalized coordinates
          const uint addrMode[3],  //!< adressing modes in X, Y and Z directions
          uint filterMode,         //!< filter mode
          uint mipFilterMode,      //!< mip filter mode
          float minLod,            //!< min level of detail
          float maxLod             //!< max level of detail
          )
      : Sampler(context, normCoords, addrMode[0], filterMode, mipFilterMode, minLod, maxLod) {
    addressMode_[1] = addrMode[1];
    addressMode_[2] = addrMode[2];
  }

  virtual ~Sampler() {
    for (const auto& it : deviceSamplers_) {
      delete it.second;
    }
  }

  bool create() {
    for (uint i = 0; i < context_.devices().size(); ++i) {
      device::Sampler* sampler = NULL;
      Device* dev = context_.devices()[i];
      if (!dev->createSampler(*this, &sampler)) {
        ClPrint(amd::LOG_DETAIL_DEBUG, amd::LOG_RESOURCE,
                 "Sampler creation failed for device: 0x%x \n", dev);
        return false;
      }
      deviceSamplers_[dev] = sampler;
    }
    return true;
  }

  device::Sampler* getDeviceSampler(const Device& dev) const {
    auto it = deviceSamplers_.find(&dev);
    if (it != deviceSamplers_.end()) {
      return it->second;
    }
    return NULL;
  }

  //! Accessor functions
  Context& context() const { return context_; }
  uint32_t state() const { return state_; }
  uint mipFilter() const { return mipFilter_; }
  float minLod() const { return minLod_; }
  float maxLod() const { return maxLod_; }
  const uint* addessMode() const { return addressMode_; }
  bool normalizedCoords() const { return (state_ & StateNormalizedCoordsTrue) ? true : false; }

  uint inline addressingMode(const int index = 0) const { return addressMode_[index]; }

  uint filterMode() const {
    return ((state_ & StateFilterMask) == StateFilterNearest) ? CL_FILTER_NEAREST
                                                              : CL_FILTER_LINEAR;
  }

  //! RTTI internal implementation
  virtual ObjectType objectType() const { return ObjectTypeSampler; }
};

}  // namespace amd

#endif /*SAMPLER_HPP_*/
