// __author__ = Juan Carlos De Abreu (jcabreur@gmail.com)

#include "DataMaterial.h"
#include "../DataTypes.h"
#include "../Checks.h"
#include "../../Material/Material.h"
#include "../DataStructures/DataCallFunction.h"

BEGIN_TEG_NAMESPACE();

// DataMaterial constructor.
DataMaterial::DataMaterial(const SmartPointer<Material>& value_)
    : IData(DATA_MATERIAL), value(value_) { }

// DataMaterial destructor.
DataMaterial::~DataMaterial() {
  value = NULL;
}

void DataMaterial::print(void) {

}

// Method to get the value.
SmartPointer<Material> DataMaterial::getValue(void) const {
  return value;
}

// Function to cast an IData to DataMaterial.
SmartPointer<DataMaterial> castToDataMaterial(const SmartPointer<IData>& d) {
  if (d == NULL) return NULL;
  if (d->type != IData::DATA_MATERIAL) return NULL;
  return SmartPointer<DataMaterial> (dynamic_cast<DataMaterial*> ((IData*) d));
}

#define CHECK_MATERIAL_METHOD_PARAM(nParamNeeded, methodExpected) \
    checkMethodParameters( \
        nParamNeeded, \
        nParams, \
        _T("Material"), \
        methodExpected, \
        method->name.c_str())

#define CHECK_MATERIAL_METHOD_TYPE(param, paramIndex, typeExpected) \
    checkMethodParameterType( \
        param, \
        paramIndex, \
        typeExpected, \
        _T("Material"), \
        method->name.c_str())

