/***************************************************************************
**
**  This file is part of GeopsySLinkGui.
**
**  GeopsySLinkGui 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 3 of the License, or
**  (at your option) any later version.
**
**  GeopsySLinkGui 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 Foobar.  If not, see <http://www.gnu.org/licenses/>
**
**  See http://www.geopsy.org for more information.
**
**  Created: 2007-04-30
**  Copyright: 2007-2019
**    Marc Wathelet
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#include <GeopsyCore.h>
#include <GeopsyGui.h>
#include <GeopsySLink.h>
#include <QGpCoreTools.h>
#include <QGpGuiTools.h>

#include "SeedLinkLoader.h"
#include "SeedLinkLoaderOptions.h"
#include "SeedLinkStreamItem.h"

namespace GeopsySLinkGui {

/*!
  \class SeedLinkLoader SeedLinkLoader.h
  \brief Brief description of class still missing

  Full description of class still missing
*/

/*!
  Description of constructor still missing
*/
SeedLinkLoader::SeedLinkLoader(QWidget * parent)
    : QWidget(parent)
{
  TRACE;
  setAttribute(Qt::WA_DeleteOnClose, true);
  setupUi(this);

  streamView->setModel(new SeedLinkStreamItem(this));
  streamView->setSelectionBehavior(QAbstractItemView::SelectRows);
  streamView->setSelectionMode(QAbstractItemView::ExtendedSelection);
  streamView->setEditTriggers(QAbstractItemView::NoEditTriggers);
  streamView->setContextMenuPolicy(Qt::CustomContextMenu);
  connect(streamView, SIGNAL(customContextMenuRequested (const QPoint & )),
           this, SLOT(showContextMenu( const QPoint & )) );

  _seedLink=new SeedLink(this);
  connect(_seedLink,SIGNAL(infoAvailable()), this, SLOT(streamInfoAvailable()), Qt::QueuedConnection);
  connect(_seedLink,SIGNAL(beginSignalChange(Signal *, TimeRange)),
           this, SLOT(beginSignalChange(Signal *)), Qt::QueuedConnection);
  connect(_seedLink,SIGNAL(endSignalChange(Signal *, TimeRange)),
           this, SLOT(endSignalChange(Signal *)), Qt::QueuedConnection);
  connect(_seedLink, SIGNAL(error(GeopsySLink::SeedLink::Error)),
           this, SLOT(seedLinkError(GeopsySLink::SeedLink::Error)), Qt::QueuedConnection);
  SeedLinkLoaderOptions::restore(_seedLink);
  seedLinkActivity->setMeasurementDelay(1.0);
  seedLinkActivity->setDisplayRange(120.0);

  // Create log stream for seed link thread
  Application::instance()->setStream(logView , _seedLink);

  Settings::getSize(this, "SeedLinkLoader" );
  Settings::splitter(splitter, "SeedLinkLoader" );
  serverName->addItems(Settings::getHistory("SeedLinkServers"));
  selectEdit->addItems(Settings::getHistory("SeedLinkSelectExpressions"));
  Settings::columnWidth(streamView, "SeedLinkLoader");
  Settings::getWidget(this, "SeedLinkLoader" );
}

/*!
  Description of destructor still missing
*/
SeedLinkLoader::~SeedLinkLoader()
{
  TRACE;
  if(_seedLink->isRunning()) {
    _seedLink->stop();
  }
  Settings::setSize(this, "SeedLinkLoader" );
  Settings::setColumnWidth(streamView, "SeedLinkLoader");
  Settings::setSplitter(splitter, "SeedLinkLoader");
  Settings::setWidget(this, "SeedLinkLoader" );
  logView->remove();
}

void SeedLinkLoader::removeOptions()
{
  TRACE;
  delete optionsBut;
  optionsBut=nullptr;
}

void SeedLinkLoader::removeView()
{
  TRACE;
  delete viewBut;
  viewBut=nullptr;
}

bool SeedLinkLoader::isAnySelected()
{
  TRACE;
  QItemSelectionModel * selModel=streamView->selectionModel();
  return !selModel->selectedRows().isEmpty();
}

void SeedLinkLoader::setStreamsEnabled(bool b)
{
  TRACE;
  streamView->setEnabled(b);
  stopBut->setEnabled(b);
  startBut->setEnabled(b);
}

void SeedLinkLoader::setServer(QString serverAddress, int serverPort)
{
  TRACE;
  // If current server is different than the one requested, stop current acquisition
  if(serverAddress!=_seedLink->serverName() || serverPort!=_seedLink->serverPort()) {
    Settings::setHistory("SeedLinkServers", serverAddress);
    serverName->clear();
    serverName->addItems(Settings::getHistory("SeedLinkServers"));
    serverName->setEditText(serverAddress);  // Useful if this function is not called via the gui
    _seedLink->setServer(serverAddress.toLatin1(), serverPort);
    port->setValue(serverPort);
  }
  static_cast<SeedLinkStreamItem *>(streamView->model())->setServer(nullptr);
  _seedLink->streams();
}

