//
//  KRSprite.cpp
//  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.
//


#include "KREngine-common.h"
#include "KRSprite.h"

#include "KRNode.h"
#include "KRCamera.h"
#include "KRContext.h"

#include "KRPipelineManager.h"
#include "KRPipeline.h"
#include "KRDirectionalLight.h"
#include "KRSpotLight.h"
#include "KRPointLight.h"
#include "KRRenderPass.h"

using namespace hydra;

/* static */
void KRSprite::InitNodeInfo(KrNodeInfo* nodeInfo)
{
  KRNode::InitNodeInfo(nodeInfo);
  nodeInfo->sprite.alpha = decltype(m_spriteAlpha)::defaultVal;
  nodeInfo->sprite.texture = -1;
}

KRSprite::KRSprite(KRScene& scene, std::string name)
  : KRNode(scene, name)
{
}

KRSprite::~KRSprite()
{}

std::string KRSprite::getElementName()
{
  return "sprite";
}

tinyxml2::XMLElement* KRSprite::saveXML(tinyxml2::XMLNode* parent)
{
  tinyxml2::XMLElement* e = KRNode::saveXML(parent);
  m_spriteTexture.save(e);
  m_spriteAlpha.save(e);
  return e;
}

void KRSprite::loadXML(tinyxml2::XMLElement* e)
{
  KRNode::loadXML(e);

  m_spriteTexture.load(e);
  m_spriteAlpha.load(e);
}

void KRSprite::setSpriteTexture(std::string sprite_texture)
{
  m_spriteTexture.val.set(sprite_texture);
}

void KRSprite::setSpriteAlpha(float alpha)
{
  m_spriteAlpha = alpha;
}

float KRSprite::getSpriteAlpha() const
{
  return m_spriteAlpha;
}

AABB KRSprite::getBounds()
{
  return AABB::Create(-Vector3::One() * 0.5f, Vector3::One() * 0.5f, getModelMatrix());
}

void KRSprite::getResourceBindings(std::list<KRResourceBinding*>& bindings)
{
  KRNode::getResourceBindings(bindings);

  bindings.push_back(&m_spriteTexture.val);
}

void KRSprite::render(RenderInfo& ri)
{
  KRNode::render(ri);
  ri.reflectedObjects.push_back(this);

  if (ri.renderPass->getType() == RenderPassType::RENDER_PASS_ADDITIVE_PARTICLES) {
    if (m_spriteAlpha > 0.0f) {
      // TODO - Sprites are currently additive only.  Need to expose this and allow for multiple blending modes

      KRMeshManager::KRVBOData& vertices = m_pContext->getMeshManager()->KRENGINE_VBO_DATA_2D_SQUARE_VERTICES;

      // Render light sprite on transparency pass
      PipelineInfo info{};
      std::string shader_name("sprite");
      info.shader_name = &shader_name;
      info.pCamera = ri.camera;
      info.point_lights = &ri.point_lights;
      info.directional_lights = &ri.directional_lights;
      info.spot_lights = &ri.spot_lights;
      info.renderPass = ri.renderPass;
      info.rasterMode = RasterMode::kAdditive;
      info.cullMode = CullMode::kCullNone;
      info.layout = vertices.getLayout();

      KRPipeline* pShader = getContext().getPipelineManager()->getPipeline(*ri.surface, info);
      if (pShader && pShader->bind(ri, getModelMatrix())) {
        m_pContext->getMeshManager()->bindVBO(ri.commandBuffer, &vertices, 1.0f);
        vkCmdDraw(ri.commandBuffer, 4, 1, 0, 0);
      }
    }
  }
  ri.reflectedObjects.pop_back();
}

bool KRSprite::getShaderValue(const KRCamera* camera, ShaderValue value, float* output) const
{
  switch (value) {
    default:
      return KRNode::getShaderValue(camera, value, output);
  }
}

bool KRSprite::getImageBinding(const std::string& name, const KRTextureBinding** binding, KRSampler** sample) const
{
  if (name == "spriteTexture") {
    *binding = &m_spriteTexture.val;
    *sample = getContext().getSamplerManager()->DEFAULT_CLAMPED_SAMPLER;
    return true;
  } else {
    return KRNode::getImageBinding(name, binding, sample);
  }
}
