// 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 and Eric Bechet (rev.1346)


#ifndef _GENDOFIDMANAGER_H_
#define _GENDOFIDMANAGER_H_

#include "genSingleton.h"
#include <vector>
#include <cassert>
#include <iostream>

// number of bits to represent any number between 0 and value (included)
// here is a version able to cope with 64 bit values.
inline unsigned int nbits(unsigned long long value)
{
  const unsigned long long b[] = {0x2, 0xC, 0xF0, 0xFF00, 0xFFFF0000,0xFFFFFFFF00000000};
  const unsigned long long S[] = {1, 2, 4, 8, 16, 32};
  unsigned long long res = 0; // result of log2(v) will go here
  for (int i = 5; i >= 0; i--) // unroll for speed...
  {
    if (value & b[i])
    {
      value >>= S[i];
      res |= S[i];
    } 
  }
  return res+1;
}

class DofIdManager : public Singleton<DofIdManager>
{
protected :
  int Shift_Comp;
  int Shift_Index;
  int Shift_LocalIndex;

  int Nb_Fields;
  std::vector<int> Nb_Indexes;

private:
  friend class Singleton<DofIdManager>;
  DofIdManager() : Nb_Fields(0), Nb_Indexes(0)
  {
    int Max_Comp=15, Max_LocalIndex=255, Max_Index=15;
    Shift_Comp = nbits(Max_Comp);
    Shift_Index = nbits(Max_Index);
    Shift_LocalIndex = nbits(Max_Index);
  }
  ~DofIdManager() {}
public :
  inline void setBinaryShift(int Max_Comp=15, int Max_Index=15, int Max_LocalIndex=255)
  {
    int Shift_Comp_ = nbits(Max_Comp);
    int Shift_Index_ = nbits(Max_Index);
    int Shift_LocalIndex_ = nbits(Max_LocalIndex);

    assert(Shift_Comp_+Shift_LocalIndex_+Shift_Index_+1<32);
    assert(Max_Comp+1==1ul<<Shift_Comp_);
    assert(Max_Index+1==1ul<<Shift_Index_);
    assert(Max_LocalIndex+1==1ul<<Shift_LocalIndex_);

    Shift_Comp = Shift_Comp_;
    Shift_Index = Shift_Index_;
    Shift_LocalIndex = Shift_LocalIndex_;
  }
  inline void getBinaryShift(int &Shift_Comp_, int &Shift_Index_, int &Shift_LocalIndex_) const
  {
    Shift_Comp_ = Shift_Comp;
    Shift_Index_ = Shift_Index;
    Shift_LocalIndex_ = Shift_LocalIndex;
  }
  inline void getMaxValues(int &Max_Field, int &Max_Comp, int &Max_Index, int &Max_LocalIndex) const
  {
    Max_Field = (1ul<<(31-(Shift_Index+Shift_LocalIndex+Shift_Comp)))-1;
    Max_Comp =  (1ul<<Shift_Comp)-1;
    Max_Index = (1ul<<Shift_Index)-1;
    Max_LocalIndex = (1ul<<Shift_LocalIndex)-1;
  }

  inline int createTypeWithTwoInts(int iField, int iComp) // iIndex=0, iLocalIndex=0
  {
    assert(nbits(iField)<=31-(Shift_Index+Shift_LocalIndex+Shift_Comp));
    assert(nbits(iComp)<=Shift_Comp);
    return iComp + ( iField<<(Shift_Index+Shift_LocalIndex+Shift_Comp) );
  }
  inline int createTypeWithThreeInts(int iField, int iComp, int iIndex) // iLocalIndex=0
  {
    assert(nbits(iField)<=31-(Shift_Index+Shift_LocalIndex+Shift_Comp));
    assert(nbits(iComp)<=Shift_Comp);
    assert(nbits(iIndex)<=Shift_Index);
    return iComp + ( ( iIndex + (iField<<Shift_Index) ) << (Shift_LocalIndex+Shift_Comp) );
  }
  inline int createTypeWithFourInts(int iField, int iComp, int iIndex, int iLocalIndex) // iLocalIndex=0
  {
    assert(nbits(iField)<=31-(Shift_Index+Shift_LocalIndex+Shift_Comp));
    assert(nbits(iComp)<=Shift_Comp);
    assert(nbits(iIndex)<=Shift_Index);
    assert(nbits(iLocalIndex)<=Shift_LocalIndex);
    return iComp + ( ( iLocalIndex + ( ( iIndex + (iField<<Shift_Index) ) << Shift_LocalIndex ) ) << Shift_Comp );
  }

  inline void getTwoIntsFromType(int iType, int &iField, int &iComp) const // iIndex=0, iLocalIndex=0
  {
    iField = iType>>(Shift_Index+Shift_LocalIndex+Shift_Comp);
    iComp = iType-((iType>>Shift_Comp)<<Shift_Comp);
  }
  inline void getThreeIntsFromType(int iType, int &iField, int &iComp, int &iIndex) const
  {
    iField = iType>>(Shift_Index+Shift_LocalIndex+Shift_Comp);
    iComp = iType-((iType>>Shift_Comp)<<Shift_Comp);
    iIndex = (iType>>(Shift_LocalIndex+Shift_Comp))-(iField<<Shift_Index);
  }
  inline void getFourIntsFromType(int iType, int &iField, int &iComp, int &iIndex, int &iLocalIndex) const
  {
    iField = iType>>(Shift_Index+Shift_LocalIndex+Shift_Comp);
    iComp = iType-((iType>>Shift_Comp)<<Shift_Comp);
    iIndex = (iType>>(Shift_LocalIndex+Shift_Comp))-(iField<<Shift_Index);
    iLocalIndex = (iType>>Shift_Comp)-((iType>>(Shift_LocalIndex+Shift_Comp))<<Shift_LocalIndex);
  }


  inline int createField()
  {
    int Max_Field = (1ul<<(31-(Shift_Index+Shift_LocalIndex+Shift_Comp)))-1;
    assert(Nb_Fields<Max_Field);
    int iField = Nb_Fields;
    ++Nb_Fields;
    Nb_Indexes.push_back(0);
    return iField;
  }
  inline int createIndex(int iField)
  {
    assert(iField<Nb_Fields);
    int Max_Index = (1ul<<Shift_Index)-1;
    assert(Nb_Indexes[iField]<Max_Index);
    return ++Nb_Indexes[iField];
  }

 
  friend std::ostream & operator<<(std::ostream &os, const DofIdManager &manager)
  {
    os << "Number of Fields : " << manager.Nb_Fields << std::endl;
    for (int i=0; i<manager.Nb_Indexes.size(); ++i)
    {
      os << "Field[" << i << "] : " << manager.Nb_Indexes[i] << " Indexes" << std::endl;
    }
    return os;
  }
};

#endif // _GENDOFIDMANAGER_H_
