/*
 * Copyright 2012 SciberQuest Inc.
 * 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 name of SciberQuest Inc. nor the names of any 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 AUTHORS 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.
 */
#include "pqSQHemisphereSource.h"
#include "pqSQMacros.h"
#include "vtkSQHemisphereSourceConfigurationReader.h"
#include "vtkSQHemisphereSourceConfigurationWriter.h"

#include "pqFileDialog.h"
#include "pqPropertyLinks.h"
#include "pqProxy.h"

#include "vtkMath.h"
#include "vtkSMDoubleVectorProperty.h"
#include "vtkSMIntVectorProperty.h"
#include "vtkSMProperty.h"
#include "vtkSMPropertyHelper.h"
#include "vtkSMProxy.h"
#include "vtkSMStringVectorProperty.h"

#include "vtkEventQtSlotConnect.h"
#include "vtkProcessModule.h"

#include <QDebug>
#include <QDoubleValidator>
#include <QFileDialog>
#include <QLineEdit>
#include <QMessageBox>
#include <QPalette>
#include <QSettings>
#include <QString>

#include "FsUtils.h"
#if defined pqSQHemisphereSourceDEBUG
#include <PrintUtils.h>
#endif

#include <fstream>
#include <iostream>
#include <sstream>
#include <string>

//-----------------------------------------------------------------------------
pqSQHemisphereSource::pqSQHemisphereSource(pqProxy* l_proxy, QWidget* l_parent)
  : pqNamedObjectPanel(l_proxy, l_parent)
{
#if defined pqSQHemisphereSourceDEBUG
  std::cerr << ":::::pqSQHemisphereSource::pqSQHemisphereSource" << std::endl;
#endif

  // Construct Qt form.
  this->Form = new pqSQHemisphereSourceForm;
  this->Form->setupUi(this);

  // set up coordinate line edits
  this->Form->c_x->setValidator(new QDoubleValidator(this->Form->c_x));
  this->Form->c_y->setValidator(new QDoubleValidator(this->Form->c_y));
  this->Form->c_z->setValidator(new QDoubleValidator(this->Form->c_z));

  this->Form->n_x->setValidator(new QDoubleValidator(this->Form->n_x));
  this->Form->n_y->setValidator(new QDoubleValidator(this->Form->n_y));
  this->Form->n_z->setValidator(new QDoubleValidator(this->Form->n_z));

  this->Form->r->setValidator(new QDoubleValidator(this->Form->r));

  // Set up configuration viewer
  this->PullServerConfig();

  // set up save/restore buttons
  QObject::connect(this->Form->save, SIGNAL(clicked()), this, SLOT(saveConfiguration()));

  QObject::connect(this->Form->restore, SIGNAL(clicked()), this, SLOT(loadConfiguration()));

  // link qt to sm
  this->Links = new pqPropertyLinks;
  this->Links->setUseUncheckedProperties(false);
  this->Links->setAutoUpdateVTKObjects(true);

  QObject::connect(this->Links, SIGNAL(qtWidgetChanged()), this, SLOT(setModified()));

  vtkSMProxy* pProxy = this->referenceProxy()->getProxy();

  this->Links->addPropertyLink(this->Form->c_x, "text", SIGNAL(textChanged(QString)), pProxy,
    pProxy->GetProperty("Center"), 0);

  this->Links->addPropertyLink(this->Form->c_y, "text", SIGNAL(textChanged(QString)), pProxy,
    pProxy->GetProperty("Center"), 1);

  this->Links->addPropertyLink(this->Form->c_z, "text", SIGNAL(textChanged(QString)), pProxy,
    pProxy->GetProperty("Center"), 2);

  this->Links->addPropertyLink(
    this->Form->n_x, "text", SIGNAL(textChanged(QString)), pProxy, pProxy->GetProperty("North"), 0);

  this->Links->addPropertyLink(
    this->Form->n_y, "text", SIGNAL(textChanged(QString)), pProxy, pProxy->GetProperty("North"), 1);

  this->Links->addPropertyLink(
    this->Form->n_z, "text", SIGNAL(textChanged(QString)), pProxy, pProxy->GetProperty("North"), 2);

  this->Links->addPropertyLink(
    this->Form->r, "text", SIGNAL(textChanged(QString)), pProxy, pProxy->GetProperty("Radius"));

  this->Links->addPropertyLink(
    this->Form->res, "value", SIGNAL(valueChanged(int)), pProxy, pProxy->GetProperty("Resolution"));
}

