/***************************************************************************
**
**  This file is part of GeopsyGui.
**
**  This library is free software; you can redistribute it and/or
**  modify it under the terms of the GNU Lesser General Public
**  License as published by the Free Software Foundation; either
**  version 2.1 of the License, or (at your option) any later version.
**
**  This file 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 Lesser General Public
**  License for more details.
**
**  You should have received a copy of the GNU Lesser General Public
**  License along with this library; if not, write to the Free Software
**  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
**
**  See http://www.geopsy.org for more information.
**
**  Created: 2006-04-21
**  Copyright: 2006-2019
**    Marc Wathelet
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#include "TimeWindowLayer.h"
#include "SignalLayer.h"

namespace GeopsyGui {

const QString TimeWindowLayer::xmlTimeWindowLayerTag="TimeWindowLayer";

REGISTER_GRAPHCONTENTLAYER(TimeWindowLayer, "TimeWindowLayer")

TimeWindowLayer::TimeWindowLayer(AxisWindow * parent)
    : GraphContentLayer(parent)
{
  TRACE;
  _sig=nullptr;
  _size=0.375;
  _showColors=false;
  _windowOwner=false;
  _xmlWindows=nullptr;
}

TimeWindowLayer::TimeWindowLayer(SignalLayer * sig)
    : GraphContentLayer(sig->graph())
{
  TRACE;
  _sig=sig;
  _sig->addReference();
  _size=0.375;
  _showColors=false;
  _windowOwner=false;
  _xmlWindows=nullptr;
}

TimeWindowLayer::~TimeWindowLayer()
{
  TRACE;
  clearTimeWindows();
  if(_sig) {
    _sig->removeReference();
  }
  delete _xmlWindows;
}

Rect TimeWindowLayer::boundingRect() const
{
  TRACE;
  if(_sig) {
    return _sig->boundingRect();
  } else {
    return Rect();
  }
}

inline void TimeWindowLayer::paintTimeWindows(const GraphContentOptions& gc, QPainter& p,
                                              const TimeWindowList& windows, Signal * sig, int iSig) const
{
  TRACE;
  p.save();
  int wtop, wheight;
  _sig->timeWindowY(gc, iSig, _size, wtop, wheight);
  const DateTime& tref=_sig->timeReference(sig);
  TimeRange gcLimits(tref.shifted(gc.xVisMin()), tref.shifted(gc.xVisMax()));
  int *wleft, *wwidth;
  int n=windows.count();
  int i=0;
  wleft=new int [n];
  wwidth=new int [n];
  p.setPen(QPen( Qt::NoPen));
  p.setBrush(QBrush(QColor(200, 255, 193, 128)));
  TimeRange sigRange=sig->timeRange().range();
  TimeRangeList::const_iterator it;
  QColor col;
  // Painting is splitted in two parts to avoid changing the pen and the brush
  // for each time window. Screen coordinates of time window are cached.
  for(it=windows.begin(); it!=windows.end(); ++it) {
    const TimeWindow& w=*static_cast<TimeWindow *>(*it);
    TimeRange rLimits=w.intersection(gcLimits);
    if(rLimits.lengthSeconds()>0.0 && sigRange.intersects(rLimits)) {
      wleft[i]=gc.xr2s(tref.secondsTo(rLimits.start()));
      wwidth[i]=gc.xr2s(tref.secondsTo(rLimits.end()))-wleft[ i ];
      if(_showColors) {
        guiColor(w.color(), col);
        col.setAlpha(128);
        p.setBrush(col);
      }
      p.drawRect(wleft[i], wtop, wwidth[i], wheight);
      i++;
    }
  }
  n=i;
  p.setPen(QPen(QColor(152, 238, 77), 1, Qt::SolidLine));
  p.setBrush(QBrush( Qt::NoBrush));
  for(i=0; i<n; i++) {
    p.drawRect(wleft[i], wtop, wwidth[i], wheight);
  }
  delete [] wleft;
  delete [] wwidth;
  p.restore();
}

void TimeWindowLayer::paintData(const LayerPainterRequest& lp, QPainter& p, double) const
{
  TRACE;
  if(!_sig || !_sig->subPool()) return;
  const GraphContentOptions& gc=lp.options();
  const SubSignalPool& subPool=*_sig->subPool();
  QMap<Signal *, TimeWindowList *>::const_iterator it;
  int isigmin, isigmax;
  SAFE_UNINITIALIZED(isigmin, 0);
  SAFE_UNINITIALIZED(isigmax, 0);
  _sig->visibleIndexRange(gc, isigmin, isigmax);
  for(int iSig=isigmin; iSig < isigmax; ++iSig) {
    if(lp.terminated()) return;
    Signal * sig=subPool.at(iSig);
    it=_windows.find(sig);
    if(it!=_windows.end()) {
      paintTimeWindows(gc, p, *it.value(), sig, iSig);
    }
  }
}

void TimeWindowLayer::toggleTrackingAction(bool checked, int id)
{
  TRACE;
  if(id==-1) {
    QAction * a=qobject_cast<QAction *>(sender());
    if(!a) return;
    id=a->data().toInt();
  }
  if(checked) {
    if(!isMouseTracking(id)) {
      LayerMouseTracking mt(this);
      mt.setId(id);
      mt.setRectangle(true);
      mt.showRectangle();
      switch (id) {
      case Add:
        mt.setPen(QPen(QColor(152, 238, 77), 1, Qt::SolidLine));
        mt.setBrush(QColor(200, 255, 193, 128));
        break;
      case Remove:
        mt.setPen(QPen(QColor(255, 75, 78), 1, Qt::SolidLine));
        mt.setBrush(QColor(254, 156, 158, 128));
        break;
      default:
        return;
      }
      beginMouseTracking(mt);
    }
  } else {
    if(isMouseTracking(id)) {
      endMouseTracking(id);
    }
  }
}

bool TimeWindowLayer::trackRectangle(int id, double rx1, double ry1, double rx2, double ry2,
                                     Qt::KeyboardModifiers)
{
  TRACE;
  LayerLocker ll(this);
  QList<Signal *> sigs=_sig->hitSignals(ry1, ry2);
  switch(id) {
  case Add:
    for(int i=sigs.count()-1; i>=0; --i) {
      Signal * sig=sigs.at(i);
      const DateTime& tref=_sig->timeReference(sig);
      GeopsyCore::TimeRange r(tref.shifted(rx1), tref.shifted(rx2));
      emit addWindows(sig, r);
    }
    return true;
  case Remove:
    for(int i=sigs.count()-1; i>=0; --i) {
      Signal * sig=sigs.at(i);
      const DateTime& tref=_sig->timeReference(sig);
      GeopsyCore::TimeRange r(tref.shifted(rx1), tref.shifted(rx2));
      emit removeWindows(sig, r);
    }
    return true;
  default:
    break;
  }
  return false;
}

/*!
  Links signal \a sig to time window list \a l
*/
void TimeWindowLayer::addTimeWindows(Signal * sig, TimeWindowList * l)
{
  TRACE;
  LayerLocker ll(this);
  _windows.insert(sig, l);
}

