// GenFem - A high-level finite element library
// Copyright (C) 2010-2026 Eric Bechet
//
// See the LICENSE file for license information and contributions.
// Please report all bugs and problems to <bechet@cadxfem.org>.
//
// Initial design: Frederic Duboeuf (rev.654)


#ifndef _SAVED_GENTERM_MANAGER_H_
#define _SAVED_GENTERM_MANAGER_H_

#include <sstream>
#include <vector>
#include <deque>
#include <map>
#include "assert.h"
#include "savedGenTerm.h"


template<class GT> class BySupport
{
public :
  const int step;
private : // Convention : indices of the supports should be ordered and contiguous [0,nbSupport-1]
  std::vector<std::shared_ptr<GT> > vGTbySupport;

public :
  BySupport(const int &step_) : step(step_) {}
  BySupport (const BySupport &other) : step(other.step), vGTbySupport(other.vGTbySupport) {}
  ~BySupport() {}
  int size() const {return vGTbySupport.size();}
  void add(const std::shared_ptr<GT> &term) { vGTbySupport.push_back(term); }
  void update (const int i, const std::shared_ptr<GT> &term) { vGTbySupport[i]=term; }
  std::shared_ptr<GT> get(const int i) { return vGTbySupport[i]; }
  void print() const
  {
    int size = vGTbySupport.size();
    printf( "    Step \'%d\', nbSupport = %d \n",step,size);
    for (int i=0; i<size; ++i)
    {
      printf( "      Support \'%4d\' \n",i);
//       vGTbySupport[i].print("");
    }
  }
};


class ByStepBase
{
public :
  std::string name;

  ByStepBase() : name(){}
  ByStepBase(std::string name_) : name(name_) {}
  ByStepBase(const ByStepBase &other) : name(other.name) {}
  virtual int getcurrentstep() const = 0;
  virtual int size() const = 0;
  std::string getName(){return name;}
  virtual void print() const = 0;
};

template<class GT> class ByStep : public ByStepBase
{
public :
  int currentstep;
private :
  std::deque< BySupport<GT>* > dequeDataByStep;

public :
  ByStep(std::string name_, const int &requestedstep_=0) :  currentstep(requestedstep_) { ByStepBase::name = name_; }
  ByStep(std::string name_, const int &currentstep_, const int &requestedstep_, GT* &term) :
    currentstep(requestedstep_), dequeDataByStep(1, new BySupport<GT>(currentstep_-requestedstep_,term)) { ByStepBase::name = name_; }
  ByStep (const ByStep &other) : dequeDataByStep(other.dequeDataByStep) { ByStepBase::name = other.getName(); }
  ~ByStep()
  {
    while ( !dequeDataByStep.empty() )
    {
      delete dequeDataByStep.back();
      dequeDataByStep.pop_back();
    }
  }
  virtual int getcurrentstep() const {return currentstep;}
  virtual int size() const {return dequeDataByStep.size();}
  std::string getName(){return name;}
  BySupport<GT>* get(const int step) // in reference to the current time step, i<size, must exist
  {
    return dequeDataByStep[size()-step-1];
  }
  BySupport<GT>* addStep(const int &currentstep_) { currentstep=currentstep_; dequeDataByStep.push_back(new BySupport<GT>(currentstep_)); return dequeDataByStep.back(); }
  void delOldStep() { delete dequeDataByStep.front(); dequeDataByStep.pop_front();}
  void print() const
  {
    int size = dequeDataByStep.size();
    printf( "  Field \'%s\', nbStep = %d \n",name.c_str(),size);
    for (int i=0; i<size; ++i)
      dequeDataByStep[i]->print();
  }
};


class SavedGenTermManager
{
private :
  std::map< std::string, ByStepBase*> mapSavedFields;
  int nbMaxSteps;

public :
  SavedGenTermManager() : mapSavedFields(), nbMaxSteps(100) {}
  ~SavedGenTermManager()
  {
    std::map< std::string, ByStepBase*>::iterator it;
    for ( it = mapSavedFields.begin(); it != mapSavedFields.end(); ++it )
      delete it->second;
    
    mapSavedFields.clear();
  }

