// Copyright (c) 1999-2014 OPEN CASCADE SAS
//
// This file is part of Open CASCADE Technology software library.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License version 2.1 as published
// by the Free Software Foundation, with special exception defined in the file
// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
// distribution for complete text of the license and disclaimer of any warranty.
//
// Alternatively, this file may be used under the terms of Open CASCADE
// commercial license or contractual agreement.


#include <IFSelect_AppliedModifiers.hxx>
#include <IFSelect_ContextModif.hxx>
#include <IFSelect_ContextWrite.hxx>
#include <IFSelect_GeneralModifier.hxx>
#include <IFSelect_ModelCopier.hxx>
#include <IFSelect_Modifier.hxx>
#include <IFSelect_Selection.hxx>
#include <IFSelect_ShareOut.hxx>
#include <IFSelect_ShareOutResult.hxx>
#include <IFSelect_WorkLibrary.hxx>
#include <Interface_Check.hxx>
#include <Interface_CheckIterator.hxx>
#include <Interface_CopyTool.hxx>
#include <Interface_EntityIterator.hxx>
#include <Interface_GeneralLib.hxx>
#include <Interface_Graph.hxx>
#include <Interface_InterfaceModel.hxx>
#include <Interface_Protocol.hxx>
#include <Message.hxx>
#include <Message_Messenger.hxx>
#include <Standard_Type.hxx>
#include <TCollection_AsciiString.hxx>
#include <TCollection_HAsciiString.hxx>

#include <stdio.h>
IMPLEMENT_STANDARD_RTTIEXT(IFSelect_ModelCopier,Standard_Transient)

//#define MISOPOINT
IFSelect_ModelCopier::IFSelect_ModelCopier ()    {  }

    void  IFSelect_ModelCopier::SetShareOut
  (const Handle(IFSelect_ShareOut)& sho)
      {  theshareout = sho;  }


//  ########################################################################
//  ########    OPERATIONS DE TRANSFERT GLOBAL (memorise ou non)    ########


    void  IFSelect_ModelCopier::ClearResult ()
      {  thefilemodels.Clear();  thefilenames.Clear();  theapplieds.Clear();
         theremain.Nullify();  }


    Standard_Boolean  IFSelect_ModelCopier::AddFile
  (const TCollection_AsciiString& filename,
   const Handle(Interface_InterfaceModel)& content)
{
  Standard_Integer nb = thefilenames.Length();
  for (Standard_Integer i = 1; i <= nb; i ++) {
    if (filename.IsEmpty()) continue;
    if (thefilenames(i).IsEqual(filename)) return Standard_False;
  }
  Handle(IFSelect_AppliedModifiers) nulapplied;
  thefilenames.Append  (filename);
  thefilemodels.Append (content);
  theapplieds.Append   (nulapplied);
  return Standard_True;
}

    Standard_Boolean  IFSelect_ModelCopier::NameFile
  (const Standard_Integer num,
   const TCollection_AsciiString& filename)
{
  Standard_Integer nb = thefilenames.Length();
  if (num <= 0 || num > nb) return Standard_False;
  for (Standard_Integer i = 1; i <= nb; i ++) {
    if (filename.IsEmpty()) continue;
    if (thefilenames(i).IsEqual(filename)) return Standard_False;
  }
  thefilenames.SetValue(num,filename);
  return Standard_True;
}

    Standard_Boolean  IFSelect_ModelCopier::ClearFile
  (const Standard_Integer num)
{
  Standard_Integer nb = thefilenames.Length();
  if (num <= 0 || num > nb) return Standard_False;
  thefilenames.ChangeValue(num).Clear();
  return Standard_True;
}

    Standard_Boolean  IFSelect_ModelCopier::SetAppliedModifiers
  (const Standard_Integer num, const Handle(IFSelect_AppliedModifiers)& applied)
{
  Standard_Integer nb = theapplieds.Length();
  if (num <= 0 || num > nb) return Standard_False;
  theapplieds.SetValue(num,applied);
  return Standard_True;
}

    Standard_Boolean  IFSelect_ModelCopier::ClearAppliedModifiers
  (const Standard_Integer num)
{
  Standard_Integer nb = theapplieds.Length();
  if (num <= 0 || num > nb) return Standard_False;
  theapplieds.ChangeValue(num).Nullify();
  return Standard_True;
}

