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

#include "DataCamera.h"
#include "../../Camera/Camera.h"
#include "../../Scene/scene_inl.h"
#include "DataVector.h"
#include "DataFloat.h"
#include "../Checks.h"

BEGIN_TEG_NAMESPACE();

// DataCamera constructor.
DataCamera::DataCamera(const SmartPointer<Camera>& value_)
    : IData(DATA_CAMERA), value(value_) { }

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

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

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


// Method to print the values of the camera.
void DataCamera::print() { }

// Method to get a method.
SmartPointer<IData> DataCamera::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_CAMERA_METHOD_PARAM(0, _T("setActive"))) {
	Scene::GetInstance().set_camera(value); return NULL;
  }
  if (CHECK_CAMERA_METHOD_PARAM(1, _T("moveFordward"))) {
    const SmartPointer<DataFloat> p0 = castToFloat(params[0]);
	if (CHECK_CAMERA_METHOD_TYPE(p0, 1, _T("float"))) {
	  value->frameInterval = p0->value; value->move(UP); return NULL;
	}
  }
  if (CHECK_CAMERA_METHOD_PARAM(1, _T("moveBackward"))) {
    const SmartPointer<DataFloat> p0 = castToFloat(params[0]);
	if (CHECK_CAMERA_METHOD_TYPE(p0, 1, _T("float"))) {
	  value->frameInterval = p0->value; value->move(DOWN); return NULL;
	}
  }
  if (CHECK_CAMERA_METHOD_PARAM(1, _T("moveLeft"))) {
    const SmartPointer<DataFloat> p0 = castToFloat(params[0]);
	if (CHECK_CAMERA_METHOD_TYPE(p0, 1, _T("float"))) {
	  value->frameInterval = p0->value; value->StrafeCam(STRAFE_LEFT); return NULL;
	}
  }
  if (CHECK_CAMERA_METHOD_PARAM(1, _T("moveRight"))) {
    const SmartPointer<DataFloat> p0 = castToFloat(params[0]);
	if (CHECK_CAMERA_METHOD_TYPE(p0, 1, _T("float"))) {	
      value->frameInterval = p0->value; value->StrafeCam(STRAFE_RIGHT); return NULL;
	}
  }

  if (CHECK_CAMERA_METHOD_PARAM(1, _T("rotateX"))) {
    const SmartPointer<DataFloat> p0 = castToFloat(params[0]);
    if (CHECK_CAMERA_METHOD_TYPE(p0, 1, _T("float")))
      value->rotateX(p0->value); return NULL;
  }
  if (CHECK_CAMERA_METHOD_PARAM(1, _T("rotateY"))) {
    const SmartPointer<DataFloat> p0 = castToFloat(params[0]);
    if (CHECK_CAMERA_METHOD_TYPE(p0, 1, _T("float")))
      value->rotateY(p0->value); return NULL;
  }

  if (CHECK_CAMERA_METHOD_PARAM(3, _T("setPos"))) {
	const SmartPointer<DataFloat> p0 = castToFloat(params[0]);
	const SmartPointer<DataFloat> p1 = castToFloat(params[1]);
	const SmartPointer<DataFloat> p2 = castToFloat(params[2]);
	if (CHECK_CAMERA_METHOD_TYPE(p0, 1, _T("float")) &&
        CHECK_CAMERA_METHOD_TYPE(p1, 2, _T("float")) &&
        CHECK_CAMERA_METHOD_TYPE(p2, 3, _T("float")))
      value->pos.set(p0->value, p1->value, p2->value);
  }
  if (CHECK_CAMERA_METHOD_PARAM(3, _T("setUp"))) {
	const SmartPointer<DataFloat> p0 = castToFloat(params[0]);
	const SmartPointer<DataFloat> p1 = castToFloat(params[1]);
	const SmartPointer<DataFloat> p2 = castToFloat(params[2]);
	if (CHECK_CAMERA_METHOD_TYPE(p0, 1, _T("float")) &&
        CHECK_CAMERA_METHOD_TYPE(p1, 2, _T("float")) &&
        CHECK_CAMERA_METHOD_TYPE(p2, 3, _T("float")))
      value->up.set(p0->value, p1->value, p2->value);
  }
  if (CHECK_CAMERA_METHOD_PARAM(3, _T("setView"))) {
	const SmartPointer<DataFloat> p0 = castToFloat(params[0]);
	const SmartPointer<DataFloat> p1 = castToFloat(params[1]);
	const SmartPointer<DataFloat> p2 = castToFloat(params[2]);
	if (CHECK_CAMERA_METHOD_TYPE(p0, 1, _T("float")) &&
        CHECK_CAMERA_METHOD_TYPE(p1, 2, _T("float")) &&
        CHECK_CAMERA_METHOD_TYPE(p2, 3, _T("float")))
      value->view.set(p0->value, p1->value, p2->value);
  }
  if (CHECK_CAMERA_METHOD_PARAM(0, _T("getPos"))) {
    SmartPointer<DataVector> pos = new DataVector();
    pos->value.push_back(newData(value->pos.x));
    pos->value.push_back(newData(value->pos.y));
    pos->value.push_back(newData(value->pos.z));
    return castToDATA(pos);
  }
  if (CHECK_CAMERA_METHOD_PARAM(0, _T("getUp"))) {
    SmartPointer<DataVector> up = new DataVector();
    up->value.push_back(newData(value->up.x));
    up->value.push_back(newData(value->up.y));
    up->value.push_back(newData(value->up.z));
    return castToDATA(up);
  }
  if (CHECK_CAMERA_METHOD_PARAM(0, _T("getView"))) {
    SmartPointer<DataVector> view = new DataVector();
    view->value.push_back(newData(value->view.x));
    view->value.push_back(newData(value->view.y));
    view->value.push_back(newData(value->view.z));
    return castToDATA(view);
  }

  return NULL;
}

#undef CHECK_CAMERA_METHOD_PARAM
#undef CHECK_CAMERA_METHOD_TYPE

END_TEG_NAMESPACE();