  inline int size() {return mapSavedFields.size();}
  inline void defineMaxSteps(int nbMaxSteps_) {nbMaxSteps=nbMaxSteps_;}
  ByStepBase* get( std::string name )
  {
    std::string strname = name;
    std::map< std::string, ByStepBase*>::iterator it;
    it = mapSavedFields.find(name);
    if (it!=mapSavedFields.end())
      return it->second;
//  else
    return NULL;
  }
  template<class ByStepb> void add(std::string name, ByStepb* &byStepB)
  {
    ByStepb* newByStep= new ByStepb(name);
    mapSavedFields[name] = newByStep;
    byStepB = newByStep;
  }

  template <class GT> void addSavedGenTerm(std::string name, std::shared_ptr<GT> &term,
                                           const int currentstep=0, const int requestedstep=0,
                                           const int support=0)
  {
    ByStepBase* existingField = get(name);

    if (existingField == NULL)
    { // inexisting Field -> CREATE
      assert( requestedstep == 0 && support == 0 );
      ByStep<GT>* savedGenTermbyStep;
      add(name,savedGenTermbyStep);
      BySupport<GT>* savedGenTermbySupport = savedGenTermbyStep->addStep(currentstep);
      savedGenTermbySupport->add(term);
      return;
    }
    //else
    { // existing Field -> UPDATE
      ByStep<GT>* savedGenTermbyStep = dynamic_cast< ByStep<GT>* >(existingField);
      assert (savedGenTermbyStep != NULL); // type checking
      int &savedcurstep = savedGenTermbyStep->currentstep;
      assert(currentstep >= savedcurstep);
      if (currentstep > savedcurstep)
      { // inexisting Step -> CREATE
        BySupport<GT>* savedGenTermbySupport = savedGenTermbyStep->addStep(currentstep);
        savedGenTermbySupport->add(term);
      
        if (savedGenTermbyStep->size()>nbMaxSteps) savedGenTermbyStep->delOldStep();
        
        return;
      }
      //elseif (currentstep == savedcurstep)
      { // existing Step -> UPDATE
        assert( requestedstep < savedGenTermbyStep->size() );

        BySupport<GT>* savedGenTermbySupport = savedGenTermbyStep->get(requestedstep);
        if(support<savedGenTermbySupport->size())
        { // existing Step -> UPDATE
          savedGenTermbySupport->update(support,term);
          return;
        }
        //else
        { // inexisting Step -> CREATE
          savedGenTermbySupport->add(term);
          return;
        }
      }
    }
  }
  template <class GT> void getSavedGenTerm(std::string name, std::shared_ptr<GT> &term,
                                           const int lastsavedstep=0, const int requestedstep=0,
                                           const int support=0)
  {
    ByStep<GT>* savedGenTermbyStep = dynamic_cast< ByStep<GT>* >(get(name));
    assert(savedGenTermbyStep != NULL); // field availability + type checking

    int &savedcurstep = savedGenTermbyStep->currentstep;
    assert( lastsavedstep == savedcurstep );
    assert( requestedstep < savedGenTermbyStep->size() );

    BySupport<GT>* savedGenTermbySupport = savedGenTermbyStep->get(requestedstep);
    assert( support < savedGenTermbySupport->size() );
    term = savedGenTermbySupport->get(support);
    return;
  }

  int getcurrentstep(std::string name)
  {
    ByStepBase* existingField = get(name);
    int currentstep = 0;
    if (existingField != NULL)
      currentstep = existingField->getcurrentstep();

    return currentstep;
  }
  
  void print()
  {
    int size = mapSavedFields.size();
    printf( "SavedGenTermManager :  nbField = %d \n", size);
    std::map< std::string, ByStepBase*>::iterator it;
    for ( it = mapSavedFields.begin(); it != mapSavedFields.end(); ++it )
      it->second->print();
  }
};


#endif// _SAVED_GENTERM_MANAGER_H_

