/*
 * Copyright 2009-2010  Stefan Gehn <stefan@srcbox.net>
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; either version 2 of 
 * the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "TouchData.hpp"
#include "settings.hpp"

namespace IrTouch
{

quint32 TouchRect::sId = 1;
qreal TouchData::sPpmX = 0;
qreal TouchData::sPpmY = 0;

static Value *findValue(ValueList &vl, quint32 id)
{
  //qDebug() << "findValue for id" << id;
  for (int i = 0; i < vl.size(); i++)
  {
    Value *v = &vl[i];
    if (v->id() == id)
      return v;
  }
  return 0;
}

static Value *findUnusedValue(ValueList &vl, int startIndex = 0)
{
  for (int i = startIndex; i < vl.size(); i++)
  {
    Value *v = &vl[i];
    if (v->mHandled == false)
      return v;
  }
  return 0;
}

TouchData::TouchData()
{
  if (sPpmX == 0 && sPpmY == 0)
  {
    IrTouch::Settings *s = IrTouch::Settings::self();
    sPpmX = 4096.0 / s->physicalWidth();
    sPpmY = 4096.0 / s->physicalHeight();
  }
  //qDebug() << this << "TouchData()";
}


// ----------------------------------------------------------------------------


TouchData::~TouchData()
{
  // qDebug() << this << "~TouchData()";
}

QRect TouchData::boundingRect() const
{
  if (mXValues.isEmpty() || mYValues.isEmpty())
    return QRect();

  int left = mXValues.first().value();
  int top = mYValues.first().value();
	int right = mXValues.last().value() + mXValues.last().extent();
  int bottom = mYValues.last().value() + mYValues.last().extent();

  return QRect(left, top, right - left, bottom - top);
}

void TouchData::scaleBy(qreal xfactor, qreal yfactor)
{
  //qDebug() << "TouchData::scaleBy(" << xfactor << "," << yfactor << ")";
  //qDebug() << "Before scaleBy(): " << *this;

  for (int xi = 0; xi < mXValues.size(); ++xi)
  {
    mXValues[xi].setScaleFactor(xfactor);
  }

  for (int yi = 0; yi < mYValues.size(); ++yi)
  {
    mYValues[yi].setScaleFactor(yfactor);
  }

  TouchRectHash::iterator iter;
  for (iter = mRects.begin(); iter != mRects.end(); ++iter)
  {
    iter.value().scaleBy(xfactor, yfactor);
  }

  //qDebug() << "After scaleBy(): " << *this;
}

void TouchData::updateRects()
{
  //qDebug() << "TouchData::updateRects()" << this;

  for (int xi = 0; xi < mXValues.size(); xi++)
    mXValues[xi].mHandled = false;
  for (int yi = 0; yi < mYValues.size(); yi++)
    mYValues[yi].mHandled = false;

  //- Alle TouchRects aktualisieren oder entfernen, falls deren Values nicht mehr existieren

  QMutableHashIterator<quint32, TouchRect> iter(mRects);
  while (iter.hasNext())
  {
    iter.next();

    TouchRect &trect = iter.value();
    Value *xv = findValue(mXValues, trect.xId());
    Value *yv = findValue(mYValues, trect.yId());

    if (xv && yv)
    {
      trect.updateFromValues(*xv, *yv);
      xv->mHandled = true;
      yv->mHandled = true;
      //qDebug() << "Updated TouchRect" << trect;
    }
    else
    {
      qDebug() << "Removing previous TouchRect" << trect;
      iter.remove();
    }
  }

  //- Neue TouchRects aus ungenutzten Values erstellen
  for (;;)
  {
    int xi = 0;
    int yi = 0;
    Value *xv = findUnusedValue(mXValues, xi);
    Value *yv = findUnusedValue(mYValues, yi);

    // Kein weiteres Paar von ungenutzten Werten gefunden, daher abbrechen
    if (!xv || !yv)
    {
      break;
    }
    else
    {
      TouchRect trect(*xv, *yv);
      qDebug() << "New TouchRect" << trect;
      mRects.insert(trect.id(), trect);

      xv->mHandled = true;
      yv->mHandled = true;
      ++xi;
      ++yi;
    }
  }
}

void TouchData::updateObjectType()
{
  mGuessedObjectType.clear();

  int xCnt = mXValues.size();
  int yCnt = mYValues.size();
  int valCnt = xCnt + yCnt;

  if (valCnt == 0)
    return;

  QRect bounds = boundingRect();
  // Height and width of boundingrect in millimeters
  int mmWidth = bounds.width() / sPpmX;
  int mmHeight = bounds.height() / sPpmY;
	double mmRatio = (double)mmWidth / (double)mmHeight;

  if (xCnt == 1 && yCnt == 1)
  {
    if ((mmWidth > 4 && mmWidth < 20) && (mmHeight > 4 && mmHeight < 30))
    {
      mGuessedObjectType = "one-finger";
    }
		else if (mmWidth > 15 && mmHeight > 50 && mmRatio > 0.15 && mmRatio < 0.48)
    {
      mGuessedObjectType = "vertical-hand-side";
    }
		else if (mmWidth > 50 && mmHeight > 15 && mmRatio < 1.0/0.15 && mmRatio > 1.0/0.48)
    {
      mGuessedObjectType = "horizontal-hand-side";
    }
    else if (mmWidth > 90 && mmWidth < 210 && mmHeight > 90 && mmHeight < 210)
    {
      mGuessedObjectType = "flat-hand";
    }
  }
  else if (valCnt == 3 || valCnt == 4 || mRects.count() == 2)
  {
    mGuessedObjectType = "two-fingers";
  }
  else if (valCnt > 4)
  {
    if (mmWidth > 60 && mmWidth < 210 && mmHeight > 70 && mmHeight < 160)
    {
      mGuessedObjectType = "all-fingers";
    }
  }

	/*qDebug() << "updateObjectType(); " << "valCnt:" << valCnt
			<< "; size (mm):" << mmWidth << "x" << mmHeight
			<< "; w/h ratio:" << QString::number(mmRatio, 'f', 2)
			<< "; detected object:" << mGuessedObjectType;*/
}

} // namespace IrTouch


QDebug operator<<(QDebug dbg, const IrTouch::TouchData &td)
{
  dbg.nospace() << "TouchData{X:" << td.xValues() << "Y:" << td.yValues() << td.rects() << "}";
  return dbg.space();
}

QDebug operator<<(QDebug dbg, const IrTouch::TouchRect &tr)
{
  dbg.nospace() << "TouchRect{ " << tr.id() << "; " << tr.xId() << " & " << tr.yId() << " -> "
      << static_cast<QRect> (tr) << "}";
  return dbg.space();
}
