/* SPDX-FileCopyrightText: 2023 Blender Authors
 *
 * SPDX-License-Identifier: GPL-2.0-or-later */

#pragma once

/** \file
 * \ingroup freestyle
 * \brief Class used to handle materials.
 */

#include "../system/FreestyleConfig.h"

#include "MEM_guardedalloc.h"

namespace Freestyle {

/** Class defining a material */
class FrsMaterial {
 public:
  /** Default constructor */
  inline FrsMaterial();

  /** Builds a Material from its line, diffuse, ambient, specular, emissive
   *  colors, a shininess coefficient and line color priority.
   *    \param iLine:
   *      A 4 element float-array containing the line color.
   *    \param iDiffuse:
   *      A 4 element float-array containing the diffuse color.
   *    \param iAmbiant:
   *      A 4 element float-array containing the ambient color.
   *    \param iSpecular:
   *      A 4 element float-array containing the specular color.
   *    \param iEmission:
   *      A 4 element float-array containing the emissive color.
   *    \param iShininess:
   *      The shininess coefficient.
   *    \param iPriority:
   *      The line color priority.
   */
  inline FrsMaterial(const float *iLine,
                     const float *iDiffuse,
                     const float *iAmbiant,
                     const float *iSpecular,
                     const float *iEmission,
                     float iShininess,
                     int iPriority);

  /** Copy constructor */
  inline FrsMaterial(const FrsMaterial &m);

  /** Destructor */
  virtual ~FrsMaterial() {}

  /** Returns the line color as a 4 float array */
  inline const float *line() const
  {
    return Line;
  }

  /** Returns the red component of the line color */
  inline const float lineR() const
  {
    return Line[0];
  }

  /** Returns the green component of the line color */
  inline const float lineG() const
  {
    return Line[1];
  }

  /** Returns the blue component of the line color */
  inline const float lineB() const
  {
    return Line[2];
  }

  /** Returns the alpha component of the line color */
  inline const float lineA() const
  {
    return Line[3];
  }

  /** Returns the diffuse color as a 4 float array */
  inline const float *diffuse() const
  {
    return Diffuse;
  }

  /** Returns the red component of the diffuse color */
  inline const float diffuseR() const
  {
    return Diffuse[0];
  }

  /** Returns the green component of the diffuse color */
  inline const float diffuseG() const
  {
    return Diffuse[1];
  }

  /** Returns the blue component of the diffuse color */
  inline const float diffuseB() const
  {
    return Diffuse[2];
  }

  /** Returns the alpha component of the diffuse color */
  inline const float diffuseA() const
  {
    return Diffuse[3];
  }

  /** Returns the specular color as a 4 float array */
  inline const float *specular() const
  {
    return Specular;
  }

  /** Returns the red component of the specular color */
  inline const float specularR() const
  {
    return Specular[0];
  }

  /** Returns the green component of the specular color */
  inline const float specularG() const
  {
    return Specular[1];
  }

  /** Returns the blue component of the specular color */
  inline const float specularB() const
  {
    return Specular[2];
  }

  /** Returns the alpha component of the specular color */
  inline const float specularA() const
  {
    return Specular[3];
  }

  /** Returns the ambient color as a 4 float array */
  inline const float *ambient() const
  {
    return Ambient;
  }

  /** Returns the red component of the ambient color */
  inline const float ambientR() const
  {
    return Ambient[0];
  }

  /** Returns the green component of the ambient color */
  inline const float ambientG() const
  {
    return Ambient[1];
  }

  /** Returns the blue component of the ambient color */
  inline const float ambientB() const
  {
    return Ambient[2];
  }

  /** Returns the alpha component of the ambient color */
  inline const float ambientA() const
  {
    return Ambient[3];
  }

  /** Returns the emissive color as a 4 float array */
  inline const float *emission() const
  {
    return Emission;
  }

  /** Returns the red component of the emissive color */
  inline const float emissionR() const
  {
    return Emission[0];
  }

  /** Returns the green component of the emissive color */
  inline const float emissionG() const
  {
    return Emission[1];
  }

  /** Returns the blue component of the emissive color */
  inline const float emissionB() const
  {
    return Emission[2];
  }

  /** Returns the alpha component of the emissive color */
  inline const float emissionA() const
  {
    return Emission[3];
  }

  /** Returns the shininess coefficient */
  inline const float shininess() const
  {
    return Shininess;
  }

  /** Returns the line color priority */
  inline const int priority() const
  {
    return Priority;
  }

  /** Sets the line color.
   *    \param r:
   *      Red component
   *    \param g:
   *      Green component
   *    \param b:
   *     Blue component
   *    \param a:
   *      Alpha component
   */
  inline void setLine(float r, float g, float b, float a);

  /** Sets the diffuse color.
   *    \param r:
   *      Red component
   *    \param g:
   *      Green component
   *    \param b:
   *     Blue component
   *    \param a:
   *      Alpha component
   */
  inline void setDiffuse(float r, float g, float b, float a);

  /** Sets the specular color.
   *    \param r:
   *      Red component
   *    \param g:
   *      Green component
   *    \param b:
   *     Blue component
   *    \param a:
   *      Alpha component
   */
  inline void setSpecular(float r, float g, float b, float a);

