/***************************************************************************
                          kstcurve.cpp: holds info for a curve
                             -------------------
    begin                : Fri Nov 3 2000
    copyright            : (C) 2000 by cbn
    email                :
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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.                                   *
 *                                                                         *
 ***************************************************************************/

#include <klocale.h>
#include <kdebug.h>

#include <qstylesheet.h>

#include "kstcolorsequence.h"
#include "kstcurvedialog_i.h"
#include "kstdatacollection.h"
#include "kstdebug.h"
#include "kstrvector.h"
#include "kstvcurve.h"

static const QString& COLOR_XVECTOR = KGlobal::staticQString("X");
static const QString& COLOR_YVECTOR = KGlobal::staticQString("Y");
static const QString& EXVECTOR = KGlobal::staticQString("EX");
static const QString& EYVECTOR = KGlobal::staticQString("EY");

KstVCurve::KstVCurve(const QString &in_tag, KstVectorPtr in_X, KstVectorPtr in_Y, KstVectorPtr in_EX, KstVectorPtr in_EY, const QColor &in_color)
: KstBaseCurve() {
  setHasPoints(false);
  setHasLines(true);
  setLineWidth(0);
  setLineStyle(0);

  commonConstructor(in_tag, in_color);
  VX = in_X;
  VY = in_Y;
  EX = in_EX;
  EY = in_EY;
  update();
}

KstVCurve::KstVCurve(QDomElement &e)
: KstBaseCurve(e) {
  QString in_tag, xname, yname, exname, eyname;
  // QColor in_color(KstColorSequence::next(-1));
  QColor in_color("red"); // the above line is invalid.

  setHasPoints(false);
  setHasLines(false);
  setHasBars(false);
  setLineWidth(0);
  setLineStyle(0);
  setBarStyle(0);

  /* parse the DOM tree */
  QDomNode n = e.firstChild();
  while( !n.isNull() ) {
    QDomElement e = n.toElement(); // try to convert the node to an element.
    if( !e.isNull() ) { // the node was really an element.
      if (e.tagName() == "tag") {
        in_tag = e.text();
      } else if (e.tagName() == "xvectag") {
        xname = e.text();
      } else if (e.tagName() == "yvectag") {
        yname = e.text();
      } else if (e.tagName() == "exVectag") {
        exname = e.text();
      } else if (e.tagName() == "eyVectag") {
        eyname = e.text();
      } else if (e.tagName() == "color") {
        in_color.setNamedColor(e.text());

      // the following options are only needed to change from the default
      } else if (e.tagName() == "hasLines") {
        HasLines = (e.text() != "0");
      } else if (e.tagName() == "hasPoints") {
        HasPoints = (e.text() != "0");
      } else if (e.tagName() == "hasBars") {
        HasBars = (e.text() != "0");
      } else if (e.tagName() == "pointType") {
        Point.setType(e.text().toInt());
      } else if (e.tagName() == "lineWidth") {
        LineWidth = e.text().toInt();
      } else if (e.tagName() == "lineStyle") {
        LineStyle = e.text().toInt();
      } else if (e.tagName() == "barStyle") {
        BarStyle = e.text().toInt();
      }
    }
    n = n.nextSibling();
  }

  _inputVectorLoadQueue.append(qMakePair(COLOR_XVECTOR, xname));
  _inputVectorLoadQueue.append(qMakePair(COLOR_YVECTOR, yname));
  _inputVectorLoadQueue.append(qMakePair(EXVECTOR, exname));
  _inputVectorLoadQueue.append(qMakePair(EYVECTOR, eyname));
  commonConstructor(in_tag, in_color);
}

void KstVCurve::commonConstructor(const QString &in_tag, const QColor &in_color) {
  _typeString = i18n("Curve");
  Color = in_color;
  setTagName(in_tag);
}

KstVCurve::~KstVCurve() {
}