//-----------------------------------------------------------------------------
pqSQHemisphereSource::~pqSQHemisphereSource()
{
#if defined pqSQHemisphereSourceDEBUG
  std::cerr << ":::::pqSQHemisphereSource::~pqSQHemisphereSource" << std::endl;
#endif

  delete this->Form;
  delete this->Links;
}

//-----------------------------------------------------------------------------
void pqSQHemisphereSource::Restore()
{
  QSettings settings("SciberQuest", "SciberQuestToolKit");
  QString lastUsedDir = settings.value("SQHemisphereSource/lastUsedDir", "").toString();

  QString fn =
    QFileDialog::getOpenFileName(this, "Open SQ Hemisphere Source", lastUsedDir, "*.sqhs");
  if (fn.size())
  {
    std::ifstream f(fn.toStdString().c_str(), std::ios_base::in);
    if (f.is_open())
    {
      char buf[1024];
      f.getline(buf, 1024);
      if (std::string(buf).find("SQ Hemisphere Source") != std::string::npos)
      {
        // Center
        f.getline(buf, 1024);
        f.getline(buf, 1024);
        std::istringstream* is;
        is = new std::istringstream(buf);
        double c[3];
        *is >> c[0] >> c[1] >> c[2];
        delete is;
        this->Form->c_x->setText(QString("%1").arg(c[0]));
        this->Form->c_y->setText(QString("%1").arg(c[1]));
        this->Form->c_z->setText(QString("%1").arg(c[2]));
        // North
        f.getline(buf, 1024);
        f.getline(buf, 1024);
        is = new std::istringstream(buf);
        double n[3];
        *is >> n[0] >> n[1] >> n[2];
        delete is;
        this->Form->n_x->setText(QString("%1").arg(n[0]));
        this->Form->n_y->setText(QString("%1").arg(n[1]));
        this->Form->n_z->setText(QString("%1").arg(n[2]));
        // Radius
        f.getline(buf, 1024);
        f.getline(buf, 1024);
        is = new std::istringstream(buf);
        double r;
        *is >> r;
        delete is;
        this->Form->r->setText(QString("%1").arg(r));
        // Resolution
        f.getline(buf, 1024);
        f.getline(buf, 1024);
        is = new std::istringstream(buf);
        int res;
        *is >> res;
        delete is;
        this->Form->res->setValue(res);
      }
      else
      {
        QMessageBox::warning(
          this, "Open SQ Hemisphere Source", "Error: Bad format not a SQ plane source file.");
      }
      f.close();
    }
    else
    {
      QMessageBox::warning(this, "Save SQ Hemisphere Source", "Error: Could not open the file.");
    }
  }
}

//-----------------------------------------------------------------------------
void pqSQHemisphereSource::Save()
{
  QString fn = QFileDialog::getSaveFileName(this, "Save SQ Hemisphere Source", "", "*.sqhs");

  if (fn.size())
  {
    QString lastUsedDir(QFileInfo(fn).path());
    QSettings settings("SciberQuest", "SciberQuestToolKit");
    settings.setValue("SQHemisphereSource/lastUsedDir", lastUsedDir);

    std::ofstream f(fn.toStdString().c_str(), std::ios_base::out | std::ios_base::trunc);
    if (f.is_open())
    {
      f << "SQ Hemisphere Source 1.0" << std::endl
        << "Center" << std::endl
        << this->Form->c_x->text().toDouble() << " " << this->Form->c_y->text().toDouble() << " "
        << this->Form->c_z->text().toDouble() << std::endl
        << "North" << std::endl
        << this->Form->n_x->text().toDouble() << " " << this->Form->n_y->text().toDouble() << " "
        << this->Form->n_z->text().toDouble() << std::endl
        << "Radius" << std::endl
        << this->Form->r->text().toDouble() << std::endl
        << "Resolution" << std::endl
        << this->Form->res->value() << std::endl
        << std::endl;
      f.close();
    }
    else
    {
      QMessageBox::warning(this, "Save SQ Hemisphere Source", "Error: Failed to create the file.");
    }
  }
}

