// Copyright (c) 2010-2023, Lawrence Livermore National Security, LLC. Produced
// at the Lawrence Livermore National Laboratory. All Rights reserved. See files
// LICENSE and NOTICE for details. LLNL-CODE-806117.
//
// This file is part of the MFEM library. For more information and source code
// availability visit https://mfem.org.
//
// MFEM is free software; you can redistribute it and/or modify it under the
// terms of the BSD-3 license. We welcome feedback and contributions, see file
// CONTRIBUTING.md for details.


#include "error.hpp"
#include "stable3d.hpp"

using namespace std;

namespace mfem
{

STable3D::STable3D (int nr)
{
   int i;

   Size = nr;
   Rows = new STable3DNode *[nr];
   for (i = 0; i < nr; i++)
   {
      Rows[i] = NULL;
   }
   NElem = 0;
}

inline void Sort3 (int &r, int &c, int &f)
{
   int t;

   if (r > c)
      if (c > f)
      {
         t = r;  r = f;  f = t;  //  (r,c,f) --> (f,c,r)
      }
      else if (r > f)
      {
         t = r;  r = c;  c = f;  f = t;  //  (r,c,f) --> (c,f,r)
      }
      else
      {
         t = r;  r = c;  c = t;  //  (r,c,f) --> (c,r,f)
      }
   else if (c > f)
   {
      if (r > f)
      {
         t = f;  f = c;  c = r;  r = t;  //  (r,c,f) --> (f,r,c)
      }
      else
      {
         t = c;  c = f;  f = t;  //  (r,c,f) --> (r,f,c)
      }
   }
}

int STable3D::Push (int r, int c, int f)
{
   STable3DNode *node;

   MFEM_ASSERT(r != c && c != f && f != r,
               "STable3D::Push : r = " << r << ", c = " << c << ", f = " << f);

   Sort3 (r, c, f);

   for (node = Rows[r]; node != NULL; node = node->Prev)
   {
      if (node->Column == c)
         if (node->Floor == f)
         {
            return node->Number;
         }
   }

#ifdef MFEM_USE_MEMALLOC
   node = NodesMem.Alloc ();
#else
   node = new STable3DNode;
#endif
   node->Column = c;
   node->Floor  = f;
   node->Number = NElem;
   node->Prev   = Rows[r];
   Rows[r] = node;

   NElem++;
   return (NElem-1);
}

int STable3D::operator() (int r, int c, int f) const
{
   STable3DNode *node;

   Sort3 (r, c, f);

   for (node = Rows[r]; node != NULL; node = node->Prev)
   {
      if (node->Column == c)
         if (node->Floor == f)
         {
            return node->Number;
         }
   }

   MFEM_ABORT("(r,c,f) = (" << r << "," << c << "," << f << ")");

   return 0;
}

int STable3D::Index (int r, int c, int f) const
{
   STable3DNode *node;

   if (r >= Size)
   {
      return -1;
   }

   Sort3 (r, c, f);

   for (node = Rows[r]; node != NULL; node = node->Prev)
   {
      if (node->Column == c)
         if (node->Floor == f)
         {
            return node->Number;
         }
   }

   return -1;
}

int STable3D::Push4 (int r, int c, int f, int t)
{
   MFEM_ASSERT(r != c && r != f && r != t && c != f && c != t && f != t,
               " r = " << r << ", c = " << c << ", f = " << f << ", t = " << t);

   int i = 0;
   int max = r;

   if (max < c) { max = c, i = 1; }
   if (max < f) { max = f, i = 2; }
   if (max < t) { i = 3; }

   switch (i)
   {
      case 0:
         return Push (c,f,t);
      case 1:
         return Push (r,f,t);
      case 2:
         return Push (r,c,t);
      case 3:
         return Push (r,c,f);
   }

   return -1;
}

int STable3D::operator() (int r, int c, int f, int t) const
{
   int i = 0;
   int max = r;

   if (max < c) { max = c, i = 1; }
   if (max < f) { max = f, i = 2; }
   if (max < t) { i = 3; }

   switch (i)
   {
      case 0:
         return (*this)(c,f,t);
      case 1:
         return (*this)(r,f,t);
      case 2:
         return (*this)(r,c,t);
      case 3:
         return (*this)(r,c,f);
   }

   return -1;
}

STable3D::~STable3D ()
{
#ifdef MFEM_USE_MEMALLOC
   // NodesMem.Clear();  // this is done implicitly
#else
   for (int i = 0; i < Size; i++)
   {
      STable3DNode *aux, *node_p = Rows[i];
      while (node_p != NULL)
      {
         aux = node_p;
         node_p = node_p->Prev;
         delete aux;
      }
   }
#endif
   delete [] Rows;
}

void STable3D::Print(std::ostream & os) const
{
   os << NElem << endl;
   for (int row = 0; row < Size; row++)
   {
      STable3DNode *node_p = Rows[row];
      while (node_p != NULL)
      {
         os << row
            << ' ' << node_p->Column
            << ' ' << node_p->Floor
            << ' ' << node_p->Number
            << endl;
         node_p = node_p->Prev;
      }
   }
}

}
