// Copyright (C) 2007-2013  CEA/DEN, EDF R&D, OPEN CASCADE
//
// Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License.
//
// This library 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
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
//
// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
//

// File   : StdMeshersGUI_NbSegmentsCreator.cxx
// Author : Open CASCADE S.A.S.
// SMESH includes
//
#include "StdMeshersGUI_NbSegmentsCreator.h"
#include "StdMeshersGUI_DistrTable.h"
#include "StdMeshersGUI_DistrPreview.h"
#include "StdMeshersGUI_SubShapeSelectorWdg.h"

#include <SMESHGUI.h>
#include <SMESHGUI_Utils.h>
#include <SMESHGUI_HypothesesUtils.h>
#include <SMESHGUI_SpinBox.h>

// IDL includes
#include CORBA_SERVER_HEADER(SMESH_BasicHypothesis)

// SALOME GUI includes
#include <SalomeApp_Tools.h>
#include <SalomeApp_IntSpinBox.h>
#include <QtxComboBox.h>

// Qt includes
#include <QLabel>
#include <QGroupBox>
#include <QFrame>
#include <QLineEdit>
#include <QButtonGroup>
#include <QRadioButton>
#include <QGridLayout>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QApplication>

#define SPACING 6
#define MARGIN  11

StdMeshersGUI_NbSegmentsCreator::StdMeshersGUI_NbSegmentsCreator()
: StdMeshersGUI_StdHypothesisCreator( "NumberOfSegments" ),
  myNbSeg( 0 ),
  myDistr( 0 ),
  myScale( 0 ),
  myTable( 0 ),
  myPreview( 0 ),
  myExpr( 0 ),
  myConvBox( 0 ),
  myConv( 0 ),
  myLScale( 0 ),
  myLTable( 0 ),
  myLExpr( 0 ),
  myInfo( 0 ),
  myGroupLayout( 0 ),
  myTableRow( 0 ),
  myPreviewRow( 0 )
{
}

StdMeshersGUI_NbSegmentsCreator::~StdMeshersGUI_NbSegmentsCreator()
{
}

bool StdMeshersGUI_NbSegmentsCreator::checkParams( QString& msg ) const
{
  if( !SMESHGUI_GenericHypothesisCreator::checkParams( msg ) )
    return false;
  NbSegmentsHypothesisData data_old, data_new;
  readParamsFromHypo( data_old );
  readParamsFromWidgets( data_new );
  bool res = storeParamsToHypo( data_new );
  storeParamsToHypo( data_old );
  res = myNbSeg->isValid( msg, true ) && res;
  res = myScale->isValid( msg, true ) && res;
  return res;
}