/*!
  Request up-to-date info about available streams
*/
void SeedLinkLoader::on_connectBut_clicked()
{
  TRACE;
  setServer(serverName->currentText(), port->value());
}

void SeedLinkLoader::seedLinkError(GeopsySLink::SeedLink::Error e)
{
  TRACE;
  static const QString serverConfigHelp=tr("Parameters info and info_trusted in seedlink.ini can be used to limit "
                                             "access to information obtained via INFO command. If the connecting IP "
                                             "matches the value of trusted (by default 127.0.0.0/8, eg., localhost), "
                                             "the parameter with trusted suffix is used.");
  switch (e) {
  case SeedLink::StreamsNotAvailable:
    Message::warning(MSG_ID, tr("Requesting stream list"),
                         tr("Server returned an error after requesting stream list. It is "
                            "probably linked to permission restrictions on the server side.\n\n")+serverConfigHelp);
    _seedLink->stations();
    break;
  case SeedLink::StationsNotAvailable:
    Message::warning(MSG_ID, tr("Requesting stream list"),
                         tr("Server returned an error after requesting station list. It is "
                            "probably linked to permission restrictions on the server side.\n\n")+serverConfigHelp);
    break;
  default:
    break;
  }
  streamView->update();
}

void SeedLinkLoader::streamInfoAvailable()
{
  TRACE;
  const SeedLinkServer& s=_seedLink->serverInfo();
  serverName->setToolTip(tr("%1\n%2\nStarted at %3").arg(s.software()).arg(s.organization()).arg(s.beginTime()));
  static_cast<SeedLinkStreamItem *>(streamView->model())->setServer(&s);
  for(int i=_seedLink->serverInfo().count()-1; i>=0; i--) {
    streamView->setFirstColumnSpanned(i, QModelIndex(), true);
  }
  setStreamsEnabled(true);
  if(!_streamSelection.isEmpty()) {
    selectStreams(_streamSelection);
    _streamSelection=QString();
    on_startBut_clicked();
  }
}

void SeedLinkLoader::beginSignalChange(Signal * sig)
{
  TRACE;
  GeopsyGuiEngine::instance()->beginSignalChange(sig);
}

void SeedLinkLoader::endSignalChange(Signal * sig)
{
  TRACE;
  seedLinkActivity->hit();
  GeopsyGuiEngine::instance()->endSignalChange(sig);
}

/*!
  Define the streams to select. Specification of streams is either the same as for slinktool or regular expressions:

    SL:stream1[:selectors1],stream2[:selectors2],...
    RX:stream1RX[:selectors1RX,...

  'stream' is in NET_STA format, followed by a selection of streams. If no selector is given, all streams are started.

  Examples:
    "SL:WA_WAU01:HHZ HHE HHN, WA_WAU02:HH?,WA_WAU03,..."
    "RX:WA_WAU[0-9]{2}"
*/
void SeedLinkLoader::selectStreams(QString selection)
{
  TRACE;
  Settings::setHistory("SeedLinkSelectExpressions", selection);
  selectEdit->clear();
  selectEdit->addItems(Settings::getHistory("SeedLinkSelectExpressions"));

  SeedLinkStreamItem * model=static_cast<SeedLinkStreamItem *>(streamView->model());
  if(model->rowCount()==0) { /* Stream list is still not available, postpone selection and start when the stream list
                                 will be available: see streamInfoAvailable(SeedLinkServer * s)*/
    _streamSelection=selection;
    return;
  }
  // First identify the type of selectors
  enum SelectorType {SeedLinkSelector, RegExpSelector};
  SelectorType selectorType;
  if(selection.startsWith("SL:")) {
    selectorType=SeedLinkSelector;
    selection=selection.mid(3);
  } else if(selection.startsWith("RX:")) {
    selectorType=RegExpSelector;
    selection=selection.mid(3);
  } else {
    selectorType=RegExpSelector;
  }
  QItemSelectionModel * selModel=streamView->selectionModel();
  selModel->clear();
  const SeedLinkServer& serverInfo=_seedLink->serverInfo();
  // Parse selection string, sl_parse_streamlist does not help because streams are not separated
  QStringList selectors=selection.split(",");
  for(QStringList::iterator itSel=selectors.begin(); itSel!=selectors.end();itSel++) {
    QString& str=*itSel;
    int selectorIndex=str.indexOf(":");
    QList<int> stationIndexes=serverInfo.indexOf(QRegExp( str.left(selectorIndex)) );
    for(QList<int>::iterator itStat=stationIndexes.begin(); itStat!=stationIndexes.end(); itStat++) {
      const SeedLinkStation * station=serverInfo.at(*itStat);
      QModelIndex stationModelIndex=model->index(*itStat, 0);
      QRegExp streamExp;
      if(selectorIndex==-1) {
        streamExp.setPattern(".*");
      } else {
        switch(selectorType) {
        case SeedLinkSelector:
          streamExp=SeedLinkStation::selectorExpression(str.mid(selectorIndex+1));
          break;
        case RegExpSelector:
          streamExp.setPattern(str.mid(selectorIndex+1));
          break;
        }
      }
      QList<int> streamIndexes=station->indexOf(streamExp);
      for(QList<int>::iterator it=streamIndexes.begin();it!=streamIndexes.end();it++) {
        selModel->select(model->index(*it, 0, stationModelIndex),
                         QItemSelectionModel::Rows | QItemSelectionModel::Select);
      }
    }
  }
}