//-----------------------------------------------------------------------------
void pqSQHemisphereSource::loadConfiguration()
{
  vtkSQHemisphereSourceConfigurationReader* reader =
    vtkSQHemisphereSourceConfigurationReader::New();

  reader->SetProxy(this->proxy());

  QString filters = QString("%1 (*%2);;All Files (*.*)")
                      .arg(reader->GetFileDescription())
                      .arg(reader->GetFileExtension());

  pqFileDialog dialog(0, this, "Load SQ Hemisphere Source Configuration", "", filters);
  dialog.setFileMode(pqFileDialog::ExistingFile);

  if (dialog.exec() == QDialog::Accepted)
  {
    QString filename;
    filename = dialog.getSelectedFiles()[0];

    int ok = reader->ReadConfiguration(filename.toStdString().c_str());
    if (!ok)
    {
      pqSQErrorMacro(qDebug(), "Failed to load the hemisphere source configuration.");
    }
  }

  reader->Delete();

  this->PullServerConfig();
}

//-----------------------------------------------------------------------------
void pqSQHemisphereSource::saveConfiguration()
{
#if defined pqSQHemisphereSourceDEBUG
  std::cerr << ":::::pqSQHemisphereSource::saveConfiguration" << std::endl;
#endif

  vtkSQHemisphereSourceConfigurationWriter* writer =
    vtkSQHemisphereSourceConfigurationWriter::New();

  writer->SetProxy(this->proxy());

  QString filters = QString("%1 (*%2);;All Files (*.*)")
                      .arg(writer->GetFileDescription())
                      .arg(writer->GetFileExtension());

  pqFileDialog dialog(0, this, "Save SQ Hemisphere Source Configuration", "", filters);
  dialog.setFileMode(pqFileDialog::AnyFile);

  if (dialog.exec() == QDialog::Accepted)
  {
    QString filename(dialog.getSelectedFiles()[0]);

    int ok = writer->WriteConfiguration(filename.toStdString().c_str());
    if (!ok)
    {
      pqSQErrorMacro(qDebug(), "Failed to save the hemisphere source configuration.");
    }
  }

  writer->Delete();
}

