#include "GR_assert.h"

///
class VAType {
  ///
  enum {iMaxLists = 15};
  ///
  Type* apTData[iMaxLists];
  ///
  int iN, iMax, aiMax[iMaxLists], iNLists;
public:
  ///
  ~VAType();
  ///
  VAType(const int iSizeInUse = 0);
  ///
  VAType(const VAType& VA);
  ///
  void vClear() {iN = 0;}
  ///
  int iSize() const {return iN;}
  ///
  int iMaxSize() const {return iMax;}
  ///
  int iIndex(const Type* const pT) const;
  ///
  void vResize(const int iNS);
public:
  ///
  void vSetup(const int iNewSize);
  // The next two functions implement reference counting semantics, to
  // allow cheap copying, etc.  Used (among other places?) to copy
  // vertex lists when copy-constructing or operator=-ing BdryXD data. 
//    void vAttach();
//    void vDetach();
///   VAType* pVAUnalias();
  Type* pTNewEntry();
  ///
  Type* pTEntry(const int i) const;
  ///
  VAType& operator=(const VAType& VA);
  ///
  Type& operator[](const int i) const;
  ///
  Type* pTFirst() const;
  ///
  Type* pTLast() const;
  ///
  Type* pTNext(Type* const pTCurr) const;
  ///
  Type* pTPrev(Type* const pTCurr) const;
  ///
  bool qValid(const Type* const pT) const;
private:
  ///
  bool qValid(const int i) const;
};

#if defined(VAImplementation)
///
VAType::~VAType()
{
  for (int i = 0; i < iNLists; i ++) {
    assert(apTData[i] != NULL);
    delete [] apTData[i];
  }
  iNLists = 0;
}

///
VAType::VAType(const int iSizeInUse) :
  iN(iSizeInUse), iMax(max(1000, iSizeInUse)), iNLists(1)
{
  assert(iSizeInUse >= 0);
  aiMax[0] = iMax;
  iNLists = 1;
  apTData[0] = new Type[iMax];
}

///
VAType::VAType(const VAType& VA) :
  iN(VA.iN), iMax(VA.iMax), iNLists(VA.iNLists)
{
  assert(VA.iN >= 0);
  assert(VA.iMax > 0);
  int iSum = 0;
  for (int ii = 0; ii < iNLists; ii++) {
    assert(VA.aiMax[ii] >= 0);
    assert(VA.apTData[ii] != NULL);
    aiMax[ii] = VA.aiMax[ii];
    apTData[ii] = new Type[aiMax[ii]];
    for (int i = 0; (i < aiMax[ii]) && (iSum+i < iN); i++)
      apTData[ii][i] = VA.apTData[ii][i];
    iSum += aiMax[ii];
  }
  assert(iSum == iMax);
}

///
VAType& VAType::operator=(const VAType& VA)
{
  assert(VA.iN >= 0);
  assert(VA.iMax > 0);
  // Do nothing if the source says VA=VA.
  if (this != &VA) {
    // Clean up the existing data.
    int i;
    for (i = 0; i < iNLists; i++)
      delete [] apTData[i];
    
    iN = VA.iN;
    iMax = VA.iMax;
    iNLists = VA.iNLists;
    int iSum = 0;
    for (int ii = 0; ii < iNLists; ii++) {
      assert(VA.aiMax[ii] >= 0);
      assert(VA.apTData[ii] != NULL);
      aiMax[ii] = VA.aiMax[ii];
      apTData[ii] = new Type[aiMax[ii]];
      iSum += aiMax[ii];
      for (i = 0; i < aiMax[ii]; i++)
	apTData[ii][i] = VA.apTData[ii][i];
    }
    assert(iSum == iMax);
  }
  return (*this);
}

///
void VAType::vResize(const int iNewSize) {
  if (iNewSize == iMax) return;
  else if (iNewSize > iMax) {
    while (iMax < iNewSize && iNLists < iMaxLists) {
      apTData[iNLists] = new Type[iMax];
      aiMax[iNLists] = iMax;
      iMax *= 2;
      iNLists ++;
    }
    if (iMax < iNewSize) vFatalError("Out of space",
				     "resizing variable-sized array");
  }
  else {
    assert(iNewSize <= iMax);
    while (iMax - aiMax[iNLists - 1] >= iNewSize && iNLists > 1) {
      iNLists --;
      delete [] apTData[iNLists];
      iMax -= aiMax[iNLists];
      aiMax[iNLists] = 0;
    }
  }
}

///
int VAType::iIndex(const Type* const pT) const
{
  assert(qValid(pT));
  int iList = 0, iSum = 0;
  while (! ((const_cast<Type*>(pT) >= apTData[iList]) &&
	    (const_cast<Type*>(pT) < apTData[iList] + aiMax[iList])) &&
	 iList < iNLists) iSum += aiMax[iList++];
  assert(iList < iNLists);
  assert(const_cast<Type*>(pT) >= apTData[iList]);
  assert(const_cast<Type*>(pT) < apTData[iList] + aiMax[iList]);
  assert((reinterpret_cast<unsigned long>(pT) - reinterpret_cast<unsigned long>(apTData[iList]))
	 % sizeof(Type) == 0);
  return iSum + (const_cast<Type*>(pT) - apTData[iList]);
}