void SeedLinkLoader::on_selectBut_clicked()
{
  TRACE;
  selectStreams(selectEdit->currentText());
}

void SeedLinkLoader::on_startBut_clicked()
{
  TRACE;
  StreamRedirection sr(App::stream(_seedLink));
  QItemSelectionModel * selModel=streamView->selectionModel();
  QModelIndexList l=selModel->selectedRows();
  SeedLinkStreamItem * model=static_cast<SeedLinkStreamItem *>(streamView->model());
  int nStartedStreams=0;
  for(QModelIndexList::iterator it=l.begin(); it!=l.end(); it++ ) {
    if(model->isStream( *it)) {
      if(_seedLink->addStream(model->stream( *it) )) {
        nStartedStreams++;
      }
      model->commit(*it);
    }
  }
  if(nStartedStreams>0) {
    _seedLink->start();
    emit started();
  }
}

void SeedLinkLoader::on_stopBut_clicked()
{
  TRACE;
  StreamRedirection sr(App::stream(_seedLink));
  QItemSelectionModel * selModel=streamView->selectionModel();
  QModelIndexList l=selModel->selectedRows();
  SeedLinkStreamItem * model=static_cast<SeedLinkStreamItem *>(streamView->model());
  int nStoppedStreams=0;
  for(QModelIndexList::iterator it=l.begin(); it!=l.end(); it++) {
    if(model->isStream(*it)) {
      if(_seedLink->removeStream(model->stream(*it))) {
        nStoppedStreams++;
      }
      model->commit(*it);
    }
  }
  if(nStoppedStreams>0) {
    if(_seedLink->listeningStreamCount()>0)
      _seedLink->start();
    else
      _seedLink->stop();
    emit stopped();
  }
}

void SeedLinkLoader::on_viewBut_clicked()
{
  TRACE;
  if(!viewBut) return;
  SubSignalPool subPool;
  QItemSelectionModel * selModel=streamView->selectionModel();
  QModelIndexList l=selModel->selectedRows();
  SeedLinkStreamItem * model=static_cast<SeedLinkStreamItem *>(streamView->model());
  int nTotal=0;
  for(QModelIndexList::iterator it=l.begin(); it!=l.end(); it++) {
    if(model->isStream(*it)) {
      SeedLinkStream * str=model->stream(*it);
      nTotal++;
      if(str->signal()) {
        subPool.addSignal(str->signal());
      }
    }
  }
  subPool.setName(tr("%1/%2 active SeedLink signal(s)").arg(subPool.count()).arg(nTotal));
  GeopsyGuiEngine::instance()->newGraphicWindow(this, subPool);
}

void SeedLinkLoader::on_optionsBut_clicked()
{
  TRACE;
  if(!optionsBut) return;
  SeedLinkLoaderOptions * d=new SeedLinkLoaderOptions(this);
  Settings::getWidget(d);
  d->updateAllFields();
  if(d->exec()==QDialog::Accepted) {
    Settings::setWidget(d);
    d->getParam(_seedLink);
  }
  delete d;
}

void SeedLinkLoader::showContextMenu(const QPoint & pos)
{
  TRACE;
  QMenu m;
  m.addAction(tr( "Expand all" ), streamView, SLOT(expandAll()) );
  m.addAction(tr( "Collapse all" ), streamView, SLOT(collapseAll()) );
  m.addAction(tr( "Select all" ), streamView, SLOT(selectAll()) );
  m.exec(streamView->viewport()->mapToGlobal(pos) );
}

} // namespace GeopsySLinkGui