/*!
  For convenience, links all signals of \a subPool to time window list \a l
*/
void TimeWindowLayer::addTimeWindows(const SubSignalPool& subPool, TimeWindowList * l)
{
  TRACE;
  LayerLocker ll(this);
  for(SubSignalPool::const_iterator it=subPool.begin(); it!=subPool.end(); it++) {
    _windows.insert(*it, l);
  }
}

void TimeWindowLayer::clearTimeWindows()
{
  TRACE;
  LayerLocker ll(this);
  if(_windowOwner) {
    for(QMap<Signal *, TimeWindowList *>::iterator it=_windows.begin(); it!=_windows.end(); it++) {
      delete it.value();
    }
  }
  _windows.clear();
}

void TimeWindowLayer::setSize(double s)
{
  TRACE;
  LayerLocker ll(this);
  _size=s;
}

void TimeWindowLayer::TimeWindowLayer::setShowColors(bool b)
{
  TRACE;
  LayerLocker ll(this);
  _showColors=b;
}

void TimeWindowLayer::xml_writeProperties(XML_WRITEPROPERTIES_ARGS) const
{
  TRACE;
  XMLSciFigs * scifigsContext=static_cast<XMLSciFigs *>(context);
  if(_sig && scifigsContext->data()) {
    writeProperty(s, "signalLayerName", _sig->objectName());
  }
  writeProperty(s, "size", _size);
  writeProperty(s, "showColors", _showColors);
}