///
void VAType::vSetup(const int iNewSize)
{
  vResize(iNewSize);
  iN = iNewSize;
}

//  void VAType::vAttach() {++ usRefCount;}

//  void VAType::vDetach() {
//   assert(usRefCount>0);
//   if ((--usRefCount) == 0) delete this;
//  }

// VAType* VAType::pVAUnalias() {
//   if (usRefCount == 1) return (this);
//   --usRefCount;
//   return new VAType(*this);
/// }

Type* VAType::pTNewEntry()
{
  if (iN == iMax) vResize(iMax+1);
  iN++;
  return (pTEntry(iN-1));
}

///
Type* VAType::pTEntry(const int i) const
{
  assert(qValid(i));
  int iList = 0, iSum = 0;
  while (i >= (iSum += aiMax[iList]) && iList < iNLists) iList++;
  assert(iList < iNLists);
  iSum -= aiMax[iList];
  assert(iSum <= i && iSum + aiMax[iList] > i);
  return(apTData[iList] + (i - iSum));
}

///
Type& VAType::operator[](const int i) const
{
  assert(qValid(i));
  return (*pTEntry(i));
}

///
Type* VAType::pTFirst() const
{
  assert(iN >= 0);
  if (iN == 0) return (0);
  else         return(&(apTData[0][0]));
}

///
Type* VAType::pTLast() const
{
  assert(iN >= 0);
  if (iN == 0) return (0);
  else         return(pTEntry(iN-1));
}

///
Type* VAType::pTNext(Type* const pTCurr) const
{
  assert(qValid(const_cast<const Type*>(pTCurr)));
  int iList = 0, iSum = 0;
  while (! ((pTCurr >= apTData[iList]) &&
	    (pTCurr <  apTData[iList] + aiMax[iList])) &&
	 iList < iNLists) iSum += aiMax[iList++];
  assert(iList < iNLists);
  assert(pTCurr >= apTData[iList]);
  assert(pTCurr <  apTData[iList] + aiMax[iList]);
  assert((reinterpret_cast<unsigned long>(pTCurr) 
	  - reinterpret_cast<unsigned long>(apTData[iList]))
	 % sizeof(Type) == 0);
  // This seems to be wrong.  iSum -= aiMax[iList];
  if (pTCurr == pTLast()
      ||
      (pTCurr - apTData[iList] == aiMax[iList] - 1
       && iList == iNLists - 1) )
    return 0;
  else if (pTCurr - apTData[iList] == aiMax[iList] - 1)
    return apTData[iList+1];
  else
    return pTCurr+1;
}

///
Type* VAType::pTPrev(Type* const pTCurr) const
{
  assert(qValid(const_cast<const Type*>(pTCurr)));
  int iList = 0, iSum = 0;
  while (! ((pTCurr >= apTData[iList]) &&
	    (pTCurr <  apTData[iList] + aiMax[iList])) &&
	 iList < iNLists) iSum += aiMax[iList++];
  assert(iList < iNLists);
  assert(pTCurr >= apTData[iList]);
  assert(pTCurr <  apTData[iList] + aiMax[iList]);
  assert((reinterpret_cast<unsigned long>(pTCurr)
	  - reinterpret_cast<unsigned long>(apTData[iList]))
	 % sizeof(Type) == 0);
  // This seems to be wrong.  iSum -= aiMax[iList];
  if (iList == 0 && pTCurr - apTData[iList] == 0)
    return 0;
  else if (pTCurr - apTData[iList] == 0)
    return apTData[iList-1] + aiMax[iList-1] - 1;
  else
    return pTCurr-1;
}

///
bool VAType::qValid(const int i) const
{
  return ((i >= 0) && (i < iN));
}

// bool VAType::qValid(Type* const pT) const
// {
//   int iList = 0, iSum = 0;
//   while (! ((pT - apTData[iList]) >= 0 &&
// 	    (pT - apTData[iList]) <  aiMax[iList]) &&
// 	 iList < iNLists) iSum += aiMax[iList++];
//   if (iList == iNLists) return false;
//   else return true;
/// }

bool VAType::qValid(const Type* const pT) const
{
  int iList = 0, iSum = 0;
  while (! ((pT >= apTData[iList]) &&
	    (pT <  apTData[iList] + aiMax[iList])) &&
	 iList < iNLists) {
    iSum += aiMax[iList];
    iList++;
  }
  if (iList == iNLists) return false;
  else {
    assert(iList < iNLists);
    assert(pT >= apTData[iList]);
    assert(pT <  apTData[iList] + aiMax[iList]);
    // This next one must always be true, provided that pT is really a
    // pointer to Type.
    assert((reinterpret_cast<unsigned long>(pT) 
	    - reinterpret_cast<unsigned long>(apTData[iList]))
	   % sizeof(Type) == 0);
    return true;
  }
}
#endif 

#undef Type
#undef VAType
