#include <numeric>
#include <limits>
#include <vector>
#include <map>
#include <cmath>
#include <iostream>

#include <QDebug>

#include "traceminuscombiner.h"
#include "../../trace/trace.h"
#include "../../types.h"
#include "../../utils.h"
#include "../../pappsoexception.h"
#include "../../exception/exceptionoutofrange.h"


namespace pappso
{


TraceMinusCombiner::TraceMinusCombiner()
{
}


TraceMinusCombiner::TraceMinusCombiner(int decimal_places)
  : TraceCombiner(decimal_places)
{
}


TraceMinusCombiner::TraceMinusCombiner(const TraceMinusCombiner &other)
  : TraceCombiner(other)
{
}


TraceMinusCombiner::TraceMinusCombiner(TraceMinusCombinerCstSPtr other)
  : TraceCombiner(other)
{
}


TraceMinusCombiner::~TraceMinusCombiner()
{
}


MapTrace &
TraceMinusCombiner::combine(MapTrace &map_trace, const Trace &trace) const
{
  //qDebug() << __FILE__ << "@" << __LINE__ << __FUNCTION__ << " ()"
           //<< "map trace size:" << map_trace.size()
           //<< "trace size:" << trace.size();

  if(!trace.size())
    return map_trace;

  for(auto &current_data_point : trace)
    {

      // If the data point is 0-intensity, then do nothing!
      if(!current_data_point.y)
        continue;

      //qDebug() << "Iterating in new trace data point:"
               //<< current_data_point.toString(15);

      double x = Utils::roundToDecimals(current_data_point.x, m_decimalPlaces);

      std::map<double, double>::iterator map_iterator;

      std::pair<std::map<pappso_double, pappso_double>::iterator, bool> result;

      //qDebug() << "Willing to insert new data point:" << x << ","
               //<< -current_data_point.y;

      result = map_trace.insert(
        std::pair<pappso_double, pappso_double>(x, -current_data_point.y));

      if(result.second)
        {
          //qDebug() << "Inserted new data point:" << x << ","
                   //<< -current_data_point.y;
          // The new element was inserted, we have nothing to do.
        }
      else
        {
          //qDebug() << "Going to decrement from" << result.first->second
                   //<< "a value of " << current_data_point.y;

          // The key already existed! The item was not inserted. We need to
          // update the value.

          result.first->second -= current_data_point.y;

          //qDebug() << "New result: " << result.first->second;
        }
    }

  //qDebug() << "Prior to returning map_trace, its size is:" << map_trace.size();

  return map_trace;
}


MapTrace &
TraceMinusCombiner::combine(MapTrace &map_trace_out,
                            const MapTrace &map_trace_in) const
{
  // qDebug() << __FILE__ << "@" << __LINE__ << __FUNCTION__ << " ()"
  //<< "map trace size:" << map_trace_out.size()
  //<< "trace size:" << trace.size();

  if(!map_trace_in.size())
    return map_trace_out;

  return combine(map_trace_out, map_trace_in.toTrace());
}


} // namespace pappso