void TimeWindowLayer::xml_writeChildren(XML_WRITECHILDREN_ARGS) const
{
  TRACE;
  XMLSciFigs * scifigsContext=static_cast<XMLSciFigs *>(context);
  if(_sig && scifigsContext->data()) {
    const SubSignalPool * subPool=_sig->subPool();
    if(subPool) {
      XMLSaveAttributes att;
      att.add("signalIndex");
      QString& index=att.value(0);
      int n=subPool->count();
      QMap<Signal *, TimeWindowList *>::const_iterator it;
      for(int i=0; i<n; i++) {
        it=_windows.find(subPool->at(i));
        if(it!=_windows.end()) {
          index=QString::number(i);
          it.value()->xml_save(s, context, att);
        }
      }
    }
  }
}

XMLMember TimeWindowLayer::xml_member(XML_MEMBER_ARGS)
{
  TRACE;
  XMLSciFigs * scifigsContext=static_cast<XMLSciFigs *>(context);
  if(scifigsContext->data()) {
    if(tag=="TimeWindowList") {
      const QString attID="signalIndex";
      XMLRestoreAttributeIterator it=attributes.find(attID);
      if(it!=attributes.end()) {
        int signalIndex=it.value().toInt();
        if(!_xmlWindows) {
          _xmlWindows=new XMLTimeWindowList;
        }
        TimeWindowList * w=new TimeWindowList;
        _xmlWindows->_windows.insert(signalIndex, w);
        return XMLMember(w);
      } else {
        App::log(tr("Missing signal index attribute\n") );
        return XMLMember(XMLMember::Unknown);
      }
    } else if(tag=="signalLayerName") {
      return  XMLMember(0);
    }
  }
  if(scifigsContext->makeUp()) {
    if(tag=="size") {
      return  XMLMember(1);
    } else if(tag=="showColors") {
      return  XMLMember(2);
    }
  }
  return GraphContentLayer::xml_member(tag, attributes, context)+3;
}

bool TimeWindowLayer::xml_setProperty(XML_SETPROPERTY_ARGS)
{
  TRACE;
  switch (memberID) {
  case 0:
    if(!_xmlWindows) {
      _xmlWindows=new XMLTimeWindowList;
    }
    _xmlWindows->_signalLayerName=content.toString();
    return true;
  case 1:
    _size=content.toDouble();
    return true;
  case 2:
    _showColors=content.toBool();
    return true;
  default:
    return GraphContentLayer::xml_setProperty(memberID-3, tag, attributes, content, context);
  }
}

/*!
  Reconstruct link to SignalLayer and identifies signal pointers.
*/
void TimeWindowLayer::xml_layerPolish(XML_POLISH_ARGS)
{
  XMLSciFigs * scifigsContext=static_cast<XMLSciFigs *>(context);
  if(scifigsContext->data() && _xmlWindows) {
    _sig=qobject_cast<SignalLayer *>(graphContent()->findLayer(SignalLayer::xmlSignalLayerTag,
                                                               _xmlWindows->_signalLayerName));
    if(_sig) {
      const SubSignalPool * subPool=_sig->subPool();
      for(QMap<int, TimeWindowList *>::iterator it=_xmlWindows->_windows.begin(); it!=_xmlWindows->_windows.end(); it++) {
        int index=it.key();
        if(index>=0 && index<subPool->count()) {
          _windows.insert(subPool->at(index), it.value());
        } else {
          delete it.value();
        }
      }
      _windowOwner=true;
      _sig->addReference();
    } else {
      for(QMap<int, TimeWindowList *>::iterator it=_xmlWindows->_windows.begin(); it!=_xmlWindows->_windows.end(); it++) {
        delete it.value();
      }
    }
    delete _xmlWindows;
    _xmlWindows=nullptr;
  }
}

} // namespace GeopsyGui