QFrame* StdMeshersGUI_NbSegmentsCreator::buildFrame()
{
  QFrame* fr = new QFrame();
  fr->setMinimumWidth(460);

  QVBoxLayout* lay = new QVBoxLayout( fr );
  lay->setMargin( 0 );
  lay->setSpacing( SPACING );

  QGroupBox* GroupC1 = new QGroupBox( tr( "SMESH_ARGUMENTS" ), fr );
  lay->addWidget( GroupC1 );

  StdMeshers::StdMeshers_NumberOfSegments_var h =
    StdMeshers::StdMeshers_NumberOfSegments::_narrow( hypothesis() );

  myGroupLayout = new QGridLayout( GroupC1 );
  myGroupLayout->setSpacing( SPACING );
  myGroupLayout->setMargin( MARGIN );
  myGroupLayout->setColumnStretch( 0, 0 );
  myGroupLayout->setColumnStretch( 1, 1 );

  int row = 0;
  // 0)  name
  myName = 0;
  if( isCreation() )
  {
    myName = new QLineEdit( GroupC1 );
    myGroupLayout->addWidget( new QLabel( tr( "SMESH_NAME" ), GroupC1 ), row, 0 );
    myGroupLayout->addWidget( myName, row, 1 );
    row++;
  }


  // 1)  number of segments
  myGroupLayout->addWidget( new QLabel( tr( "SMESH_NB_SEGMENTS_PARAM" ), GroupC1 ), row, 0 );
  myNbSeg = new SalomeApp_IntSpinBox( GroupC1 );
  myNbSeg->setMinimum( 1 );
  myNbSeg->setMaximum( 9999 );
  myGroupLayout->addWidget( myNbSeg, row, 1 );
  row++;

  
  // 2)  type of distribution
  myGroupLayout->addWidget( new QLabel( tr( "SMESH_DISTR_TYPE" ), GroupC1 ), row, 0 );
  myDistr = new QtxComboBox( GroupC1 );
  QStringList types;
  types.append( tr( "SMESH_DISTR_REGULAR" ) );
  types.append( tr( "SMESH_DISTR_SCALE"   ) );
  types.append( tr( "SMESH_DISTR_TAB"     ) );
  types.append( tr( "SMESH_DISTR_EXPR"    ) );
  myDistr->addItems( types );
  myGroupLayout->addWidget( myDistr, row, 1 );
  row++;

  
  // 3)  scale
  myGroupLayout->addWidget( myLScale = new QLabel( tr( "SMESH_NB_SEGMENTS_SCALE_PARAM" ), GroupC1 ), row, 0 );
  myScale = new SMESHGUI_SpinBox( GroupC1 );
  myScale->RangeStepAndValidator( 1E-5, 1E+5, 0.1, "parametric_precision" );
  myGroupLayout->addWidget( myScale, row, 1 );
  row++;

  
  // 4) Distribution definition
  QGridLayout* myDistLayout = new QGridLayout(GroupC1);
  myGroupLayout->addLayout( myDistLayout, row, 0, 1, 2 );
  myGroupLayout->setRowStretch( row, 1 );
  row ++;

       // a)  expression
  QHBoxLayout* myExprLayout = new QHBoxLayout(GroupC1);
  myExprLayout->addWidget( myLExpr = new QLabel( "f(t)=", GroupC1 ), 0);
  myExpr = new QLineEdit( GroupC1 );
  myExprLayout->addWidget( myExpr,1);
  myDistLayout->addLayout(myExprLayout,1 ,0);
  myDistLayout->setRowStretch(2, 1);

       // b)  warning
  myInfo = new QLabel( tr( "SMESH_FUNC_DOMAIN" ), GroupC1 );
  myDistLayout->addWidget( myInfo, 0, 0, 1, 2);
  
       // c)  table
  myTable = new StdMeshersGUI_DistrTableFrame( GroupC1 );
  myDistLayout->addWidget( myTable, 1, 0, 2, 1 );

       // d) preview
  myPreview = new StdMeshersGUI_DistrPreview( GroupC1, h.in() );  
  myPreview->setMinimumHeight(220);
  myDistLayout->addWidget( myPreview, 1, 1, 2, 1 );
  
  // 5)  conversion (radiogroup)
  myConvBox = new QGroupBox( tr( "SMESH_CONV_MODE" ), GroupC1 );
  myConv = new QButtonGroup( GroupC1 );

  QHBoxLayout* convLay = new QHBoxLayout( myConvBox );
  convLay->setMargin( MARGIN );
  convLay->setSpacing( SPACING );

  QRadioButton* rbExp = new QRadioButton( tr( "SMESH_EXP_MODE" ), myConvBox );
  QRadioButton* myCutNeg = new QRadioButton( tr( "SMESH_CUT_NEG_MODE" ), myConvBox );

  convLay->addWidget( rbExp );
  convLay->addWidget( myCutNeg );
  myConv->addButton( rbExp, 0 );
  myConv->addButton( myCutNeg, 1 );

  myGroupLayout->addWidget( myConvBox, row, 0, 1, 2 );
  row++;


  // 6) reverse edge parameters
  myReversedEdgesBox = new QGroupBox(tr( "SMESH_REVERSED_EDGES" ), fr);
  QHBoxLayout* edgeLay = new QHBoxLayout( myReversedEdgesBox );

  myDirectionWidget = new StdMeshersGUI_SubShapeSelectorWdg( myReversedEdgesBox );
  QString aGeomEntry = getShapeEntry();
  QString aMainEntry = getMainShapeEntry();
  if ( aGeomEntry == "" )
    aGeomEntry = h->GetObjectEntry();
  myDirectionWidget->SetGeomShapeEntry( aGeomEntry );
  myDirectionWidget->SetMainShapeEntry( aMainEntry );
  myDirectionWidget->SetListOfIDs( h->GetReversedEdges() );
  edgeLay->addWidget( myDirectionWidget );

  lay->addWidget( myReversedEdgesBox );
  lay->setStretchFactor( GroupC1, 2);
  lay->setStretchFactor( myReversedEdgesBox, 1);
  
  connect( myNbSeg, SIGNAL( valueChanged( const QString& ) ), this, SLOT( onValueChanged() ) );
  connect( myDistr, SIGNAL( activated( int ) ), this, SLOT( onValueChanged() ) );
  connect( myTable, SIGNAL( valueChanged( int, int ) ), this, SLOT( onValueChanged() ) );
  connect( myExpr,  SIGNAL( textChanged( const QString& ) ), this, SLOT( onValueChanged() ) );
  connect( myConv,  SIGNAL( buttonClicked( int ) ), this, SLOT( onValueChanged() ) );

  return fr;
}