//  ....    Copy : Opere les Transferts, les Memorise (pas d envoi fichier ici)

      Interface_CheckIterator IFSelect_ModelCopier::Copy
  (IFSelect_ShareOutResult& eval,
   const Handle(IFSelect_WorkLibrary)& WL,
   const Handle(Interface_Protocol)& protocol) 
{
  Interface_CopyTool TC (eval.Graph().Model(), protocol);
  return Copying (eval,WL,protocol,TC);
}

//  Copy Interne

    Interface_CheckIterator  IFSelect_ModelCopier::Copying
  (IFSelect_ShareOutResult& eval,
   const Handle(IFSelect_WorkLibrary)& WL,
   const Handle(Interface_Protocol)& protocol,
   Interface_CopyTool& TC)
{
  Message::SendInfo() << "** WorkSession : Copying split data before sending"<<std::endl;
  const Interface_Graph& G = eval.Graph();
  Interface_CheckIterator checks;
  theshareout = eval.ShareOut();
  theremain = new TColStd_HArray1OfInteger(0,G.Size()); theremain->Init(0);
  for (eval.Evaluate(); eval.More(); eval.Next()) {
    Handle(Interface_InterfaceModel) model;
    TCollection_AsciiString filename = eval.FileName();
    Standard_Integer dispnum = eval.DispatchRank();
    Standard_Integer numod, nbmod;
    eval.PacketsInDispatch (numod,nbmod);
    Handle(IFSelect_AppliedModifiers) curapp;
    CopiedModel (G, WL,protocol, eval.PacketRoot(), filename,dispnum,numod, TC,
		 model, curapp,checks);

    AddFile (filename, model);
    theapplieds.SetValue (theapplieds.Length(), curapp);
  }
  theshareout->SetLastRun (theshareout->NbDispatches());
  checks.SetName ("X-STEP WorkSession : Split Copy (no Write)");
  return checks;
}

//  Send a deux arguments : Envoi Fichier du Resultat deja memorise

    Interface_CheckIterator  IFSelect_ModelCopier::SendCopied
  (const Handle(IFSelect_WorkLibrary)& WL,
   const Handle(Interface_Protocol)& protocol)
{
  Message::SendInfo() << "** WorkSession : Sending split data already copied"<<std::endl;
  Standard_Integer nb = NbFiles();
  Interface_CheckIterator checks;
  if (nb > 0) {
    for (Standard_Integer i = 1; i <= nb; i ++) {
      if (FileName(i).Length() == 0) continue;
      Handle(IFSelect_AppliedModifiers) curapp = theapplieds.Value(i);
      IFSelect_ContextWrite ctx (FileModel(i),protocol,curapp,FileName(i).ToCString());
      Standard_Boolean res = WL->WriteFile (ctx);
      Interface_CheckIterator checklst = ctx.CheckList();
      checks.Merge(checklst);
//	(FileName(i).ToCString(), FileModel(i),protocol,curapp,checks);
//      if (!checks.IsEmpty(Standard_False)) {
//	sout<<"  **  On Sending File n0."<<i<<", Check Messages :  **"<<std::endl;
//	checks.Print (sout,Standard_False);
//      }
      if (!res) {
	char mess[100];  sprintf(mess,"Split Send (WriteFile) abandon on file n0.%d",i);
	checks.CCheck(0)->AddFail (mess);
	Message::SendInfo() << "  **  Sending File n0."<<i<<" has failed, abandon  **"<<std::endl;
	return checks;
      }
      AddSentFile (FileName(i).ToCString());
    }
    ClearResult();
  }
  checks.SetName ("X-STEP WorkSession : Split Send (Copy+Write)");
  return checks;
}


