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

#include "DataVector.h"

#include "DataInt.h"
#include "../Checks.h"
#include "../DataStructures.h"

BEGIN_TEG_NAMESPACE();

void DataVector::freePtrs(void) {
  const int nElem = static_cast<int> (value.size());
  for (int i = 0; i < nElem; ++i) {
    if (value[i] == NULL) continue;
	if (value[i]->type == IData::DATA_VECTOR) {
      castToDataVector(value[i])->freePtrs();
	}
  }
  value.clear();
}

SmartPointer<IData> DataVector::resolve(std::map<std::string, SmartPointer<IData>>& variables) {
	SmartPointer<DataVector> tmp = new DataVector();
	const int nElem = static_cast<int> (value.size());
	for (int i = 0; i < nElem; ++i) {
		if (value[i] != NULL) tmp->value.push_back(value[i]->resolve(variables));
	}
	return castToDATA(tmp);
}

// for the "." operator
SmartPointer<IData> DataVector::getAttribute(std::string attrName) { 
	//if (attrName == "size") return newData(static_cast<int> (value.size()));
	return NULL;
}

// for the "[]" operator 
SmartPointer<IData> DataVector::getElement(SmartPointer<IData> elem) { 
  return NULL;
}

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

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


SmartPointer<IData> DataVector::getMethod(
    SmartPointer<IData> elem,
	std::map<std::string, SmartPointer<IData>>& variables) {

  // 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_VECTOR_METHOD_PARAM(0, _T("size"))) {
    return newData(static_cast<int> (value.size()));
  }
  if (CHECK_VECTOR_METHOD_PARAM(1, _T("get"))) {
    const SmartPointer<DataInt> p0 = castToInt(params[0]);
	if (!CHECK_VECTOR_METHOD_TYPE(p0, 1, _T("int"))) return NULL;
    const int nElem = static_cast<int> (value.size());
    if (nElem == 0) {
      PRINT_LOG(TYPE_ERROR, _T("error: accessing empty list"));
      return NULL;
    }
	if (p0->value < 0) return value[(nElem + (p0->value % nElem)) % nElem];
    return value[p0->value % nElem];
  }
  if (CHECK_VECTOR_METHOD_PARAM(2, _T("set"))) {
    const SmartPointer<DataInt> p0 = castToInt(params[0]);
	if (!CHECK_VECTOR_METHOD_TYPE(p0, 1, _T("int"))) return NULL;
    const int nElem = static_cast<int> (value.size());
    if (nElem == 0) {
      PRINT_LOG(TYPE_ERROR, _T("error: setting an empty list"));
      return NULL;
    }
	if (p0->value < 0) value[(nElem + (p0->value % nElem)) % nElem] = params[1];
    else value[p0->value % nElem] = params[1];
    return NULL;
  }
  if (CHECK_VECTOR_METHOD_PARAM(1, _T("add"))) {
    if (!CHECK_VECTOR_METHOD_TYPE(params[0], 1, _T("any"))) return NULL;
    value.push_back(params[0]);
	return NULL;
  }
  if (CHECK_VECTOR_METHOD_PARAM(1, _T("remove"))) {
    const SmartPointer<DataInt> p0 = castToInt(params[0]);
    if (!CHECK_VECTOR_METHOD_TYPE(p0, 1, _T("int"))) return NULL;
    const int nElem = static_cast<int> (value.size());
    if (nElem == 0) {
      PRINT_LOG(TYPE_ERROR, _T("error: remove element in an empty list"));
      return NULL;
    }
	if (p0->value < 0) value.erase(value.begin() + (nElem + (p0->value % nElem)) % nElem);
	else value.erase(value.begin() + p0->value % nElem);
	return NULL;
  }
  return NULL;

}

#undef CHECK_VECTOR_METHOD_PARAM
#undef CHECK_VECTOR_METHOD_TYPE

END_TEG_NAMESPACE();
