//  Copyright (C) 2007-2010  CEA/DEN, EDF R&D
//
//  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
//

// Partitioning/decimation module for the SALOME v3.2 platform
//
/**
 * \file    MULTIPR_i.hxx
 *
 * \brief   C++ implementation of the CORBA interface of the MULTIPR module.
 *
 * \author  Olivier LE ROUX - CS, Virtual Reality Dpt
 *
 * \date    01/2007
 */


//*****************************************************************************
// Includes section
//*****************************************************************************

#ifndef __MULTIPR_IMPLEMENTATION_CORBA__
#define __MULTIPR_IMPLEMENTATION_CORBA__

#include <SALOMEconfig.h>
#include CORBA_SERVER_HEADER(MULTIPR)
#include "SALOME_Component_i.hxx"
#include "Utils_CorbaException.hxx"

#include "SALOME_GenericObj_i.hh"

#include "MULTIPR_Obj.hxx"

#include <map>
#include <vector>
#include <string>

class MULTIPR_Gen_i;

//*****************************************************************************
// Class MULTIPR_Obj_i
// C++ implementation of the MULTIPR_Obj CORBA interface
// This class is a wrapper to encapsulate MULTIPR_Obj
//*****************************************************************************

class MULTIPR_Obj_i :
    public POA_MULTIPR_ORB::MULTIPR_Obj,
    public virtual SALOME::GenericObj_i
{

public:

    /**
     * Constructor.
     * Associate a MED file (sequential or distributed) with this object.
     * \param  pMEDFilename MED file to be associated with this object.
     */
    MULTIPR_Obj_i (PortableServer::POA_ptr thePOA,
                   const char* pMEDFilename,
                   bool isPersistence = false,
                   bool isMultifile   = false)
        throw (SALOME::SALOME_Exception);

    /**
     * Destructor.
     */
    virtual ~MULTIPR_Obj_i();

    //---------------------------------------------------------------------
    // Basic accessors/mutators
    //--------------------------------------------------------------------

    /**
     * Reset the object.
     */
    void    reset()
        throw (SALOME::SALOME_Exception);

    /**
     * Returns true iff this obj represents a valid sequential MED file.
     * \return true iff this obj represents a valid sequential MED file.
     */
    CORBA::Boolean isValidSequentialMEDFile()
        throw (SALOME::SALOME_Exception);

    /**
     * Returns true iff this obj represents a valid distributed MED file.
     * \return true iff this obj represents a valid distributed  MED file.
     */
    CORBA::Boolean isValidDistributedMEDFile()
        throw (SALOME::SALOME_Exception);

    /**
     * Returns the name of the associated MED file.
     * \return the name of the associated MED file.
     */
    char* getFilename()
        throw (SALOME::SALOME_Exception);

    /**
     * Returns the name of the associated sequential MED file (for a distributed MED file).
     * \return the name of the associated sequential MED file (for a distributed MED file).
     */
    char* getSeqFilename()
        throw (SALOME::SALOME_Exception);

    /**
     * Defines the mesh to be processed.
     * \param  pMeshName name of the mesh to be partitionned.
     */
    void setMesh(const char* pMeshName)
        throw (SALOME::SALOME_Exception);

    /**
     * Returns a name of the mesh to be processed.
     * \return a name of the mesh to be processed.
     */
    std::string getMeshName() const
        throw (SALOME::SALOME_Exception);

    /**
     * Sets boxing parameters for decimation (100 by default).
     * \param  pBoxing number of cells along each axis of the grid
     * (= acceleration structure) ; should be in [1..200].
     */
    void setBoxing(CORBA::Long pBoxing)
        throw (SALOME::SALOME_Exception);

    /**
     * Returns currently set boxing parameter for decimation.
     * \return currently set boxing parameter for decimation.
     */
    int getBoxing() const { return mBoxing; }

    /**
     * Returns the list of meshes contained in the sequential MED file.
     * Assumes this object encapsulates a sequential MED file.
     * \return the list of meshes contained in the sequential MED file.
     */
    MULTIPR_ORB::string_array* getMeshes()
        throw (SALOME::SALOME_Exception);

    /**
     * Returns the list of fields contained in the sequential MED file.
     * Assumes this object encapsulates a sequential MED file.
     * \param pPartList The list of parts to get the fields from (separator is '|').
     * \return the list of fields contained in the sequential MED file.
     */
    MULTIPR_ORB::string_array* getFields(const char* pPartList)
        throw (SALOME::SALOME_Exception);

    /**
     * Returns the number of timestamps for a given field.
     * Assumes this object encapsulates a sequential MED file.
     * \param pPartList The list of parts to get the fields from (separator is '|').
     * \param  pFieldName name of any field.
     * \return the number of timestamps for a given field; 0 if field not found.
     */
    CORBA::Long getTimeStamps(const char* pPartList, const char* pFieldName)
        throw (SALOME::SALOME_Exception);

    /**
     * Get the minimum and maximum value of a part's field.
     * \param pPartName The name of the part.
     * \param pFieldName The name of the field.
     * \param pMin The mininum value to fill.
     * \param pMax The maxinum value to fill.
     */
    void getFieldMinMax(const char* pPartName, const char* pFieldName, CORBA::Float& pMin, CORBA::Float& pMax)
       throw (SALOME::SALOME_Exception);

    /**
     * Returns the name of all partitions.
     * Assumes this object encapsulates a distributed MED file.
     * \return the name of all partitions.
     */
    MULTIPR_ORB::string_array* getParts()
        throw (SALOME::SALOME_Exception);

    /**
     * Returns all information abour a part.
     * Assumes this object encapsulates a distributed MED file.
     * \param  pPartName name of the part.
     * \return information about a part.
     */
    char* getPartInfo(const char* pPartName)
        throw (SALOME::SALOME_Exception);

    //---------------------------------------------------------------------
    // Algorithms
    //---------------------------------------------------------------------

    /**
     * Creates a distributed MED file (v2.3) by extracting all the groups from the current mesh of the current MED sequential MED file.
     *         Assumes:
     *         - the file is in MED format and can be read using MED file v2.3.
     *         - the file is sequential (not a distributed MED).
     *         - the file only contains TETRA10 elements (dimension of space and mesh is 3).
     *         - the file have no profil.
     * \return the name of each part.
     */
    MULTIPR_ORB::string_array* partitionneDomaine()
        throw (SALOME::SALOME_Exception);

    /**
     * Creates a distributed MED file (V2.3) by splitting a group of a MED file previously created by partitionneDomaine.
     *         Assumes:
     *         - the file is a distributed MED file, previously created by partitionneDomaine()
     *           (=> each part only contain 1 mesh, TETRA10 elements only)
     *         - nbPart > 1
     * \param  pPartName     name of the part to be splitted.
     * \param  pNbParts      number of parts; must be > 1.
     * \param  pPartitionner use value 0=MULTIPR_METIS for Metis or 1=MULTIPR_SCOTCH for Scotch.
     * \return the name of each part.
     */
    MULTIPR_ORB::string_array* partitionneGroupe(
        const char* pPartName,
        CORBA::Long pNbParts,
        CORBA::Long pPartitionner)
        throw (SALOME::SALOME_Exception);

    /**
     * Creates 3 resolutions of the given part of a distributed MED file (V2.3).
     *         Assumes:
     *         - the file is a distributed MED file, previously created by partitionneDomaine() or partitionneGrain()
     *           (=> each part only contain 1 mesh, TETRA10 elements only)
     * \param  pPartName    name of the part to be decimated.
     * \param  pFieldName   name of the field used for decimation.
     * \param  pFieldIt     iteration (time step) of the field.
     * \param  pFilterName  name of the filter to be used.
     * \param  pTmed        threshold used for medium resolution.
     * \param  pTlow        threshold used for low resolution; tmed must be less than tlow
     * \param  pTadius      radius used to determine the neighbourhood.
     * \return the name of each part.
     */
    MULTIPR_ORB::string_array* decimePartition(
        const char*   pPartName,
        const char*   pFieldName,
        CORBA::Long   pFieldIt,
        const char*   pFilterName,
        const char*   pFilterParams)
        throw (SALOME::SALOME_Exception);

    /**
     * Works exactly like the above method, but returns not a list
     * of all parts, but a list of empty obtained resolutions.
     * \return the list of empty resolutions.
     */
  /*
    MULTIPR_ORB::string_array* decimatePart(
        const char*   pPartName,
        const char*   pFieldName,
        CORBA::Long   pFieldIt,
        const char*   pFilterName,
        const char*   pFilterParams)
        throw (SALOME::SALOME_Exception);
  */

    /**
     * Returns useful information to configure decimation parameters.
     * Depends on part, field and filter: generic operation.
     * \param  pPartName     name of the part.
     * \param  pFieldName    name of the field used for decimation.
     * \param  pFieldIt      iteration (time step) of the field.
     * \param  pFilterName   name of the filter to be used.
     * \param  pFilterParams params to be used with the filter (depends on filter; this string will be parsed).
     */
    char* evalDecimationParams(
        const char* pPartName,
        const char* pFieldName,
        CORBA::Long pFieldIt,
        const char* pFilterName,
        const char* pFilterParams)
        throw (SALOME::SALOME_Exception);

    /*!
     * Removes all the parts starting with "pPrefixPartName" from the distributed MED file.
     * Example: if pPrefixPartName="PART_4" => remove "PART_4" and all sub-parts "PART_4_*", but not "PART41".
     * Assume this object encapsulates a distributed MED file.
     * \param  pPrefixPartName name of the part.
     */
    void removeParts(const char* pPrefixPartName)
        throw (SALOME::SALOME_Exception);

    /**
     * Get mesh statistics.
     * \return Mesh statistics !
     */
    char* getMEDInfo(const char* pPartName)
        throw (SALOME::SALOME_Exception);

    //---------------------------------------------------------------------
    // I/O
    //---------------------------------------------------------------------

    /**
     * Saves the associated MED file if necessary.
     * \param  pPath path where to save the file.
     */
    void save(const char* pPath)
        throw (SALOME::SALOME_Exception);

    /**
     * Check save progress.
     * \return current save progress in percents.
     */
    CORBA::Long getSaveProgress();

    /**
     * Reset save progress to zero.
     */
    void resetSaveProgress();

    //---------------------------------------------------------------------
    // Persistence and Dump Python
    //---------------------------------------------------------------------

    /**
     * Saves the associated MED file to the given location.
     * \note To be used only for persistence.
     * \param pPath path where to save the file.
     */
    void savePersistent (const char* pPath)
      throw (SALOME::SALOME_Exception);

    /**
     * Set Engine.
     */
    //void setEngine (MULTIPR_ORB::MULTIPR_Gen_ptr theEngine);
    void setEngine (MULTIPR_Gen_i* theEngine);

private:

    /**
     * The associated MULTIPR object.
     */
    multipr::Obj* mObj;

    /**
     * Boxing paremeter: number of cells along each axis.
     * E.g. if mBoxing=10 then total number of cells = 10*10*10 = 1000.
     * By default, mBoxing=100.
     */
    int mBoxing;

    /**
     * Engine.
     */
    MULTIPR_Gen_i* _engine;

    /**
     * Is restored MED file (Used by Persistence to remove temporary files).
     */
    bool mIsTmp;
};