//  .... Send a 4 arguments : Calcul du Transfert et Envoi sur Fichier

    Interface_CheckIterator  IFSelect_ModelCopier::Send
  (IFSelect_ShareOutResult& eval,
   const Handle(IFSelect_WorkLibrary)& WL,
   const Handle(Interface_Protocol)& protocol)
{
  Interface_CopyTool TC (eval.Graph().Model(), protocol);
  return Sending (eval,WL,protocol,TC);
}

    Interface_CheckIterator  IFSelect_ModelCopier::Sending
  (IFSelect_ShareOutResult& eval,
   const Handle(IFSelect_WorkLibrary)& WL,
   const Handle(Interface_Protocol)& protocol,
   Interface_CopyTool& TC)
{
  const Interface_Graph& G = eval.Graph();
  Interface_CheckIterator checks;
  Standard_Integer i = 0;
  Message::SendInfo() << "** WorkSession : Copying then sending split data"<<std::endl;
  theshareout = eval.ShareOut();
  theremain = new TColStd_HArray1OfInteger(0,G.Size()); theremain->Init(0);
  for (eval.Evaluate(); eval.More(); eval.Next()) {
    i ++;
    Handle(Interface_InterfaceModel) model;
    TCollection_AsciiString filename = eval.FileName();
    Standard_Integer dispnum = eval.DispatchRank();
    Standard_Integer numod, nbmod;
    eval.PacketsInDispatch (numod,nbmod);
    Handle(IFSelect_AppliedModifiers) curapp;
    CopiedModel (G, WL,protocol, eval.PacketRoot(), filename,dispnum,numod, TC,
		 model, curapp, checks);
    IFSelect_ContextWrite ctx (model,protocol,curapp,filename.ToCString());
    Standard_Boolean res = WL->WriteFile (ctx);
    Interface_CheckIterator checklst = ctx.CheckList();
    checks.Merge(checklst);
//      (filename.ToCString(), model, protocol, curapp, checks);
//    if (!checks.IsEmpty(Standard_False)) {
//      sout<<"  **  On Sending File "<<filename<<", Check Messages :  **"<<std::endl;
//      checks.Print (sout,model,Standard_False);
//    }
    if (!res) {
      char mess[100];  sprintf(mess,"Split Send (WriteFile) abandon on file n0.%d",i);
      checks.CCheck(0)->AddFail (mess);
      Message::SendInfo() << "  **  Sending File "<<filename<<" has failed, abandon  **"<<std::endl;
      checks.SetName ("X-STEP WorkSession : Split Send (only Write)");
      return checks;
    }
    AddSentFile (filename.ToCString());
  }
  theshareout->SetLastRun (theshareout->NbDispatches());
  checks.SetName ("X-STEP WorkSession : Split Send (only Write)");
  return checks;
}


//  .... SendAll : Donnees a tranferer dans G, aucun split, envoi sur fichier

    Interface_CheckIterator  IFSelect_ModelCopier::SendAll
  (const Standard_CString filename,   const Interface_Graph& G,
   const Handle(IFSelect_WorkLibrary)& WL,
   const Handle(Interface_Protocol)& protocol)
{
  Interface_CheckIterator checks;
  checks.SetName ("X-STEP WorkSession : Send All");
  Message::SendInfo() << "** WorkSession : Sending all data"<<std::endl;
  Handle(Interface_InterfaceModel)  model = G.Model();
  if (model.IsNull() || protocol.IsNull() || WL.IsNull()) return checks;

  Interface_CopyTool TC (model, protocol);
  Standard_Integer i, nb = model->NbEntities();
  for (i = 1; i <= nb; i ++)  TC.Bind (model->Value(i),model->Value(i));

  Interface_EntityIterator pipo;
  Handle(Interface_InterfaceModel)  newmod;
  Handle(IFSelect_AppliedModifiers) applied;
  CopiedModel (G, WL,protocol,pipo,TCollection_AsciiString(filename),
	       0,0,TC,newmod, applied,checks);

  IFSelect_ContextWrite ctx (model,protocol,applied,filename);
  Standard_Boolean res = WL->WriteFile (ctx);
  Interface_CheckIterator checklst = ctx.CheckList();
  checks.Merge(checklst);
  if (!res) checks.CCheck(0)->AddFail ("SendAll (WriteFile) has failed");
//  if (!checks.IsEmpty(Standard_False)) {
//    Message::SendWarning() <<
//      "  **    SendAll has produced Check Messages :    **"<<std::endl;
//    checks.Print (sout,model,Standard_False);
//  }
  return checks;
}