  /** Sets the ambient color.
   *    \param r:
   *      Red component
   *    \param g:
   *      Green component
   *    \param b:
   *     Blue component
   *    \param a:
   *      Alpha component
   */
  inline void setAmbient(float r, float g, float b, float a);

  /** Sets the emissive color.
   *    \param r:
   *      Red component
   *    \param g:
   *      Green component
   *    \param b:
   *     Blue component
   *    \param a:
   *      Alpha component
   */
  inline void setEmission(float r, float g, float b, float a);

  /** Sets the shininess.
   *    \param s:
   *      Shininess
   */
  inline void setShininess(float s);

  /** Sets the line color priority.
   *    \param priority:
   *      Priority
   */
  inline void setPriority(int priority);

  /* operators */
  inline FrsMaterial &operator=(const FrsMaterial &m);
  inline bool operator!=(const FrsMaterial &m) const;
  inline bool operator==(const FrsMaterial &m) const;

 private:
  /** Material properties */
  float Line[4];
  float Diffuse[4];
  float Specular[4];
  float Ambient[4];
  float Emission[4];
  float Shininess;
  int Priority;

  MEM_CXX_CLASS_ALLOC_FUNCS("Freestyle:FrsMaterial")
};

FrsMaterial::FrsMaterial()
{
  Line[0] = Line[1] = Line[2] = 0.0f;
  Line[3] = 1.0f;

  Ambient[0] = Ambient[1] = Ambient[2] = 0.2f;
  Ambient[3] = 1.0f;

  Diffuse[0] = Diffuse[1] = Diffuse[2] = 0.8f;
  Diffuse[3] = 1.0f;

  Emission[0] = Emission[1] = Emission[2] = 0.0f;
  Emission[3] = 1.0f;

  Specular[0] = Specular[1] = Specular[2] = 0.0f;
  Specular[3] = 1.0f;

  Shininess = 0.0f;
  Priority = 0;
}

FrsMaterial::FrsMaterial(const float *iLine,
                         const float *iDiffuse,
                         const float *iAmbiant,
                         const float *iSpecular,
                         const float *iEmission,
                         const float iShininess,
                         const int iPriority)
{
  for (int i = 0; i < 4; i++) {
    Line[i] = iLine[i];
    Diffuse[i] = iDiffuse[i];
    Specular[i] = iSpecular[i];
    Ambient[i] = iAmbiant[i];
    Emission[i] = iEmission[i];
  }

  Shininess = iShininess;
  Priority = iPriority;
}

FrsMaterial::FrsMaterial(const FrsMaterial &m)
{
  for (int i = 0; i < 4; i++) {
    Line[i] = m.line()[i];
    Diffuse[i] = m.diffuse()[i];
    Specular[i] = m.specular()[i];
    Ambient[i] = m.ambient()[i];
    Emission[i] = m.emission()[i];
  }

  Shininess = m.shininess();
  Priority = m.priority();
}

void FrsMaterial::setLine(const float r, const float g, const float b, const float a)
{
  Line[0] = r;
  Line[1] = g;
  Line[2] = b;
  Line[3] = a;
}

void FrsMaterial::setDiffuse(const float r, const float g, const float b, const float a)
{
  Diffuse[0] = r;
  Diffuse[1] = g;
  Diffuse[2] = b;
  Diffuse[3] = a;
}

void FrsMaterial::setSpecular(const float r, const float g, const float b, const float a)
{
  Specular[0] = r;
  Specular[1] = g;
  Specular[2] = b;
  Specular[3] = a;
}

void FrsMaterial::setAmbient(const float r, const float g, const float b, const float a)
{
  Ambient[0] = r;
  Ambient[1] = g;
  Ambient[2] = b;
  Ambient[3] = a;
}

void FrsMaterial::setEmission(const float r, const float g, const float b, const float a)
{
  Emission[0] = r;
  Emission[1] = g;
  Emission[2] = b;
  Emission[3] = a;
}

void FrsMaterial::setShininess(const float s)
{
  Shininess = s;
}

void FrsMaterial::setPriority(const int priority)
{
  Priority = priority;
}

FrsMaterial &FrsMaterial::operator=(const FrsMaterial &m)
{
  for (int i = 0; i < 4; i++) {
    Line[i] = m.line()[i];
    Diffuse[i] = m.diffuse()[i];
    Specular[i] = m.specular()[i];
    Ambient[i] = m.ambient()[i];
    Emission[i] = m.emission()[i];
  }

  Shininess = m.shininess();
  Priority = m.priority();
  return *this;
}

bool FrsMaterial::operator!=(const FrsMaterial &m) const
{
  if (Shininess != m.shininess()) {
    return true;
  }
  if (Priority != m.priority()) {
    return true;
  }

  for (int i = 0; i < 4; i++) {
    if (Line[i] != m.line()[i]) {
      return true;
    }
    if (Diffuse[i] != m.diffuse()[i]) {
      return true;
    }
    if (Specular[i] != m.specular()[i]) {
      return true;
    }
    if (Ambient[i] != m.ambient()[i]) {
      return true;
    }
    if (Emission[i] != m.emission()[i]) {
      return true;
    }
  }

  return false;
}

bool FrsMaterial::operator==(const FrsMaterial &m) const
{
  return (!((*this) != m));
}

} /* namespace Freestyle */
