/***************************************************************************
**
**  This file is part of dinverdc.
**
**  dinverdc 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.
**
**  dinverdc 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-10-31
**  Copyright: 2005-2019
**    Marc Wathelet
**    Marc Wathelet (LGIT, Grenoble, France)
**
***************************************************************************/

#include <DinverDCCore.h>
#include <DinverGui.h>
#include <QGpCoreTools.h>
#include "ParamLayerWidget.h"
#include "ParamProfileWidget.h"

/*
 *  Constructs a ParamLayerWidget as a child of 'parent', with the
 *  name 'name' and widget flags set to 'f'.
 */
ParamLayerWidget::ParamLayerWidget(QWidget *parent)
    : QWidget(parent)
{
  TRACE;
  setupUi(this);
  _profile=nullptr;
  _index=-1;

  QPalette palette;
  palette.setColor(topLine->foregroundRole(), Qt::darkGray);
  topLine->setPalette(palette);
  middleLine->setPalette(palette);
}

/*
 *  Destroys the object and frees any allocated resources
 */
ParamLayerWidget::~ParamLayerWidget()
{
  TRACE;
  // no need to delete child widgets, Qt does it all for us
}

/*!
  Call it once to propagate profile information to this layer.
  If \a v is not null, default values are copied from v
*/
void ParamLayerWidget::setProfile(ParamProfileWidget * p, ParamLayerWidget * v)
{
  TRACE;
  _profile=p;
  if(_profile) {
    topParam->pUnit->setText(p->unit());
    if(v) {
      topParam->pMin->setText(v->topParam->pMin->text());
      topParam->pMax->setText(v->topParam->pMax->text());
      bottomParam->pMin->setText(v->bottomParam->pMin->text());
      bottomParam->pMax->setText(v->bottomParam->pMax->text());
      topParam->pFixed->setChecked(v->topParam->pFixed->isChecked());
      bottomParam->pFixed->setChecked(v->bottomParam->pFixed->isChecked());
    } else {
      QString min=Number::toString(p->defaultMinimum(), 'g', 6);
      QString max=Number::toString(p->defaultMaximum(), 'g', 6);
      topParam->pMin->setText(min);
      topParam->pMax->setText(max);
      bottomParam->pUnit->setText(p->unit());
      bottomParam->pMin->setText(min);
      bottomParam->pMax->setText(max);
      if(p->defaultMinimum()==p->defaultMaximum()) {
        topParam->pFixed->setChecked(true);
        bottomParam->pFixed->setChecked(true);
      }
    }
  }
  if(v) {
    dhParam->pUnit->setText(v->dhParam->pUnit->text());
    dhParam->pMin->setText(v->dhParam->pMin->text());
    dhParam->pMax->setText(v->dhParam->pMax->text());
  } else {
    dhParam->pUnit->setText("m");
    dhParam->pMin->setText("1");
    dhParam->pMax->setText("1000");
  }

  on_shape_activated(0);
  on_depthThickness_activated(0);
  on_linkedTo_activated(0);
}

bool ParamLayerWidget::tilted() const
{
  TRACE;
  return depthThickness->currentIndex()>1 || (_index==0 && depthThickness->currentIndex()>0);
}

bool ParamLayerWidget::isDepth() const
{
  TRACE;
  if(_index ==0) {
    return true;
  } else {
    switch(depthThickness->currentIndex()) {
    case 0:
    case 2:
      return true;
    default:
      return false;
    }
  }
}

/*!
  Call it every time the index of this layer may change (move up or down the layer)
*/
void ParamLayerWidget::setIndex(int index)
{
  TRACE;
  _index=index;
  QString str=name();
  topParam->pName->setText( "Top " + str + ":" );
  bottomParam->pName->setText( "Bottom " + str + ":" );
  if(index > 0) {
    switch(_profile->defaultCondition()) {
    case SimpleCondition::LessThan:
      lastParamCondition->show();
      lastParamCondition->setText(( _profile->shortName() + "%1" ).arg(index - 1) + " < " + str);
      break;
    case SimpleCondition::GreaterThan:
      lastParamCondition->show();
      lastParamCondition->setText(( _profile->shortName() + "%1" ).arg(index - 1) + " > " + str);
      break;
    case SimpleCondition::NoCondition:
      lastParamCondition->setChecked(false);
      lastParamCondition->hide();
      break;
    }
  } else {
    lastParamCondition->hide();
    while(depthThickness->count()>1) depthThickness->removeItem(1);
    depthThickness->setCurrentIndex(0);
  }
  on_linkedTo_activated(0);
  on_depthThickness_activated(0);
  if(_profile && _index==_profile->nLayers()-1) {
    depthThickness->hide();
    linkedTo->hide();
    linkedToLabel->hide();
    dhParam->hide();
  } else {
    depthThickness->show();
    linkedTo->show();
    linkedToLabel->show();
    if(linkedTo->currentIndex()==0) {
      dhParam->show();
    }
  }
  on_shape_activated(0);
  emit updateDHLinks();
}