void StdMeshersGUI_NbSegmentsCreator::retrieveParams() const
{
  NbSegmentsHypothesisData data;
  readParamsFromHypo( data );

  if( myName )
    myName->setText( data.myName );
  if(data.myNbSegVarName.isEmpty())
    myNbSeg->setValue( data.myNbSeg );
  else
    myNbSeg->setText( data.myNbSegVarName );
  
  myDistr->setCurrentIndex( data.myDistrType );
  if(data.myScaleVarName.isEmpty())
    myScale->setValue( data.myScale );
  else
    myScale->setText( data.myScaleVarName );
  myConv->button( data.myConv )->setChecked( true );
  myTable->setFuncMinValue(myConv->checkedId()==0 ? -1E20 : 0);
  myTable->setData( data.myTable );
  myExpr->setText( data.myExpr );

  if ( dlg() )
    dlg()->setMinimumSize( dlg()->minimumSizeHint().width(), dlg()->minimumSizeHint().height() );
}

QString StdMeshersGUI_NbSegmentsCreator::storeParams() const
{
  NbSegmentsHypothesisData data;
  readParamsFromWidgets( data );
  storeParamsToHypo( data );
    
  QString valStr = QString::number( data.myNbSeg ) += "; ";

  enum DistrType
  {
    Regular, //!< equidistant distribution
    Scale,   //!< scale distribution
    TabFunc, //!< distribution with density function presented by table
    ExprFunc //!< distribution with density function presented by expression
  };
  bool hasConv = false;
  switch ( data.myDistrType ) {
  case Regular :
    valStr += tr("SMESH_DISTR_REGULAR");
    break;
  case Scale   : 
    valStr += tr("SMESH_NB_SEGMENTS_SCALE_PARAM") + " = " + QString::number( data.myScale );\
    break;
  case TabFunc : {
    //valStr += tr("SMESH_TAB_FUNC");
    bool param = true;
    for( int i=0; i < data.myTable.length(); i++, param = !param ) {
      if ( param )
        valStr += "[";
      valStr += QString::number( data.myTable[ i ]);
      valStr += ( param ? "," : "]" );
    }
    hasConv = true;
    break;
  }
  case ExprFunc:
    valStr += data.myExpr;
    hasConv = true;
    break;
  }
  if ( hasConv )
  {
    if ( data.myConv )
      valStr += "; " + tr("SMESH_CUT_NEG_MODE");
    else
      valStr += "; " + tr("SMESH_EXP_MODE");
  }

  return valStr;
}

bool StdMeshersGUI_NbSegmentsCreator::readParamsFromHypo( NbSegmentsHypothesisData& h_data ) const
{
  StdMeshers::StdMeshers_NumberOfSegments_var h =
    StdMeshers::StdMeshers_NumberOfSegments::_narrow( initParamsHypothesis() );

  h_data.myName = hypName();

  h_data.myNbSeg = (int) h->GetNumberOfSegments();

  CORBA::String_var aVaribaleName = h->GetVarParameter( "SetNumberOfSegments" );
  h_data.myNbSegVarName = aVaribaleName.in();

  int distr = (int) h->GetDistrType();
  h_data.myDistrType = distr;
  h_data.myScale = distr==1 ? h->GetScaleFactor() : 1.0;
  
  if(distr==1) {
    aVaribaleName = h->GetVarParameter( "SetScaleFactor" );
    h_data.myScaleVarName = aVaribaleName.in();
  }
  else 
    h_data.myScaleVarName = QString("");

  if( distr==2 )
  {
    SMESH::double_array* a = h->GetTableFunction();
    h_data.myTable = *a;
    delete a;
  }
  else
  {
    SMESH::double_array& a = h_data.myTable;
    // by default, constant table function f(t)=1
    a.length( 4 );
    a[0] = 0.0; a[1] = 1.0;
    a[2] = 1.0; a[3] = 1.0; 
  }

  h_data.myExpr = distr==3 ? h->GetExpressionFunction() : "1";
  h_data.myConv = distr==2 || distr==3 ? h->ConversionMode() : 1; /*cut negative by default*/

  return true;
}