//  .... SendSelected : Donnees a tranferer dans G, filtrees par iter,
//       aucun split, envoi sur fichier

    Interface_CheckIterator  IFSelect_ModelCopier::SendSelected
  (const Standard_CString filename,   const Interface_Graph& G,
   const Handle(IFSelect_WorkLibrary)& WL,
   const Handle(Interface_Protocol)& protocol,
   const Interface_EntityIterator& list)
{
  Interface_CheckIterator checks;
  checks.SetName ("X-STEP WorkSession : Send Selected");
  Message::SendInfo() << "** WorkSession : Sending selected data"<<std::endl;
  Handle(Interface_InterfaceModel)  original = G.Model();
  if (original.IsNull() || protocol.IsNull() || WL.IsNull()) return checks;
  Handle(Interface_InterfaceModel) newmod  = original->NewEmptyModel();
  Interface_CopyTool TC (original, protocol);
  TC.FillModel(newmod);    // pour Header ...

//  Pas de copie : AddWithRefs plus declaration de Bind
  Interface_GeneralLib lib(protocol);
  for (list.Start(); list.More(); list.Next()) {
    newmod->AddWithRefs (list.Value(),lib);
  }
  Standard_Integer i, nb = newmod->NbEntities();
  for (i = 1; i <= nb; i ++)  TC.Bind (newmod->Value(i),newmod->Value(i));
  if (theremain.IsNull())
    { theremain = new TColStd_HArray1OfInteger(0,G.Size()); theremain->Init(0); }

  Interface_EntityIterator pipo;
  Handle(IFSelect_AppliedModifiers) applied;
  CopiedModel (G, WL,protocol, pipo,TCollection_AsciiString(filename),
	       0,0,TC,newmod, applied,checks);
//  Alimenter Remaining : les entites copiees sont a noter
  Handle(Standard_Transient) ent1,ent2;
  for (Standard_Integer ic = TC.LastCopiedAfter (0,ent1,ent2); ic > 0;
       ic = TC.LastCopiedAfter (ic,ent1,ent2) ) {
    if (ic <= theremain->Upper())
      theremain->SetValue(ic,theremain->Value(ic)+1);
  }
  IFSelect_ContextWrite ctx (newmod,protocol,applied,filename);
  Standard_Boolean res = WL->WriteFile (ctx);
  Interface_CheckIterator checklst = ctx.CheckList();
  checks.Merge(checklst);
  if (!res) checks.CCheck(0)->AddFail ("SendSelected (WriteFile) has failed");
//  if (!checks.IsEmpty(Standard_False)) {
//    Message::SendWarning() <<
//      "  **    SendSelected has produced Check Messages :    **"<<std::endl;
//    checks.Print (sout,original,Standard_False);
//  }
  return checks;
}