QString ParamLayerWidget::name()
{
  TRACE;
  return ParamLayer::name(_profile->shortName(), _index);
}

void ParamLayerWidget::on_shape_activated(int)
{
  TRACE;
  if(_profile && _index==_profile->nLayers()-1) {
    if(shape->currentIndex()!=0)
      Message::warning(MSG_ID, "Layer", "Nothing else than Uniform is accepted "
                           "for the bottom half-space.", Message::ok());
    shape->setCurrentIndex(0);
  }
  if(shape->currentIndex()>0) {
    bottomParam->show();
    topParam->pName->setText("Top "+name()+":");
    nSubLayersLabel->show();
    nSubLayers->show();
  } else {
    bottomParam->hide();
    topParam->pName->setText(name()+":");
    nSubLayersLabel->hide();
    nSubLayers->hide();
  }
}

void ParamLayerWidget::on_depthThickness_activated(int)
{
  TRACE;
  switch (depthThickness->currentIndex()) {
  case 1:
    if(_index>0) {
      dhParam->pName->setText( "H" + name() + ":" );
    } else {
      dhParam->pName->setText( "D" + name() + ":" );
    }
    break;
  default:
    dhParam->pName->setText( "D" + name() + ":" );
    break;
  }
}

void ParamLayerWidget::on_linkedTo_activated(int)
{
  TRACE;
  if(linkedTo->currentIndex() > -1) {
    if(linkedTo->currentIndex() > 0 || _index==_profile->nLayers()-1) {
      dhParam->hide();
      depthThickness->hide();
    } else {
      depthThickness->show();
      dhParam->show();
      if(_index==0) depthThickness->setCurrentIndex(0);
    }
  }
}

bool ParamLayerWidget::isSelected()
{
  TRACE;
  return selected->isChecked();
}

void ParamLayerWidget::on_selected_clicked()
{
  TRACE;
  selected->setChecked(true);
}


void ParamLayerWidget::on_selected_toggled(bool)
{
  TRACE;
  if(selected->isChecked()) {
    emit wantToBeSelected();
    selected->blockSignals(true);
    selected->setChecked(true);
    selected->blockSignals(false);
  }
}

/*!
  Set the list of possible links only if the layer is already initialized (_iLayer>=0)
*/
void ParamLayerWidget::setDHLinks(QStringList& links)
{
  TRACE;
  if(_index==-1) return;
  QString linkName=linkedTo->currentText();
  linkedTo->blockSignals(true);
  linkedTo->clear();
  linkedTo->addItem( "Not linked" );
  for(QStringList::Iterator it=links.begin();it!=links.end();++it) {
    if(*it!=name()) linkedTo->addItem( *it);
  }
  linkedTo->blockSignals(false);
  setCurrentLink(linkName);
}

ParamLayer * ParamLayerWidget::paramLayer(ParamProfile * profile)
{
  TRACE;
  ParamLayer * layer=new ParamLayer(profile, _index);
  switch (shape->currentIndex()) {
  case 1: layer->setShape(ParamLayer::Linear); break;
  case 2: layer->setShape(ParamLayer::LinearIncrease); break;
  case 3: layer->setShape(ParamLayer::LinearDecrease); break;
  case 4: layer->setShape(ParamLayer::PowerLaw); break;
  default: layer->setShape(ParamLayer::Uniform); break;
  }
  layer->setLastParamCondition(lastParamCondition->isChecked());
  layer->setSubLayerCount(nSubLayers->value());
  double val;
  bool ok=true;
  val=Number::toDouble(topParam->pMin->text(), ok);
  if(ok) {
    layer->setTopMinimumValue(val);
  }
  if(topParam->pFixed->isChecked()) {
    layer->setTopMaximumValue(layer->topMinimumValue());
  } else {
    val=Number::toDouble(topParam->pMax->text(), ok);
    if(ok) {
      layer->setTopMaximumValue(val);
    }
  }
  val=Number::toDouble(bottomParam->pMin->text(), ok);
  if(ok) {
    layer->setBottomMinimumValue(val);
  }
  if(bottomParam->pFixed->isChecked()) {
    layer->setBottomMaximumValue(layer->bottomMinimumValue());
  } else {
    val=Number::toDouble(bottomParam->pMax->text(), ok);
    if(ok) {
      layer->setBottomMaximumValue(val);
    }
  }
  layer->setLinkedTo(linkedTo->currentText());
  layer->setDepth(isDepth());
  val=Number::toDouble(dhParam->pMin->text(), ok);
  if(ok) {
    layer->setMinimumDepth(val);
  }
  if(dhParam->pFixed->isChecked()) {
    layer->setMaximumDepth(layer->minimumDepth());
  } else {
    val=Number::toDouble(dhParam->pMax->text(), ok);
    if(ok) {
      layer->setMaximumDepth(val);
    }
  }
  // TODO: currently does not report errors, to implement?
  return layer;
}

