//
//  KRTexture.h
//  Kraken Engine
//
//  Copyright 2026 Kearwood Gilbert. All rights reserved.
//
//  Redistribution and use in source and binary forms, with or without modification, are
//  permitted provided that the following conditions are met:
//
//  1. Redistributions of source code must retain the above copyright notice, this list of
//  conditions and the following disclaimer.
//
//  2. Redistributions in binary form must reproduce the above copyright notice, this list
//  of conditions and the following disclaimer in the documentation and/or other materials
//  provided with the distribution.
//
//  THIS SOFTWARE IS PROVIDED BY KEARWOOD GILBERT ''AS IS'' AND ANY EXPRESS OR IMPLIED
//  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
//  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL KEARWOOD GILBERT OR
//  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
//  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
//  SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
//  ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
//  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
//  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//  The views and conclusions contained in the software and documentation are those of the
//  authors and should not be interpreted as representing official policies, either expressed
//  or implied, of Kearwood Gilbert.
//

#pragma once

#include "KREngine-common.h"
#include "KRContextObject.h"
#include "resources/KRResource.h"

namespace mimir {
  class Block;
}
class KRCamera;
class KRDeviceManager;
class KRDevice;

class KRTexture : public KRResource
{
public:
  KRTexture(KRContext& context, std::string name);
  virtual ~KRTexture();

  void releaseHandles();
  long getMemSize();
  virtual long getReferencedMemSize();

  virtual long getMemRequiredForLod(int lod) = 0;
  long getMemRequiredForLodRange(int min_lod, int max_lod = 0xff);
  virtual void resize(int lod);

  long getLastFrameUsed();

  typedef enum
  {
    TEXTURE_USAGE_NONE = 0x00,
    TEXTURE_USAGE_UI = 0x01,
    TEXTURE_USAGE_SKY_CUBE = 0x02,
    TEXTURE_USAGE_LIGHT_MAP = 0x04,
    TEXTURE_USAGE_REFECTION_CUBE = 0x08,
    TEXTURE_USAGE_LIGHT_FLARE = 0x10,
    TEXTURE_USAGE_SHADOW_DEPTH = 0x20,
    TEXTURE_USAGE_PARTICLE = 0x40,
    TEXTURE_USAGE_SPRITE = 0x80,

    TEXTURE_USAGE_MATERIAL = 0x01000000,

    TEXTURE_USAGE_MATERIAL_BASE_COLOR = TEXTURE_USAGE_MATERIAL | 0x00,
    TEXTURE_USAGE_MATERIAL_NORMAL = TEXTURE_USAGE_MATERIAL | 0x01,
    TEXTURE_USAGE_MATERIAL_EMISSIVE = TEXTURE_USAGE_MATERIAL | 0x02,
    TEXTURE_USAGE_MATERIAL_OCCLUSION = TEXTURE_USAGE_MATERIAL | 0x04,
    TEXTURE_USAGE_MATERIAL_METALIC_ROUGHNESS = TEXTURE_USAGE_MATERIAL | 0x08,
    TEXTURE_USAGE_MATERIAL_ANISOTROPY = TEXTURE_USAGE_MATERIAL | 0x10,
    TEXTURE_USAGE_MATERIAL_CLEARCOAT = TEXTURE_USAGE_MATERIAL | 0x20,
    TEXTURE_USAGE_MATERIAL_CLEARCOAT_ROUGHNESS = TEXTURE_USAGE_MATERIAL | 0x40,
    TEXTURE_USAGE_MATERIAL_CLEARCOAT_NORMAL = TEXTURE_USAGE_MATERIAL | 0x80,
    TEXTURE_USAGE_MATERIAL_SPECULAR = TEXTURE_USAGE_MATERIAL | 0x100,
    TEXTURE_USAGE_MATERIAL_SPECULAR_COLOR = TEXTURE_USAGE_MATERIAL | 0x200,
    TEXTURE_USAGE_MATERIAL_THICKNESS = TEXTURE_USAGE_MATERIAL | 0x400,
    TEXTURE_USAGE_MATERIAL_TRANSMISSION = TEXTURE_USAGE_MATERIAL | 0x800,


  } texture_usage_t;

  float getStreamPriority();

  virtual void requestResidency(float lodCoverage, texture_usage_t textureUsage);
  void requestResidency(uint32_t usage, float lodCoverage = 0.0f) override;
  virtual bool isAnimated();

  virtual KRTexture* compress(bool premultiply_alpha = false);
  int getCurrentLodMaxDim();
  int getNewLod(); // For use by streamer only
  int getMaxMipMap();
  bool hasMipmaps();
  virtual int getFaceCount() const = 0;
  virtual VkFormat getFormat() const = 0;
  virtual hydra::Vector3i getDimensions() const = 0;
  int getLodCount() const;

  kraken_stream_level getStreamLevel();
  float getLastFrameLodCoverage() const;

  void _swapHandles();

  VkImageView getFullImageView(KrDeviceHandle device);
  VkImage getImage(KrDeviceHandle device);

protected:
  virtual bool createGPUTexture(int lod) = 0;
  void destroyHandles();
  void destroyNewHandles();

  struct TextureHandle
  {
    VkImage image;
    VkImageView fullImageView;
    KrDeviceHandle device;
    VmaAllocation allocation;

    void destroy(KRDeviceManager* deviceManager);
  };

  std::vector<TextureHandle> m_handles;
  std::vector<TextureHandle> m_newHandles;
  std::atomic_bool m_haveNewHandles;

  std::atomic_flag m_handle_lock;

  int m_current_lod;
  int m_new_lod;

  int m_lod_count;

  long m_last_frame_used;
  float m_last_frame_max_lod_coverage;
  texture_usage_t m_last_frame_usage;

  bool allocate(KRDevice& device, int target_lod, VkImageCreateFlags imageCreateFlags, VkMemoryPropertyFlags properties, VkImage* image, VmaAllocation* allocation
#if KRENGINE_DEBUG_GPU_LABELS  
  , const char* debug_label
#endif
  );

private:
  std::atomic<long> m_textureMemUsed;
  std::atomic<long> m_newTextureMemUsed;
};