// Method to get a method.
SmartPointer<IData> DataMaterial::getMethod(
    SmartPointer<IData> elem,
	std::map<std::string, SmartPointer<IData>>& variables) {
  // Check if is a valid pointer.
  if (value == NULL) return NULL;

  // Check if the elem is a method call.
  if (elem == NULL || elem->type != IData::DATA_CALL_FUNCTION) return NULL;

  const SmartPointer<DataCallFunction> method = castToDataCallFunction(elem);
  const int nParams = static_cast<int> (method->param.size());
  std::vector<SmartPointer<IData>> params(nParams);
  for (int i = 0; i < nParams; ++i) params[i] = method->param[i]->resolve(variables);

  if (CHECK_MATERIAL_METHOD_PARAM(0, _T("getAplha"))) {
      return newData(value->alpha);
  }
  if (CHECK_MATERIAL_METHOD_PARAM(0, _T("getShininess"))) {
      return newData(value->shininess);
  }
  if (CHECK_MATERIAL_METHOD_PARAM(0, _T("getDiffuseX"))) {
      return newData(value->diffuse.x);
  }
  if (CHECK_MATERIAL_METHOD_PARAM(0, _T("getDiffuseY"))) {
      return newData(value->diffuse.y);
  }
  if (CHECK_MATERIAL_METHOD_PARAM(0, _T("getDiffuseZ"))) {
      return newData(value->diffuse.z);
  }
  if (CHECK_MATERIAL_METHOD_PARAM(0, _T("getAmbientX"))) {
      return newData(value->diffuse.x);
  }
  if (CHECK_MATERIAL_METHOD_PARAM(0, _T("getAmbientY"))) {
      return newData(value->diffuse.y);
  }
  if (CHECK_MATERIAL_METHOD_PARAM(0, _T("getAmbientZ"))) {
      return newData(value->diffuse.z);
  }
  if (CHECK_MATERIAL_METHOD_PARAM(0, _T("getSpecularX"))) {
      return newData(value->specular.x);
  }
  if (CHECK_MATERIAL_METHOD_PARAM(0, _T("getSpecularY"))) {
      return newData(value->specular.y);
  }
  if (CHECK_MATERIAL_METHOD_PARAM(0, _T("getSpecularZ"))) {
      return newData(value->specular.z);
  }
  if (CHECK_MATERIAL_METHOD_PARAM(0, _T("getEmissionX"))) {
      return newData(value->emission.x);
  }
  if (CHECK_MATERIAL_METHOD_PARAM(0, _T("getEmissionY"))) {
      return newData(value->emission.y);
  }
  if (CHECK_MATERIAL_METHOD_PARAM(0, _T("getEmissionZ"))) {
      return newData(value->emission.z);
  }
  if (CHECK_MATERIAL_METHOD_PARAM(1, _T("setAlpha"))) {
    const SmartPointer<DataFloat> p0 = castToFloat(params[0]);
	if (CHECK_MATERIAL_METHOD_TYPE(p0, 1, _T("float"))) {
      value->setAlpha(p0->value);
      return newData(p0->value);
    }
  }
  if (CHECK_MATERIAL_METHOD_PARAM(1, _T("setShininess"))) {
    const SmartPointer<DataFloat> p0 = castToFloat(params[0]);
	if (CHECK_MATERIAL_METHOD_TYPE(p0, 1, _T("float")))
      return newData(value->shininess = p0->value);
  }
  if (CHECK_MATERIAL_METHOD_PARAM(1, _T("setDiffuseX"))) {
    const SmartPointer<DataFloat> p0 = castToFloat(params[0]);
	if (CHECK_MATERIAL_METHOD_TYPE(p0, 1, _T("float")))
      return newData(value->diffuse.x = p0->value);
  }
  if (CHECK_MATERIAL_METHOD_PARAM(1, _T("setDiffuseY"))) {
    const SmartPointer<DataFloat> p0 = castToFloat(params[0]);
	if (CHECK_MATERIAL_METHOD_TYPE(p0, 1, _T("float")))
      return newData(value->diffuse.y = p0->value);
  }
  if (CHECK_MATERIAL_METHOD_PARAM(1, _T("setDiffuseZ"))) {
    const SmartPointer<DataFloat> p0 = castToFloat(params[0]);
	if (CHECK_MATERIAL_METHOD_TYPE(p0, 1, _T("float")))
      return newData(value->diffuse.z = p0->value);
  }
  if (CHECK_MATERIAL_METHOD_PARAM(1, _T("setAmbientX"))) {
    const SmartPointer<DataFloat> p0 = castToFloat(params[0]);
	if (CHECK_MATERIAL_METHOD_TYPE(p0, 1, _T("float")))
      return newData(value->ambient.x = p0->value);
  }
  if (CHECK_MATERIAL_METHOD_PARAM(1, _T("setAmbientY"))) {
    const SmartPointer<DataFloat> p0 = castToFloat(params[0]);
	if (CHECK_MATERIAL_METHOD_TYPE(p0, 1, _T("float")))
      return newData(value->ambient.y = p0->value);
  }
  if (CHECK_MATERIAL_METHOD_PARAM(1, _T("setAmbientZ"))) {
    const SmartPointer<DataFloat> p0 = castToFloat(params[0]);
	if (CHECK_MATERIAL_METHOD_TYPE(p0, 1, _T("float")))
      return newData(value->ambient.z = p0->value);
  }
  if (CHECK_MATERIAL_METHOD_PARAM(1, _T("setSpecularX"))) {
    const SmartPointer<DataFloat> p0 = castToFloat(params[0]);
	if (CHECK_MATERIAL_METHOD_TYPE(p0, 1, _T("float")))
      return newData(value->specular.x = p0->value);
  }
  if (CHECK_MATERIAL_METHOD_PARAM(1, _T("setSpecularY"))) {
    const SmartPointer<DataFloat> p0 = castToFloat(params[0]);
	if (CHECK_MATERIAL_METHOD_TYPE(p0, 1, _T("float")))
      return newData(value->specular.y = p0->value);
  }
  if (CHECK_MATERIAL_METHOD_PARAM(1, _T("setSpecularZ"))) {
    const SmartPointer<DataFloat> p0 = castToFloat(params[0]);
	if (CHECK_MATERIAL_METHOD_TYPE(p0, 1, _T("float")))
      return newData(value->specular.z = p0->value);
  }
  if (CHECK_MATERIAL_METHOD_PARAM(1, _T("setEmissionX"))) {
    const SmartPointer<DataFloat> p0 = castToFloat(params[0]);
	if (CHECK_MATERIAL_METHOD_TYPE(p0, 1, _T("float")))
      return newData(value->emission.x = p0->value);
  }
  if (CHECK_MATERIAL_METHOD_PARAM(1, _T("setEmissionY"))) {
    const SmartPointer<DataFloat> p0 = castToFloat(params[0]);
	if (CHECK_MATERIAL_METHOD_TYPE(p0, 1, _T("float")))
      return newData(value->emission.y = p0->value);
  }
  if (CHECK_MATERIAL_METHOD_PARAM(1, _T("setEmissionZ"))) {
    const SmartPointer<DataFloat> p0 = castToFloat(params[0]);
	if (CHECK_MATERIAL_METHOD_TYPE(p0, 1, _T("float")))
      return newData(value->emission.z = p0->value);
  }
  return NULL;
}

#undef CHECK_MATERIAL_METHOD_PARAM
#undef CHECK_MATERIAL_METHOD_TYPE

END_TEG_NAMESPACE();