//  ##########################################################################
//  ########        UN TRANSFERT UNITAIRE (avec Modifications)        ########

    void  IFSelect_ModelCopier::CopiedModel
  (const Interface_Graph& G,
   const Handle(IFSelect_WorkLibrary)& WL,
   const Handle(Interface_Protocol)& protocol,
   const Interface_EntityIterator& tocopy,
   const TCollection_AsciiString& filename,
   const Standard_Integer dispnum, const Standard_Integer /* numod */,
   Interface_CopyTool& TC,
   Handle(Interface_InterfaceModel)& newmod,
   Handle(IFSelect_AppliedModifiers)& applied,
   Interface_CheckIterator& checks) const
{
//  ...  Premiere partie "standard" : remplissage du modele  ...
//  On cree le Modele, on le remplit avec les Entites, et avec le Header depart

//  ATTENTION : dispnum = 0  signifie prendre modele original, ne rien copier
//                             et aussi : pas de Dispatch (envoi en bloc)

  applied.Nullify();
  Handle(Interface_InterfaceModel) original = G.Model();
  if (dispnum > 0) {
    newmod  = original->NewEmptyModel();
    TC.Clear();
    WL->CopyModel (original,newmod,tocopy,TC);

    Handle(Standard_Transient) ent1,ent2;
//  Alimenter Remaining : les entites copiees sont a noter
    for (Standard_Integer ic = TC.LastCopiedAfter (0,ent1,ent2); ic > 0;
	 ic = TC.LastCopiedAfter (ic,ent1,ent2) ) {
      if (ic <= theremain->Upper())
	theremain->SetValue(ic,theremain->Value(ic)+1);
    }
  }
  else if (newmod.IsNull()) newmod = original;

//  ...  Ensuite : On prend en compte les Model Modifiers  ...
  Standard_Integer nbmod = 0;
  if (!theshareout.IsNull()) nbmod = theshareout->NbModifiers(Standard_True);
  Standard_Integer i; // svv Jan11 2000 : porting on DEC
  for (i = 1; i <= nbmod; i ++) {
    Handle(IFSelect_Modifier) unmod = theshareout->ModelModifier(i);

//    D abord,  critere Dispatch/Packet
    if (dispnum > 0)
      if (!unmod->Applies (theshareout->Dispatch(dispnum))) continue;
    IFSelect_ContextModif ctx (G,TC,filename.ToCString());
//    Ensuite, la Selection
    Handle(IFSelect_Selection) sel = unmod->Selection();
    if (!sel.IsNull()) {
      Interface_EntityIterator entiter = sel->UniqueResult(G);
      ctx.Select (entiter);
    }
    if (ctx.IsForNone()) continue;
    unmod->Perform (ctx,newmod,protocol,TC);
    Interface_CheckIterator checklst = ctx.CheckList();
    checks.Merge (checklst);

//    Faut-il enregistrer les erreurs dans newmod ? bonne question
//    if (!checks.IsEmpty(Standard_False)) {
//      Message::SendWarning() <<
//        " Messages on Copied Model n0 "<<numod<<", Dispatch Rank "<<dispnum<<std::endl;
//      checks.Print(sout,newmod,Standard_False);
//    }
  }

//  ...  Puis les File Modifiers : en fait, on les enregistre  ...
  nbmod = 0;
  if (!theshareout.IsNull()) nbmod = theshareout->NbModifiers(Standard_False);
  if (nbmod == 0) return;
  applied = new IFSelect_AppliedModifiers (nbmod,newmod->NbEntities());
  for (i = 1; i <= nbmod; i ++) {
    Handle(IFSelect_GeneralModifier) unmod = theshareout->GeneralModifier(Standard_False,i);

//    D abord,  critere Dispatch/Packet
    if (dispnum > 0)
      if (!unmod->Applies (theshareout->Dispatch(dispnum))) continue;
//    Ensuite, la Selection
    Handle(IFSelect_Selection) sel = unmod->Selection();
    if (sel.IsNull()) applied->AddModif (unmod);    // vide -> on prend tout
    else {
      Interface_EntityIterator list = sel->UniqueResult(G);
      Handle(Standard_Transient) newent;

//    Entites designees par la Selection et Copiees ?
//    -> s ilyena au moins une, le Modifier s applique, sinon il est rejete
//    -> et cette liste est exploitable par le Modifier ...
      for (list.Start(); list.More(); list.Next()) {
	if (TC.Search (list.Value(),newent))
	  applied->AddNum (newmod->Number(newent));
      }
    }
  }
}


    void  IFSelect_ModelCopier::CopiedRemaining
  (const Interface_Graph& G, const Handle(IFSelect_WorkLibrary)& WL,
   Interface_CopyTool& TC,   Handle(Interface_InterfaceModel)& newmod)
{
  Handle(Interface_InterfaceModel) original = G.Model();
//  Interface_CopyTool TC(original,protocol);
  newmod  = original->NewEmptyModel();
  TC.Clear();
  Interface_EntityIterator tocopy;
  Standard_Integer nb = G.Size();
  theremain = new TColStd_HArray1OfInteger(0,nb+1); theremain->Init(0);
  for (Standard_Integer i = 1; i <= nb; i ++) {
    if (G.Status(i) == 0) tocopy.AddItem (original->Value(i));
    else theremain->SetValue(i,-1);  //  ?? -1
  }
  WL->CopyModel (original,newmod,tocopy,TC);

  if (newmod->NbEntities() == 0) newmod.Nullify();
  else {
//  CE QUI SUIT NE DOIT PAS ETRE SUPPRIME ! cf theremain
    Handle(Standard_Transient) ent1,ent2;
    for (Standard_Integer ic = TC.LastCopiedAfter (0,ent1,ent2); ic > 0;
	 ic = TC.LastCopiedAfter (ic,ent1,ent2) ) {
      if (ic <= theremain->Upper())
	theremain->SetValue(ic,1);
    }
//  qq impressions de mise au point
#ifdef MISOPOINT
    std::cout << " Remaining Model : " << newmod->NbEntities() << " Entities"<<std::endl;
    Standard_Integer ne = 0;
    for (i = 1; i <= nb; i ++) {
      if (theremain->Value(i) == 0) {
	if (ne == 0)     std::cout << " Refractaires : ";
	ne ++;  std::cout << " " << i;
      }
    }
    if (ne > 0) std::cout << "  -- " << ne << " Entities" << std::endl;
    else std::cout<<"  -- Remaining data complete"<<std::endl;
#endif
  }
}

    Standard_Boolean  IFSelect_ModelCopier::SetRemaining
  (Interface_Graph& CG) const
{
  Standard_Integer nb = CG.Size();
  if (theremain.IsNull()) return (nb == 0);
  if (nb != theremain->Upper()) return Standard_False;
  for (Standard_Integer i = 1; i <= nb; i ++) {
    if (CG.Status(i) >= 0) CG.SetStatus(i,CG.Status(i)+theremain->Value(i));
  }
  theremain->Init(0);
  return Standard_True;
}

