/***************************************************************************
**
**  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: 2005-09-20
**  Copyright: 2005-2019
**    Marc Wathelet
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#include <GeopsyGui.h>
#include <SciFigs.h>
#include <QGpGuiMath.h>

#include "StationCoordinates.h"

StationCoordinates::StationCoordinates(QWidget *parent, Qt::WindowFlags f)
    : Dialog(parent, f)
{
  TRACE;
  setupUi(this);

  StationCoordinatesItem * model=new StationCoordinatesItem(this);
  model->setStations(&_stations);
  stationTable->setModel(model);
  stationTable->setSelectionBehavior(QAbstractItemView::SelectRows);
  stationTable->setSelectionMode(QAbstractItemView::ExtendedSelection);
  stationTable->setEditTriggers(QAbstractItemView::AllEditTriggers);

  _map=0;
}

StationCoordinates::~StationCoordinates()
{
  TRACE;
  endMap();
}

StationCoordinatesItem * StationCoordinates::model()
{
  return static_cast<StationCoordinatesItem *>(stationTable->model());
}

void StationCoordinates::on_loadBut_clicked()
{
  TRACE;
  CoordinateFileWidget cf;
  if(cf.read()) {
    // Collect coordinates
    model()->beginStationChange();
    beginMap();
    QList<NamedPoint> points=cf.points();
    for(QList<NamedPoint>::iterator it=points.begin(); it!=points.end();it++) {
      NamedPoint * st;
      QMap<QString, int>::iterator itmap=_map->find(it->name());
      if(itmap!=_map->end()) {
        st=&_stations[itmap.value()];
      } else {
        _stations.append(NamedPoint());
        st=&_stations.last();
      }
      *st=*it;
    }
    endMap();
    model()->endStationChange();
    if(_utmZone!=cf.utmZone()) {
      if(Message::warning(MSG_ID, tr("UTM zone changed"), tr("Current UTM zone is not the same as the UTM zone of loaded points. "
                                                             "Do you want to change UTM zone to %1?").arg(cf.utmZone().toString()),
                          Message::yes(), Message::no())==Message::Answer0) {
        _utmZone=cf.utmZone();
        updateUtmZone();
      }
    }
  }
}

void StationCoordinates::on_saveBut_clicked()
{
  TRACE;
  CoordinateFileWidget::write(_stations, _utmZone);
}

void StationCoordinates::on_addBut_clicked()
{
  TRACE;
  model()->beginStationChange();
  _stations.append(NamedPoint());
  model()->endStationChange();
}

/*!
  Add a new station if \a name does not exist in current list, else modify station named \a name.
  If \a force is true and \a name exists in current list, the station named \a name is not modified, but a newone is added.
  In this last case addStation() return true.
*/
bool StationCoordinates::addStation(QString name, Point coord, bool force)
{
  TRACE;
  NamedPoint * st;
  QMap<QString, int>::iterator it=_map->find(name);
  bool ret;
  if(it!=_map->end()) {
    st=&_stations[it.value()];
    if(force && coord!=*st) {
      _stations.append(NamedPoint());
      st=&_stations.last();
      st->setName(name);
      ret=true;
    } else {
      ret=false;
    }
  } else {
    _stations.append(NamedPoint());
    st=&_stations.last();
    st->setName(name);
    _map->insert(name, _map->count());
    ret=false;
  }
  *st=coord;
  return ret;
}

Point StationCoordinates::lookup(Signal * sig, bool& ok)
{
  TRACE;
  if( !_map) beginMap();
  QMap<QString, int>::iterator it=_map->find(keyName(sig));
  if(it!=_map->end()) {
    ok=true;
    return _stations.at(it.value());
  } else {
    ok=false;
    return Point();
  }
}

void StationCoordinates::beginMap()
{
  TRACE;
  // Create a coordinate map to speedup the lookup table
  _map=new QMap<QString, int>;
  int n=_stations.count();
  for(int i=0;i < n;i++ ) {
    const NamedPoint& st=_stations.at(i);
    _map->insert(st.name(), i);
  }
}

void StationCoordinates::endMap()
{
  TRACE;
  delete _map;
  _map=0;
}

void StationCoordinates::updateUtmZone()
{
  TRACE;
  if(_utmZone.isValid()) {
    utmZoneLabel->show();
    utmZoneLabel->setText(QString("UTM %1").arg(_utmZone.toString()));
  } else {
    utmZoneLabel->hide();
  }
}

/*!
  Return false if two stations have the same names or if various UTM zone are detected.
  User error messages are sent to App::stream().
*/
bool StationCoordinates::init(SubSignalPool * subPool)
{
  TRACE;
  _stations.clear();
  bool ambiguities=false;
  model()->beginStationChange();
  beginMap();
  for(SubSignalPool::iterator it=subPool->begin(); it!= subPool->end(); it++ ) {
    Signal * sig=*it;
    if(addStation(keyName(sig), coordinates(sig), true)) {
      App::log(tr("Station '%1' found with different coordinates. You will not be able to change coordinates of signals. "
                          "Check that all components have the the same coordinates.\n").arg(sig->name()));
      ambiguities=true;
    }
    if(_utmZone.isValid()) {
      if(sig->utmZone()!=_utmZone) {
        App::log(tr("UTM zone is '%1' and station '%2' belongs to UTM zone %3. Geopsy only works with metric Cartesian "
                    "coordinates within a single UTM zone. A database may contain signal from various UTM zones but they "
                    "cannot be processed together (for signal processing based on station coordinates).\n")
                         .arg(_utmZone.toString()).arg(sig->name()).arg(sig->utmZone().toString()));
        ambiguities=true;
      }
    } else {
      _utmZone=sig->utmZone();
    }
  }
  endMap();
  model()->endStationChange();
  updateUtmZone();
  return !ambiguities;
}

void StationCoordinates::on_relativePosBut_clicked()
{
  TRACE;
  RelativePositions * d=new RelativePositions(this);
  d->setStationList(&_stations);
  Settings::getWidget(d);
  d->exec();
  Settings::setWidget(d);
  delete d;
}