//-----------------------------------------------------------------------------
void pqSQHemisphereSource::PullServerConfig()
{
#if defined pqSQHemisphereSourceDEBUG
  std::cerr << ":::::pqSQHemisphereSource::PullServerConfig" << std::endl;
#endif

  vtkSMProxy* pProxy = this->referenceProxy()->getProxy();

  // Center
  vtkSMDoubleVectorProperty* cProp =
    dynamic_cast<vtkSMDoubleVectorProperty*>(pProxy->GetProperty("GetCenter"));
  pProxy->UpdatePropertyInformation(cProp);
  double* c = cProp->GetElements();
  this->Form->c_x->setText(QString("%1").arg(c[0]));
  this->Form->c_y->setText(QString("%1").arg(c[1]));
  this->Form->c_z->setText(QString("%1").arg(c[2]));

  // North
  vtkSMDoubleVectorProperty* nProp =
    dynamic_cast<vtkSMDoubleVectorProperty*>(pProxy->GetProperty("GetNorth"));
  pProxy->UpdatePropertyInformation(nProp);
  double* n = nProp->GetElements();
  this->Form->n_x->setText(QString("%1").arg(n[0]));
  this->Form->n_y->setText(QString("%1").arg(n[1]));
  this->Form->n_z->setText(QString("%1").arg(n[2]));

  // Radius
  vtkSMDoubleVectorProperty* rProp =
    dynamic_cast<vtkSMDoubleVectorProperty*>(pProxy->GetProperty("GetRadius"));
  pProxy->UpdatePropertyInformation(rProp);
  double r = rProp->GetElement(0);
  this->Form->r->setText(QString("%1").arg(r));

  // Resolution
  vtkSMIntVectorProperty* resProp =
    dynamic_cast<vtkSMIntVectorProperty*>(pProxy->GetProperty("GetResolution"));
  pProxy->UpdatePropertyInformation(resProp);
  int res = resProp->GetElement(0);
  this->Form->res->setValue(res);

#if defined pqSQHemisphereSourceDEBUG
  std::cerr << "Pulled: " << std::endl
            << "C   " << c[0] << ", " << c[1] << ", " << c[2] << std::endl
            << "N   " << n[0] << ", " << n[1] << ", " << n[2] << std::endl
            << "r   " << r << std::endl
            << "res " << res << std::endl
            << std::endl;
#endif
}

//-----------------------------------------------------------------------------
void pqSQHemisphereSource::PushServerConfig()
{
#if defined pqSQHemisphereSourceDEBUG
  std::cerr << ":::::pqSQHemisphereSource::PushServerConfig" << std::endl;
#endif
  vtkSMProxy* pProxy = this->referenceProxy()->getProxy();

  // Center
  double c[3];
  c[0] = this->Form->c_x->text().toDouble();
  c[1] = this->Form->c_y->text().toDouble();
  c[2] = this->Form->c_z->text().toDouble();
  vtkSMDoubleVectorProperty* cProp =
    dynamic_cast<vtkSMDoubleVectorProperty*>(pProxy->GetProperty("Center"));
  cProp->SetElements(c, 3);

  // North
  double n[3];
  n[0] = this->Form->n_x->text().toDouble();
  n[1] = this->Form->n_y->text().toDouble();
  n[2] = this->Form->n_z->text().toDouble();
  vtkSMDoubleVectorProperty* nProp =
    dynamic_cast<vtkSMDoubleVectorProperty*>(pProxy->GetProperty("North"));
  nProp->SetElements(n, 3);

  // Radius
  double r;
  r = this->Form->r->text().toDouble();
  vtkSMDoubleVectorProperty* rProp =
    dynamic_cast<vtkSMDoubleVectorProperty*>(pProxy->GetProperty("Radius"));
  rProp->SetElement(0, r);

  // Resolution
  int res;
  res = this->Form->res->value();
  vtkSMIntVectorProperty* resProp =
    dynamic_cast<vtkSMIntVectorProperty*>(pProxy->GetProperty("Resolution"));
  resProp->SetElement(0, res);

#if defined pqSQHemisphereSourceDEBUG
  std::cerr << "Pushed: " << std::endl
            << "C   " << c[0] << ", " << c[1] << ", " << c[2] << std::endl
            << "N   " << n[0] << ", " << n[1] << ", " << n[2] << std::endl
            << "r   " << r << std::endl
            << "res " << res << std::endl
            << std::endl;
#endif

  // Let proxy send updated values.
  pProxy->UpdateVTKObjects();
}

//-----------------------------------------------------------------------------
void pqSQHemisphereSource::accept()
{
#if defined pqSQHemisphereSourceDEBUG
  std::cerr << ":::::pqSQHemisphereSource::accept" << std::endl;
#endif

  pqNamedObjectPanel::accept();
}

//-----------------------------------------------------------------------------
void pqSQHemisphereSource::reset()
{
#if defined pqSQHemisphereSourceDEBUG
  std::cerr << ":::::pqSQHemisphereSource::reset" << std::endl;
#endif

  pqNamedObjectPanel::reset();
}
