/***************************************************************************
**
**  This file is part of geopsy.
**
**  geopsy 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.
**
**  geopsy 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: 2004-04-05
**  Copyright: 2004-2019
**    Marc Wathelet
**    Marc Wathelet (ULg, Liège, Belgium)
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#include <QIcon>

#include <GeopsyCore.h>
#include <QGpGuiTools.h>
#include <SciFigs.h>
#include "MapWindow.h"
#include "SigSelectionDnD.h"

MapWindow::MapWindow(QWidget* parent)
  : SubPoolWindow(parent)
{
  TRACE;
  setObjectName("map");
  QVBoxLayout * l=new QVBoxLayout(this);
  l->setMargin(1);
  _map=new ArrayMap(this);
  l->addWidget(_map);
  _map->setObjectName("map");
  setWindowIcon(QIcon(":map-22x22.png"));

  _map->xAxis()->setTitle("X (m)");
  _map->xAxis()->setSizeType(Axis::Scaled);
  _map->xAxis()->setSizeInfo(1000);
  _map->yAxis()->setTitle("Y (m)");
  _map->yAxis()->setSizeType(Axis::Scaled);
  _map->yAxis()->setSizeInfo(1000);
  _map->mapLayer()->addTrackingAction(tr("&Select stations"), 0, tr("Once selected, the stations can be dragged and dropped to other viewers."));
  _map->updateExternalGeometry();

  QAction * a=new QAction(tr("Select from &coordinates"), this);
  a->setToolTip(tr("Select stations that match with coordinates read from a file"));
  connect(a, SIGNAL(triggered(bool)), this, SLOT(selectFromCoordinates()));
  _map->graphContent()->QWidget::addAction(a);

  // Drag and drop to transfert signal selection
  SigSelectionDnD * sigDnD=new SigSelectionDnD(_map->graphContent());
  connect(sigDnD,SIGNAL(selectionDropped(QWidget *, const SubSignalPool&)), this, SLOT(addSignals(QWidget *, const SubSignalPool&)));
  connect(sigDnD,SIGNAL(getSelection(SubSignalPool&)), this,SLOT(getSelection(SubSignalPool&)));
  connect(_map->graphContent(),SIGNAL(mouseTrackingBegin()), sigDnD,SLOT(disableFilter()));
  connect(_map->graphContent(),SIGNAL(mouseTrackingEnd()), sigDnD,SLOT(enableFilter()));

  // Make up
  if(GeopsyCoreEngine::instance()->preferences()->restoreMapMakeUp() &&
      !GeopsyCoreEngine::instance()->preferences()->mapMakeUpFile().isEmpty()) {
    _map->restoreMakeUp(GeopsyCoreEngine::instance()->preferences()->mapMakeUpFile());
  }
}

MapWindow::~MapWindow()
{
  TRACE;
}

void MapWindow::setLimits()
{
  TRACE;
  Rect r=_map->mapLayer()->boundingRect();
  r.enlarge(0.20, LinearScale, LinearScale);
  _map->setMapScale(r.x1(), r.y1(), r.x2(), r.y2());
}

void MapWindow::setWindowTitle(QString title)
{
  TRACE;
  SubPoolWindow::setWindowTitle("Map - "+title);
}

void MapWindow::subPoolUpdate ()
{
  TRACE;
  if(subPool().isEmpty()) {
    setWindowTitle(QString());
    return;
  }
  if(subPool().count()==1) {
    Signal& sig=*subPool().at(0);
    QString traceName=sig.name()+" "+sig.componentLetter(sig.component());
    setWindowTitle(traceName);
  }
  else setWindowTitle(subPool().name());
  endSignalChange();
  // Make up
  if(GeopsyCoreEngine::instance()->preferences()->restoreMapMakeUp()) {
    _map->restoreMakeUp(GeopsyCoreEngine::instance()->preferences()->mapMakeUpFile());
  }
}

void MapWindow::endSignalChange(Signal * sig)
{
  TRACE;
  if(subPool().isEmpty()) return;
  _map->mapLayer()->clear();
  SubSignalPool::iterator it;
  for(it=subPool().begin(); it!=subPool().end(); ++it) {
    Signal * sig=*it;
    Curve<NamedPoint>& c=static_cast<NameLine *>(_map->addLine())->curve();
    c.resize(1);
    NamedPoint& p=c.first();
    p=sig->receiver();
    p.setName(sig->name());
  }
  setLimits();
  _map->deepUpdate();
  SubPoolWindow::endSignalChange(sig);
}

void MapWindow::getSelection(SubSignalPool& sel)
{
  TRACE;
  int n=subPool().count();
  for(int i=0; i<n; i++) {
    if(_map->mapLayer()->line(i)->isSelected()) {
      sel.addSignal(subPool().at(i));
    }
  }
  sel.setName(tr("Selected from %1").arg(subPool().name()));
  // If nothing is selected, put everything in the selection
  if(sel.isEmpty()) {
    sel=subPool();
  }
}

/*!
  Select stations that are close to the coordinates provided in a file.
  The coordinates should approximate the receiver coordinates but they are
  not necessarily equal. The accepted match precision is half of the minimum
  distance between sensors, to avoid ambiguity and to provide scalability.
*/
void MapWindow::selectFromCoordinates()
{
  TRACE;
  CoordinateFileWidget cf;
  if(cf.read()) {
    QList<NamedPoint> points=cf.points();
    int n=subPool().count();
    // Get the minimum distance between sensors
    double mind=std::numeric_limits<double>::infinity();
    for(int i=0; i<n; i++) {
      const Point& p1=subPool().at(i)->receiver();
      for(int j=i; j<n; j++) {
        double d=subPool().at(j)->receiver().distanceTo(p1);
        if(d>0.0 && d<mind) {
          mind=d;
        }
      }
    }
    mind*=0.5;
    APP_LOG(1, tr("Maximum distance to match=%1 m\n").arg(mind));
    for(int i=0; i<n; i++) {
      const Point& p=subPool().at(i)->receiver();
      APP_LOG(1, tr("Sensor '%1' at %2\n").arg(subPool().at(i)->name()).arg(p.toString()));
      for(QList<NamedPoint>::iterator it=points.begin(); it!=points.end(); it++) {
        APP_LOG(1, tr("  distance to '%1'= %2 m\n")
                   .arg(it->name()).arg(p.distanceTo(*it)));
        if(p.distanceTo(*it)<mind) {
          _map->mapLayer()->line(i)->setSelected(true);
          break;
        }
      }
    }
    _map->deepUpdate();
  }
}

void MapWindow::fastFourierTransform(DoubleSignal::SignalType st)
{
  TRACE;
  beginSignalChange();
  subPool().fastFourierTransform(st);
  endSignalChange();
}