bool KstVCurve::loadInputs() {
  QValueList<QPair<QString,QString> >::Iterator i;
  KST::vectorList.lock().readLock();
  for (i = _inputVectorLoadQueue.begin(); i != _inputVectorLoadQueue.end(); ++i) {
    if ((*i).first == COLOR_XVECTOR) {
      VX = *KST::vectorList.findTag((*i).second);
      if (!VX.data()) {
        KstDebug::self()->log(i18n("Unable to find X vector for %2: [%1]").arg((*i).second).arg(tagName()), KstDebug::Warning);
        KST::vectorList.lock().readUnlock();
        return false;
      }
    } else if ((*i).first == COLOR_YVECTOR) {
      VY = *KST::vectorList.findTag((*i).second);
      if (!VY.data()) {
        KstDebug::self()->log(i18n("Unable to find Y vector for %2: [%1]").arg((*i).second).arg(tagName()), KstDebug::Warning);
        KST::vectorList.lock().readUnlock();
        return false;
      }
    } else if ((*i).first == EXVECTOR) {
      EX = *KST::vectorList.findTag((*i).second);
    } else if ((*i).first == EYVECTOR) {
      EY = *KST::vectorList.findTag((*i).second);
    }
  }
  KST::vectorList.lock().readUnlock();

  VX->readLock();
  VY->readLock();
  if (EX) {
    EX->readLock();
  }
  if (EY) {
    EY->readLock();
  }
  update(); // This happens in the GUI thread.  Might be a good idea to provide
            // a mechanism to ask the update thread to update an object instead.
            // (which would save us all this ugly locking too :))
  VX->readUnlock();
  VY->readUnlock();
  if (EX) {
    EX->readUnlock();
  }
  if (EY) {
    EY->readUnlock();
  }

  return true;
}

KstObject::UpdateType KstVCurve::update(int update_counter) {
  if (!VX || !VY || KstObject::checkUpdateCounter(update_counter))
    return NO_CHANGE;

  VX->update(update_counter);
  VY->update(update_counter);

  if (hasXError()) {
    EX->update(update_counter);
  }

  if (hasYError()) {
    EY->update(update_counter);
  }

  MaxX = VX->max();
  MinX = VX->min();
  MeanX = VX->mean();
  MinPosX = VX->minPos();
  _ns_maxx = VX->ns_max();
  _ns_minx = VX->ns_min();

  if (MinPosX > MaxX) {
    MinPosX = 0;
  }
  MaxY = VY->max();
  MinY = VY->min();
  MeanY = VY->mean();
  MinPosY = VY->minPos();
  _ns_maxy = VY->ns_max();
  _ns_miny = VY->ns_min();

  if (MinPosY > MaxY) {
    MinPosY = 0;
  }

  if (VX->sampleCount() > VY->sampleCount()) {
    NS = VX->sampleCount();
  } else {
    NS = VY->sampleCount();
  }

  return UPDATE;
}

void KstVCurve::point(int i, double &x, double &y) {
  x = VX->interpolate(i, NS);
  y = VY->interpolate(i, NS);
}

void KstVCurve::getEXPoint(int i, double &x, double &y, double &ex) {
  x = VX->interpolate(i, NS);
  y = VY->interpolate(i, NS);
  ex = EX->interpolate(i, NS);
}

void KstVCurve::getEYPoint(int i, double &x, double &y, double &ey) {
  x = VX->interpolate(i, NS);
  y = VY->interpolate(i, NS);
  ey = EY->interpolate(i, NS);
}

QString KstVCurve::xVTag() const{
  if (!VX.data()) {
    return QString::null;
  } else {
    return VX->tagName();
  }
}

QString KstVCurve::yVTag() const{
  if (!VY.data()) {
    return QString::null;
  } else {
    return VY->tagName();
  }
}

QString KstVCurve::xETag() const{
  if (!EX.data()) {
    return "<None>";
  } else {
    return EX->tagName();
  }
}

