#include "tracejs.h"
#include "datapointjs.h"


namespace pappso
{


TraceJs::TraceJs(QObject *parent) : QObject(parent), m_trace()
{
}

TraceJs::TraceJs(const Trace &trace, QObject *parent) : QObject(parent), m_trace(trace)
{
}

int
TraceJs::getSize() const
{
  return m_trace.size();
}

void
TraceJs::append(double x, double y)
{
  m_trace.push_back(DataPoint(x, y));
  emit sizeChangedSignal();
}

void
TraceJs::append(const DataPointJs *data_point_js_p)
{
  if(data_point_js_p != nullptr)
    {
      m_trace.push_back(data_point_js_p->dataPoint());
      emit sizeChangedSignal();
    }
}

DataPointJs *
TraceJs::getPointAt(int index) const
{
  if(index < 0 || index >= static_cast<int>(m_trace.size()))
    {
      return nullptr;
    }

  // Simple version: create wrapper on the fly
  return new DataPointJs(m_trace[index], const_cast<TraceJs *>(this));
}


QVariantList
TraceJs::getTrace() const
{
  QVariantList list;

  for(const DataPoint &data_point : m_trace)
    {
      DataPointJs *data_point_js_p = new DataPointJs(data_point, const_cast<TraceJs *>(this));
      list.append(QVariant::fromValue(data_point_js_p));
    }

  return list;
}

void
TraceJs::initialize(const QVariantList &js_points)
{
  m_trace.clear();

  for(const QVariant &variant : js_points)
    {
      QObject *obj = variant.value<QObject *>();

      // The cast returns nullptr if the variant did not
      // convert to DataPointJs.
      if(auto data_point_js = qobject_cast<DataPointJs *>(obj))
        {
          DataPoint data_point = data_point_js->dataPoint();
          m_trace.push_back(data_point); // Extract the internal DataPoint
        }
      else
        {
          qWarning() << "Failed initializing Trace because list member is not a DataPointJs object:"
                     << obj;
        }
    }

  emit sizeChangedSignal();
}


void
TraceJs::initialize(const QVariantMap &map)
{
  m_trace.clear();
  m_trace.reserve(map.size());

  for(auto it = map.constBegin(); it != map.constEnd(); ++it)
    {
      bool key_ok, value_ok;
      double key   = it.key().toDouble(&key_ok);
      double value = it.value().toDouble(&value_ok);

      if(key_ok && value_ok)
        m_trace.emplace_back(key, value);
      else
        qWarning() << "Skipping invalid map entry at key:" << it.key();
    }

  emit sizeChangedSignal();
}

std::size_t
TraceJs::initialize(const QVariantList &x_values, const QVariantList &y_values)
{
  if(x_values.size() != y_values.size())
    qFatal() << "The two containers must have the same size.";

  m_trace.clear();
  m_trace.reserve(x_values.size());

  for(int iter = 0; iter < x_values.size(); ++iter)
    m_trace.push_back(DataPoint(x_values.at(iter).toDouble(), y_values.at(iter).toDouble()));

  emit sizeChangedSignal();

  return m_trace.size();
}

size_t
TraceJs::initialize(const QString &x_text, const QString &y_text)
{
  m_trace.initialize(x_text, y_text);

  emit sizeChangedSignal();

  return m_trace.size();
}

size_t
TraceJs::initialize(const QString &space_sep_text)
{
  m_trace.initialize(space_sep_text);

  emit sizeChangedSignal();

  return m_trace.size();
}

size_t
TraceJs::initialize(const TraceJs &other)
{
  m_trace.clear();
  m_trace.reserve(other.m_trace.size());
  std::copy(other.m_trace.begin(), other.m_trace.end(), std::back_inserter(m_trace));

  emit sizeChangedSignal();

  return m_trace.size();
}


//////////////////////////////////////// OLD ///////////////////////////////


QVariantList
TraceJs::xValues() const
{
  QVariantList list;
  list.reserve(m_trace.size());

  for(const DataPoint &point : m_trace)
    {
      list.append(QVariant::fromValue(point.x));
    }

  return list;
}

QVariantList
TraceJs::yValues() const
{
  QVariantList list;
  list.reserve(m_trace.size());

  for(const DataPoint &point : m_trace)
    {
      list.append(QVariant::fromValue(point.y));
    }

  return list;
}

QVariantMap
TraceJs::toMap() const
{
  QVariantMap map;
  for(const auto &point : m_trace)
    {
      map.insert(QString::number(point.x), point.y);
    }
  return map;
}

void
TraceJs::clear()
{
  m_trace.clear();
}

void
TraceJs::registerJsConstructor(QJSEngine *engine)
{
  if(!engine)
    {
      qWarning() << "Cannot register class: engine is null";
      return;
    }

  // Register the meta object as a constructor
  QJSValue jsMetaObject = engine->newQMetaObject(&TraceJs::staticMetaObject);
  engine->globalObject().setProperty("Trace", jsMetaObject);
}

} // namespace pappso
