text: //=============================================================================================================
  /**
   * @file     eegosportsimpedancewidget.cpp
   * @author   Lorenz Esch <Lorenz.Esch@tu-ilmenau.de>
   *           Christoph Dinh <chdinh@nmr.mgh.harvard.edu>;
   *           Matti Hamalainen <msh@nmr.mgh.harvard.edu>;
   *           Johannes Vorwerk <johannes.vorwerk@umit.at>
   * @since    0.1.0
   * @date     February, 2020
   *
   * @section  LICENSE
   *
   * Copyright (C) 2020, Lorenz Esch, Christoph Dinh, Matti Hamalainen, Johannes Vorwerk. All rights reserved.
   *
   * Redistribution and use in source and binary forms, with or without modification, are permitted provided that
   * the following conditions are met:
   *     * Redistributions of source code must retain the above copyright notice, this list of conditions and the
   *       following disclaimer.
   *     * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and
   *       the following disclaimer in the documentation and/or other materials provided with the distribution.
   *     * Neither the name of MNE-CPP authors nor the names of its contributors may be used
   *       to endorse or promote products derived from this software without specific prior written permission.
   *
   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
   * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
   * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
   * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
   * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
   * POSSIBILITY OF SUCH DAMAGE.
   *
   *
   * @brief    Contains the declaration of the EEGoSportsImpedanceWidget class.
   *
   */
  
  //=============================================================================================================
  // INCLUDES
  //=============================================================================================================
  
  #include "eegosportsimpedancewidget.h"
  #include "ui_eegosportsimpedancewidget.h"
  
  #include <utils/layoutloader.h>
  #include <utils/layoutmaker.h>
  
  #include "../eegosports.h"
  
  //=============================================================================================================
  // QT INCLUDES
  //=============================================================================================================
  
  #include <QFileDialog>
  #include <QStandardPaths>
  #include <QString>
  #include <QDate>
  #include <QMessageBox>
  
  //=============================================================================================================
  // USED NAMESPACES
  //=============================================================================================================
  
  using namespace EEGOSPORTSPLUGIN;
  
  //=============================================================================================================
  // DEFINE MEMBER METHODS
  //=============================================================================================================
  
  EEGoSportsImpedanceWidget::EEGoSportsImpedanceWidget(EEGoSports* pEEGoSports, QWidget *parent)
  : m_pEEGoSports(pEEGoSports)
  , QWidget(parent)
  , m_pUi(new Ui::EEGoSportsImpedanceWidget)
  , m_dMaxImpedance(20000)
  {
      m_pUi->setupUi(this);
  
      // Init colormap object
      m_cbColorMap = QSharedPointer<ColorMap>(new ColorMap());
  
      // Init GUI stuff
      m_qGScene = new EEGoSportsImpedanceScene(m_pUi->m_graphicsView_impedanceView);
      m_pUi->m_graphicsView_impedanceView->setScene(m_qGScene);
      m_pUi->m_graphicsView_impedanceView->show();
  
      m_pUi->m_pushButton_stop->setEnabled(false);
  
      // Connects of this widget
      connect(m_pUi->m_pushButton_stop, &QPushButton::released, this, &EEGoSportsImpedanceWidget::stopImpedanceMeasurement);
      connect(m_pUi->m_pushButton_start, &QPushButton::released, this, &EEGoSportsImpedanceWidget::startImpedanceMeasurement);
      connect(m_pUi->m_pushButton_takeScreenshot, &QPushButton::released, this, &EEGoSportsImpedanceWidget::takeScreenshot);
      connect(m_pUi->m_pushButton_loadLayout, &QPushButton::released, this, &EEGoSportsImpedanceWidget::loadLayout);
      connect(m_pUi->m_pushButton_saveValues, &QPushButton::released, this, &EEGoSportsImpedanceWidget::saveToFile);
      connect(m_pUi->m_pushButton_Help, &QPushButton::released, this, &EEGoSportsImpedanceWidget::helpDialog);
      //connect(m_pUi->m_verticalSlider_manualImpedanceValue, &QSlider::valueChanged, this, &EEGoSportsImpedanceWidget::setIm);
  }
  
  //=============================================================================================================
  
  EEGoSportsImpedanceWidget::~EEGoSportsImpedanceWidget()
  {
      if (m_pEEGoSports->m_bCheckImpedances = true)
          stopImpedanceMeasurement();
  
      delete m_pUi;
  }
  
  //=============================================================================================================
  
  void EEGoSportsImpedanceWidget::updateGraphicScene(const VectorXd& matValue)
  {
      // Get scene items
      QList<QGraphicsItem *> itemList = m_qGScene->items();
  
      // Update color and impedance values for each electrode item
      int matIndex = 0;
      double impedanceValue = 0.0;
      int numberItems = itemList.size();
  
      if(itemList.size()>matValue.rows())
      {
          qDebug()<<"EEGoSportsImpedanceWidget - ERROR - There were more items in the scene than samples received from the device - Check the current layout! Only available channels will be displayed!"<<endl;
     //     qDebug()<<"itemlist.size() " << itemList.size() << " matValue.rows() " << matValue.rows() << endl;
          numberItems = matValue.rows();
          return;
      }
  
      // For testing purposes only!
      //impedanceValue = m_pUi->m_verticalSlider_manualImpedanceValue->value();
  
      double threshold = m_pUi->m_doubleSpinBox_manualImpedanceThreshold->value();
      double colormapmax = m_pUi->m_doubleSpinBox_manualImpedanceColormapMax->value();
  
      for(int i = 0; i<numberItems; i++)
      {
          EEGoSportsElectrodeItem *item = (EEGoSportsElectrodeItem *) itemList.at(i);
  
          // find matrix index for given electrode name
          matIndex = m_qmElectrodeNameIndex[item->getElectrodeName()];
          impedanceValue = matValue[matIndex];
  
          // set new color and impedance value. Clip received impedance value if > predefined max impedance value
          if(impedanceValue>3*threshold || impedanceValue<0)
              impedanceValue = -1.0;
          //double scale = 0.000053;
          //cout << scale <<endl;
          //double valueScaledNormalized = (scale*impedanceValue)/(scale*impedanceValue+1);
  
          if(m_pUi->m_radioButton_Threshold->isChecked())
          {
              QColor thresholdColor;
              if(impedanceValue > 2*threshold)
              {
                  thresholdColor.setRed(255);
                  thresholdColor.setGreen(140);
              }
              else if(impedanceValue > threshold)
              {
                  thresholdColor.setRed(255);
                  thresholdColor.setGreen(255);
              }
              else if(impedanceValue > 0)
                  thresholdColor.setGreen(255);
              else
                  thresholdColor.setRed(255);
              item->setColor(thresholdColor);
          }
          else if(m_pUi->m_radioButton_Colormap->isChecked())
              item->setColor(m_cbColorMap->valueToJet(std::min(colormapmax,impedanceValue/colormapmax)));
  
          item->setImpedanceValue(impedanceValue);
      }
  
      m_qGScene->update(m_qGScene->itemsBoundingRect());
  }
  
  //=============================================================================================================
  
  void EEGoSportsImpedanceWidget::initGraphicScene()
  {
      // Clear all items from scene
      m_qGScene->clear();
  
      // Load standard layout file
  //    LayoutLoader *asaObject = new LayoutLoader();
      QList<QVector<float> > elcLocation3D;
      QList<QVector<float> > elcLocation2D;
      QString unit;
      QStringList elcChannelNames;
      QString sElcFilePath = QString("../resources/general/3DLayouts/standard_waveguard66.elc");
  
      if(!LayoutLoader::readAsaElcFile(sElcFilePath, elcChannelNames, elcLocation3D, elcLocation2D, unit))
      {
          qDebug() << "Error: Reading elc file.";
          return;
      }
  
      // Generate lookup table for channel names to corresponding matrix/vector index
      for(int i = 0; i<elcLocation2D.size(); i++)
          m_qmElectrodeNameIndex.insert(elcChannelNames.at(i), i);
  
      // Add electrodes to scene
      for(int i = 0; i<elcLocation2D.size(); i++)
      {
          QVector2D position(elcLocation2D[i][1]*-4.5,elcLocation2D[i][0]*-4.5); // swap x y to rotate 90°, multiply to mirror by x and y axis, multiply by to scale
          addElectrodeItem(elcChannelNames.at(i), position);
      }
  
      m_pUi->m_graphicsView_impedanceView->fitInView(m_qGScene->itemsBoundingRect(), Qt::KeepAspectRatio);
  }
  
  //=============================================================================================================
  
  void EEGoSportsImpedanceWidget::addElectrodeItem(const QString& electrodeName, const QVector2D& position)
  {
      EEGoSportsElectrodeItem *item = new EEGoSportsElectrodeItem(electrodeName, QPointF(position.x(), position.y()), QColor(m_cbColorMap->valueToJet(1)), m_qmElectrodeNameIndex[electrodeName]);
      item->setPos(QPointF(position.x(), position.y()));
      m_qGScene->addItem(item);
  }
  
  //=============================================================================================================
  
  void EEGoSportsImpedanceWidget::startImpedanceMeasurement()
  {
      m_pEEGoSports->m_bCheckImpedances = true;
  
      if(m_pEEGoSports->start())
      {
          m_pUi->m_pushButton_stop->setEnabled(true);
          m_pUi->m_pushButton_start->setEnabled(false);
      }
      else
      {
          m_pEEGoSports->m_bCheckImpedances = false;
          QMessageBox::information(0, tr("MNE Scan - Start"), QString(QObject::tr("Not able to start impedance measurement!\nEither the device is turned off (check your OS device manager) or the driver DLL (EEGO-SDK.dll) is not installed in one of the monitored dll paths.")), QMessageBox::Ok);
          return;
      }
  }
  
  //=============================================================================================================
  
  void EEGoSportsImpedanceWidget::stopImpedanceMeasurement()
  {
      m_pEEGoSports->m_bCheckImpedances = false;
  
      if(m_pEEGoSports->stop())
      {
          m_pUi->m_pushButton_stop->setEnabled(false);
          m_pUi->m_pushButton_start->setEnabled(true);
      }
      else
          m_pEEGoSports->m_bCheckImpedances = true;
  }
  
  //=============================================================================================================
  
  void EEGoSportsImpedanceWidget::takeScreenshot()
  {
      // Open file dialog
      QDate date;
      QString fileName = QFileDialog::getSaveFileName(this,
                                                      "Save Screenshot",
                                                      QString("%1/%2_%3_%4_Impedances").arg(QStandardPaths::writableLocation(QStandardPaths::DesktopLocation)).arg(date.currentDate().year()).arg(date.currentDate().month()).arg(date.currentDate().day()),
                                                      tr("Vector graphic(*.svg);;Images (*.png)"));
  
      if(!fileName.isEmpty())
      {
          // Generate screenshot
          if(fileName.contains(".svg"))
          {
              QSvgGenerator svgGen;
  
              svgGen.setFileName(fileName);
              QRectF rect = m_qGScene->itemsBoundingRect();
              svgGen.setSize(QSize(rect.width(), rect.height()));
              //svgGen.setViewBox(QRect(0, 0, rect.width(), rect.height()));
  
              QPainter painter(&svgGen);
              m_qGScene->render(&painter);
          }
  
          if(fileName.contains(".png"))
          {
              m_qGScene->setSceneRect(m_qGScene->itemsBoundingRect());                          // Re-shrink the scene to it's bounding contents
              QImage image(m_qGScene->sceneRect().size().toSize(), QImage::Format_ARGB32);  // Create the image with the exact size of the shrunk scene
              image.fill(Qt::transparent);                                              // Start all pixels transparent
  
              QPainter painter(&image);
              m_qGScene->render(&painter);
              image.save(fileName);
          }
      }
  }
  
  //=============================================================================================================
  
  void EEGoSportsImpedanceWidget::loadLayout()
  {
      QString sElcFilePath = QFileDialog::getOpenFileName(this,
                                                          tr("Open Layout"),
                                                          "./resources/mne_scan/plugins/eegosports/loc_files/",
                                                          tr("ELC layout file (*.elc)"));
  
      // Load standard layout file
  //    LayoutLoader *asaObject = new LayoutLoader();
      QList<QVector<float> > elcLocation3D;
      QList<QVector<float> > elcLocation2D;
      QString unit;
      QStringList elcChannelNames;
  
      if(!LayoutLoader::readAsaElcFile(sElcFilePath, elcChannelNames, elcLocation3D, elcLocation2D, unit))
          qDebug() << "Error: Reading elc file.";
      else
          m_qGScene->clear();
  
      // Clean old map -> Generate lookup table for channel names and corresponding index
      m_qmElectrodeNameIndex.clear();
  
      for(int i = 0; i<elcLocation2D.size(); i++)
          m_qmElectrodeNameIndex.insert(elcChannelNames.at(i), i);
  
      // Add electrodes to scene
      for(int i = 0; i<elcLocation2D.size(); i++)
      {
          QVector2D position(elcLocation2D[i][1]*-4.5,elcLocation2D[i][0]*-4.5); // swap x y to rotate 90°, multiply to mirror by x and y axis, multiply by to scale
  
          addElectrodeItem(elcChannelNames.at(i), position);
      }
  
      m_pUi->m_graphicsView_impedanceView->fitInView(m_qGScene->itemsBoundingRect(), Qt::KeepAspectRatio);
  }
  
  //=============================================================================================================
  
  void EEGoSportsImpedanceWidget::closeEvent(QCloseEvent *event)
  {
      Q_UNUSED(event);
  
      // On window close event -> stop impedance measurement
      if(m_pEEGoSports->isRunning()) {
          stopImpedanceMeasurement();
      }
  }
  
  //=============================================================================================================
  
  bool EEGoSportsImpedanceWidget::compareChannelIndex(EEGoSportsElectrodeItem* a, EEGoSportsElectrodeItem* b)
  {
      return a->getChannelIndex() < b->getChannelIndex();
  }
  
  //=============================================================================================================
  
  void EEGoSportsImpedanceWidget::saveToFile()
  {
      // Open file dialog
      QDate date;
      QString fileName = QFileDialog::getSaveFileName(this,
                                                      "Save impedance values",
                                                      QString("%1/%2_%3_%4_Impedances").arg(QStandardPaths::writableLocation(QStandardPaths::DesktopLocation)).arg(date.currentDate().year()).arg(date.currentDate().month()).arg(date.currentDate().day()),
                                                      tr("Text file (*.txt)"));
  
      std::ofstream outputFileStream;
      outputFileStream.open(fileName.toStdString(), std::ios::trunc); //ios::trunc deletes old file data
  
      QList<QGraphicsItem *> itemList = m_qGScene->items();
  
      // Convert to QList with EEGoSportsElectrodeItem's
      QList<EEGoSportsElectrodeItem *> itemListNew;
      for(int i = 0; i<itemList.size(); i++)
          itemListNew.append((EEGoSportsElectrodeItem *)itemList.at(i));
  
      // Sort list corresponding to the channelIndex
      std::sort(itemListNew.begin(), itemListNew.end(), EEGoSportsImpedanceWidget::compareChannelIndex);
  
      // Update position
      for(int i = 0; i<itemListNew.size(); i++)
      {
          EEGoSportsElectrodeItem *item = itemListNew.at(i);
          outputFileStream << i << " " << item->getElectrodeName().toStdString() << " " << item->getImpedanceValue() << endl;
      }
  
      outputFileStream.close();
  }
  
  //=============================================================================================================
  
  void EEGoSportsImpedanceWidget::helpDialog()
  {
      QMessageBox msgBox;
      msgBox.setText("- Use mouse wheel to zoom.\n- Hold and move right mouse button to scale the electrode positions in the scene.\n- Double click to fit the scene into the view.");
      msgBox.exec();
  }
  