QString KstVCurve::yETag() const {
  if (!EY.data()) {
    return "<None>";
  } else {
    return EY->tagName();
  }
}

bool KstVCurve::hasXError() const {
  return EX.data() != 0L;
}

bool KstVCurve::hasYError() const {
  return EY.data() != 0L;
}

/** Save curve information */
void KstVCurve::save(QTextStream &ts, const QString& indent) {
  QString l2 = indent + "  ";
  ts << indent << "<curve>" << endl;
  ts << l2 << "<tag>" << QStyleSheet::escape(tagName()) << "</tag>" << endl;
  ts << l2 << "<xvectag>" << QStyleSheet::escape(VX->tagName()) << "</xvectag>" << endl;
  ts << l2 << "<yvectag>" << QStyleSheet::escape(VY->tagName()) << "</yvectag>" << endl;
  if (EX.data()) {
    ts << l2 << "<exVectag>" << QStyleSheet::escape(EX->tagName()) << "</exVectag>" << endl;
  }
  if (EY.data()) {
    ts << l2 << "<eyVectag>" << QStyleSheet::escape(EY->tagName()) << "</eyVectag>" << endl;
  }
  ts << l2 << "<color>" << Color.name() << "</color>" << endl;
  if (HasLines) {
    ts << l2 << "<hasLines/>" << endl;
    ts << l2 << "<lineWidth>" << LineWidth << "</lineWidth>" << endl;
    ts << l2 << "<lineStyle>" << LineStyle << "</lineStyle>" << endl;
  }
  if (HasPoints) {
    ts << l2 << "<hasPoints/>" << endl;
    ts << l2 << "<pointType>" << Point.type() << "</pointType>" << endl;
  }
  if (HasBars) {
    ts << l2 << "<hasBars/>" << endl;
    ts << l2 << "<barStyle>" << BarStyle << "</barStyle>" << endl;
  }
//   ts << l2 << "<hasLines>" << HasLines << "</hasLines>" << endl;
//   ts << l2 << "<hasPoints>" << HasPoints << "</hasPoints>" << endl;
//   ts << l2 << "<pointType>" << Point.type() << "</pointType>" << endl;
//   ts << l2 << "<lineWidth>" << LineWidth << "</lineWidth>" << endl;
//   ts << l2 << "<lineStyle>" << LineStyle << "</lineStyle>" << endl;
  ts << indent << "</curve>" << endl;
}

void KstVCurve::setXVector(KstVectorPtr new_vx) {
  VX = new_vx;
}

void KstVCurve::setYVector(KstVectorPtr new_vy) {
  VY = new_vy;
}

void KstVCurve::setXError(KstVectorPtr new_ex) {
  EX = new_ex;
}

void KstVCurve::setYError(KstVectorPtr new_ey) {
  EY = new_ey;
}

QString KstVCurve::xLabel() const {
  return VX->label();
}

QString KstVCurve::yLabel() const {
  return VY->label();
}

QString KstVCurve::topLabel() const {
  return VY->fileLabel();
}

KstCurveType KstVCurve::type() const {
  return KST_VCURVE;
}

QString KstVCurve::propertyString() const {
  return i18n("%1 vs %2").arg(yVTag()).arg(xVTag());
}

void KstVCurve::_showDialog() {
  KstCurveDialogI::globalInstance()->show_Edit(tagName());
}

int KstVCurve::samplesPerFrame() const {
  const KstRVector *rvp = dynamic_cast<const KstRVector*>(VY.data());
  return rvp ? rvp->samplesPerFrame() : 1;
}

bool KstVCurve::uses(KstObjectPtr p) const {
  bool rc = KstBaseCurve::uses(p);
  KstVectorPtr v = kst_cast<KstVector>(p);
  if (v) {
    rc = rc || VX == v || VY == v || EX == v || EY == v;
  }
  return rc;
}

// vim: ts=2 sw=2 et
