// Copyright 2024 SquareBlock Inc. All Rights Reserved.
// Author: tianlei.richard@qq.com (tianlei.richard)

#include "phone_material.h"

#include "spdlog/spdlog.h"
#include <opencv2/highgui/highgui.hpp>

PhoneMaterial::PhoneMaterial(
    const std::unordered_map<Texture::TextureType, std::shared_ptr<Texture>>
        &textures,
    const std::shared_ptr<Lamp> &lamp)
    : point_lamp_(lamp), textures_(textures), Ka(cv::Vec3b{16, 16, 16}),
      Ks(cv::Vec3b(64, 64, 64)),
      specular_attenuation_factor_(defaultAttenuationFactor) {
  spdlog::info("Specular attenuation factor: {}", specular_attenuation_factor_);
}

cv::Vec3b PhoneMaterial::shade(const Vertex &shading_point,
                               const Point3d &view_point) {
  cv::Vec3b shading_res{0, 0, 0};

  // Diffuse
  assert(textures_.find(Texture::DiffuseMap) != textures_.end());
  const auto &diffuse_map = textures_[Texture::DiffuseMap];
  const auto &Kd = diffuse_map->sample(shading_point.texture_coordinate);

  const auto &point2light_vector =
      (point_lamp_->position - shading_point.position).normalized();
  const auto &point2light_distance =
      (shading_point.position - point_lamp_->position).norm();
  const auto &current_intensity =
      point_lamp_->intensity / (point2light_distance * point2light_distance);

  const auto diffuse_coeff =
      current_intensity *
      std::max(shading_point.normal.dot(point2light_vector), 0.);
  cv::Vec3b diffuse_color = Kd * diffuse_coeff;
  shading_res += diffuse_color;

  // Specular
  const auto &point2view_vector =
      (view_point - shading_point.position).normalized();
  const auto &half_vector =
      (point2light_vector + point2view_vector).normalized();
  const auto specular_coeff =
      current_intensity *
      std::max(std::pow(shading_point.normal.dot(half_vector),
                        specular_attenuation_factor_),
               0.);
  cv::Vec3b specular_color = Ks * specular_coeff;
  shading_res += specular_color;

  // Ambient
  cv::Vec3b ambient_color = Ka;
  shading_res += ambient_color;

  return shading_res;
}