void ParamLayerWidget::setFrom(ParamLayer * p)
{
  TRACE;
  QString str=name();
  int iShape=0;
  switch (p->shape()) {
  case ParamLayer::Linear: iShape=1; break;
  case ParamLayer::LinearIncrease: iShape=2; break;
  case ParamLayer::LinearDecrease: iShape=3; break;
  case ParamLayer::PowerLaw: iShape=4; break;
  case ParamLayer::Uniform: iShape=0; break;
  }
  shape->setCurrentIndex(iShape);
  if(_index>0) {
    switch(_profile->defaultCondition()) {
    case SimpleCondition::LessThan:
      lastParamCondition->setText(( _profile->shortName() + "%1" ).arg(_index - 1) + " < " + str);
      lastParamCondition->setChecked(p->isLastParamCondition());
      lastParamCondition->show();
      break;
    case SimpleCondition::GreaterThan:
      lastParamCondition->setText(( _profile->shortName() + "%1" ).arg(_index - 1) + " > " + str);
      lastParamCondition->setChecked(p->isLastParamCondition());
      lastParamCondition->show();
      break;
    case SimpleCondition::NoCondition:
      lastParamCondition->setChecked(false);
      lastParamCondition->hide();
    }
  }

  nSubLayers->setValue(p->gradientProfile().subLayerCount());

  topParam->pMin->setText(Number::toString(p->topMinimumValue(), 'g', 6));
  topParam->pMax->setText(Number::toString(p->topMaximumValue(), 'g', 6));
  topParam->pFixed->setChecked(p->topMinimumValue()==p->topMaximumValue());
  bottomParam->pMin->setText(Number::toString(p->bottomMinimumValue(), 'g', 6));
  bottomParam->pMax->setText(Number::toString(p->bottomMaximumValue(), 'g', 6));
  bottomParam->pFixed->setChecked(p->bottomMinimumValue()==p->bottomMaximumValue());

  dhParam->pMin->setText(Number::toString(p->minimumDepth(), 'g', 6));
  dhParam->pMax->setText(Number::toString(p->maximumDepth(), 'g', 6));
  dhParam->pFixed->setChecked(p->minimumDepth()==p->maximumDepth());
  depthThickness->setCurrentIndex(p->isDepth() ? 0 : 1);
}

void ParamLayerWidget::setLinksFrom(ParamLayer * p)
{
  TRACE;
  setCurrentLink(p->linkedTo());
}

void ParamLayerWidget::setCurrentLink(QString link)
{
  TRACE;
  int n=linkedTo->count();
  for(int i=0;i < n;i++ ) {
    if(linkedTo->itemText(i)==link) {
      linkedTo->setCurrentIndex(i);
      on_linkedTo_activated(0);
      return;
    }
  }
  linkedTo->setCurrentIndex(0);
  on_linkedTo_activated(0);
}

void ParamLayerWidget::setEditable(bool e)
{
  TRACE;
  selected->setEnabled(e);
  shape->setEnabled(e);
  lastParamCondition->setEnabled(e);
  nSubLayers->setEnabled(e);
  nSubLayersLabel->setEnabled(e);
  linkedTo->setEnabled(e);
  linkedToLabel->setEnabled(e);
  depthThickness->setEnabled(e);
  topParam->setEnabled(e);
  bottomParam->setEnabled(e);
  dhParam->setEnabled(e);
}