//*****************************************************************************
// Class MULTIPR_Gen_i
// C++ implementation of the MULTIPR_Gen CORBA interface
//*****************************************************************************

class MULTIPR_Gen_i :
    public POA_MULTIPR_ORB::MULTIPR_Gen,
    public Engines_Component_i
{

public:

    MULTIPR_Gen_i(
        CORBA::ORB_ptr orb,
        PortableServer::POA_ptr poa,
        PortableServer::ObjectId* contId,
        const char* instanceName,
        const char* interfaceName);

    virtual ~MULTIPR_Gen_i();

    char* getVersion()
        throw (SALOME::SALOME_Exception);

    void partitionneDomaine(
        const char* medFilename,
        const char* meshName)
        throw (SALOME::SALOME_Exception);

    void partitionneGroupe(
        const char* medFilename,
        const char* partName,
        CORBA::Long nbParts,
        CORBA::Long partitionner)
        throw (SALOME::SALOME_Exception);

    void decimePartition(
        const char*   medFilename,
        const char*   partName,
        const char*   fieldName,
        CORBA::Long   fieldIt,
        const char*   filterName,
        const char*   filterParams)
        throw (SALOME::SALOME_Exception);

    MULTIPR_ORB::MULTIPR_Obj_ptr getObject(const char* medFilename)
        throw (SALOME::SALOME_Exception);

  // ****************************************************
  // Set/Get current study for Persistence and Dump Python
  // ****************************************************

  // Set current study
  void SetCurrentStudy (SALOMEDS::Study_ptr theStudy);
  // Get current study
  SALOMEDS::Study_ptr GetCurrentStudy();

  // Mark current study as modified, if theObj is published in it
  void ObjModified (MULTIPR_ORB::MULTIPR_Obj_ptr theObj);

  // ****************************************************
  // Interface inherited methods (from SALOMEDS::Driver)
  // ****************************************************

  // Save SMESH data
  SALOMEDS::TMPFile* Save (SALOMEDS::SComponent_ptr theComponent,
                           const char*              theURL,
                           bool                     isMultiFile);
  // Load SMESH data
  bool Load (SALOMEDS::SComponent_ptr theComponent,
	     const SALOMEDS::TMPFile& theStream,
	     const char*              theURL,
	     bool                     isMultiFile);

  // Save SMESH data in ASCII format
  SALOMEDS::TMPFile* SaveASCII (SALOMEDS::SComponent_ptr theComponent,
			        const char*              theURL,
			        bool                     isMultiFile);
  // Load SMESH data in ASCII format
  bool LoadASCII (SALOMEDS::SComponent_ptr theComponent,
		  const SALOMEDS::TMPFile& theStream,
		  const char*              theURL,
		  bool                     isMultiFile);

  // Clears study-connected data when it is closed
  void Close (SALOMEDS::SComponent_ptr theComponent);

  // Get component data type
  char* ComponentDataType();

  // Transform data from transient form to persistent
  char* IORToLocalPersistentID (SALOMEDS::SObject_ptr theSObject,
			        const char*           IORString,
			        CORBA::Boolean        isMultiFile,
			        CORBA::Boolean        isASCII);
  // Transform data from persistent form to transient
  char* LocalPersistentIDToIOR (SALOMEDS::SObject_ptr theSObject,
			        const char*           aLocalPersistentID,
			        CORBA::Boolean        isMultiFile,
			        CORBA::Boolean        isASCII);

  // Returns true if object can be published in the study
  bool CanPublishInStudy (CORBA::Object_ptr theIOR);
  // Publish object in the study
  SALOMEDS::SObject_ptr PublishInStudy (SALOMEDS::Study_ptr   theStudy,
		                        SALOMEDS::SObject_ptr theSObject,
		                        CORBA::Object_ptr     theObject,
		                        const char*           theName)
    throw (SALOME::SALOME_Exception);

  // Copy-paste methods - returns true if object can be copied to the clipboard
  CORBA::Boolean CanCopy (SALOMEDS::SObject_ptr theObject) { return false; }
  // Copy-paste methods - copy object to the clipboard
  SALOMEDS::TMPFile* CopyFrom (SALOMEDS::SObject_ptr theObject, CORBA::Long& theObjectID) { return false; }
  // Copy-paste methods - returns true if object can be pasted from the clipboard
  CORBA::Boolean CanPaste (const char* theComponentName, CORBA::Long theObjectID) { return false; }
  // Copy-paste methods - paste object from the clipboard
  SALOMEDS::SObject_ptr PasteInto (const SALOMEDS::TMPFile& theStream,
				   CORBA::Long              theObjectID,
				   SALOMEDS::SObject_ptr    theObject)
  {
    SALOMEDS::SObject_var aResultSO;
    return aResultSO._retn();
  }

  // ============
  // Dump python
  // ============

  virtual Engines::TMPFile* DumpPython (CORBA::Object_ptr theStudy,
                                        CORBA::Boolean isPublished,
                                        CORBA::Boolean& isValidScript);

  void AddToPythonScript (int theStudyID, std::string theString);

private:
  std::string DumpPython_impl (int theStudyID,
                               bool isPublished,
                               bool& aValidScript,
                               std::string theSavedTrace);

  std::string GetNewPythonLines (int theStudyID);
  void CleanPythonTrace (int theStudyID);
  void SavePython (SALOMEDS::Study_ptr theStudy);

private:
  // Current study
  SALOMEDS::Study_var myCurrentStudy;
  // Dump Python: trace of API methods calls
  std::map < int, std::vector <std::string> > myPythonScripts;
  // Tmp directory. Used by Persistence.
  std::string myTmpDir;
};


extern "C" PortableServer::ObjectId* MULTIPREngine_factory(
    CORBA::ORB_ptr orb,
    PortableServer::POA_ptr poa,
    PortableServer::ObjectId * contId,
    const char* instanceName,
    const char* interfaceName);

#endif // __MULTIPR_IMPLEMENTATION_CORBA__

// EOF