bool StdMeshersGUI_NbSegmentsCreator::storeParamsToHypo( const NbSegmentsHypothesisData& h_data ) const
{
  StdMeshers::StdMeshers_NumberOfSegments_var h =
    StdMeshers::StdMeshers_NumberOfSegments::_narrow( hypothesis() );

  bool ok = true;
  try
  {
    if( isCreation() )
      SMESH::SetName( SMESH::FindSObject( h ), h_data.myName.toLatin1().data() );

    h->SetVarParameter( h_data.myNbSegVarName.toLatin1().constData(), "SetNumberOfSegments" );
    h->SetNumberOfSegments( h_data.myNbSeg );
    int distr = h_data.myDistrType;
    h->SetDistrType( distr );
    
    if( distr==1 ) {
      h->SetVarParameter( h_data.myScaleVarName.toLatin1().constData(), "SetScaleFactor" );
      h->SetScaleFactor( h_data.myScale );
    }
    if( distr==2 || distr==3 )
      h->SetConversionMode( h_data.myConv );

    if( distr==1 || distr==2 || distr==3 ) {
      h->SetReversedEdges( myDirectionWidget->GetListOfIDs() );
      h->SetObjectEntry( myDirectionWidget->GetMainShapeEntry() );
    }

    if( distr==2 )
      h->SetTableFunction( h_data.myTable );

    if( distr==3 )
      h->SetExpressionFunction( h_data.myExpr.toLatin1().data() );
    //setting of function must follow after setConversionMode, because otherwise
    //the function will be checked with old conversion mode, so that it may occurs
    //unexpected errors for user
  }
  catch(const SALOME::SALOME_Exception& ex)
  {
    SalomeApp_Tools::QtCatchCorbaException(ex);
    ok = false;
  }
  return ok;
}

bool StdMeshersGUI_NbSegmentsCreator::readParamsFromWidgets( NbSegmentsHypothesisData& h_data ) const
{
  h_data.myName      = myName ? myName->text() : "";
  h_data.myNbSeg     = myNbSeg->value();
  h_data.myNbSegVarName =  myNbSeg->text();
  h_data.myScaleVarName =  myScale->text();
  h_data.myDistrType = myDistr->currentIndex();
  h_data.myConv      = myConv->checkedId();
  h_data.myScale     = myScale->value();
  myTable->data( h_data.myTable );
  h_data.myExpr      = myExpr->text();
  return true;
}

void StdMeshersGUI_NbSegmentsCreator::onValueChanged()
{
  int distr = myDistr->currentIndex();

/*  if( distr==2 ) //table func
    myCutNeg->setText( tr( "SMESH_NO_CONV" ) );
  else if( distr==3 )
    myCutNeg->setText( tr( "SMESH_CUT_NEG_MODE" ) );*/

  if( distr==2 && sender()==myConv ) //table func
  {
    myTable->setFuncMinValue( myConv->checkedId()==0 ? -1E20 : 0 );
    SMESH::double_array arr;
    myTable->data( arr );
    myTable->setData( arr ); //update data in table
  }

  myScale->setShown( distr==1 );
  myLScale->setShown( distr==1 );
  myReversedEdgesBox->setShown( !distr==0 );
  myDirectionWidget->showPreview( !distr==0 );

  bool isFunc = distr==2 || distr==3;
  myPreview->setShown( isFunc );
  myConvBox->setShown( isFunc );
  
  myTable->setShown( distr==2 );
  myExpr->setShown( distr==3 );
  myLExpr->setShown( distr==3 );
  myInfo->setShown( distr==3);

  //change of preview
  int nbSeg = myNbSeg->value();
  if( distr==2 ) //preview for table-described function
  {
    SMESH::double_array a;
    myTable->data( a );
    myPreview->setParams( a, nbSeg, false );
  }
  else if( distr==3 ) //preview for analytic-described function
    myPreview->setParams( myExpr->text(), nbSeg, 100, false );

  if( isFunc )
    myPreview->setConversion( StdMeshersGUI_DistrPreview::Conversion( myConv->checkedId() ) );

  if ( (QtxComboBox*)sender() == myDistr && dlg() ) {
    QApplication::instance()->processEvents();
    myGroupLayout->invalidate();
    dlg()->layout()->invalidate();
    dlg()->updateGeometry();
    dlg()->setMinimumSize( dlg()->minimumSizeHint() );
    dlg()->resize( dlg()->minimumSize() );
    QApplication::instance()->processEvents();
  }
}