//  ##########################################################################
//  ########        RESULTAT de la Memorisation des Transferts        ########

    Standard_Integer  IFSelect_ModelCopier::NbFiles () const 
      {  return thefilemodels.Length();  }

    TCollection_AsciiString  IFSelect_ModelCopier::FileName
  (const Standard_Integer num) const 
      {  return thefilenames.Value(num);  }

    Handle(Interface_InterfaceModel)  IFSelect_ModelCopier::FileModel
  (const Standard_Integer num) const
      {  return thefilemodels.Value(num);  }

    Handle(IFSelect_AppliedModifiers)  IFSelect_ModelCopier::AppliedModifiers
  (const Standard_Integer num) const
      {  return theapplieds.Value(num);  }


    void  IFSelect_ModelCopier::BeginSentFiles
  (const Handle(IFSelect_ShareOut)& sho, const Standard_Boolean record)
{
  thesentfiles.Nullify();
  if (record) thesentfiles = new TColStd_HSequenceOfHAsciiString();
//  et numerotation des fichiers par defaut : detenue par ShareOut
  if (sho.IsNull()) return;
  Standard_Integer lastrun = sho->LastRun();
  sho->ClearResult (Standard_True);
  sho->SetLastRun (lastrun);        // on ne s interesse quaux numeros
}

    void  IFSelect_ModelCopier::AddSentFile (const Standard_CString filename)
      {  if (!thesentfiles.IsNull())
	   thesentfiles->Append(new TCollection_HAsciiString(filename));  }

    Handle(TColStd_HSequenceOfHAsciiString)  IFSelect_ModelCopier::SentFiles () const
      {  return thesentfiles;  }
