#include "FiniteElemSpace.h"
#include "DOFAdmin.h"
#include "DOFVector.h"
#include "BasisFunction.h"
#include "Mesh.h"
#include <algorithm>

namespace AMDiS {

  std::vector<FiniteElemSpace*> FiniteElemSpace::feSpaces;

  FiniteElemSpace::FiniteElemSpace(DOFAdmin* admin_,
				   const BasisFunction* bas_fcts,
				   Mesh* aMesh, 
				   std::string aString)
    : name(aString), 
      admin(admin_), 
      basFcts(bas_fcts), 
      mesh(aMesh)
  {
    FUNCNAME("FiniteElemSpace::FiniteElemSpace()");

    TEST_EXIT(mesh)("no Mesh\n");
   
    if (!admin) {
      const DOFAdmin *admin_local = NULL;
      const DimVec<int> *ndof = NULL;
      ndof = basFcts->getNumberOfDOFs();
      TEST_EXIT(ndof)("no n_dof or basFcts->n_dof\n");
      for (int i = 0; i < mesh->getNumberOfDOFAdmin(); i++) {
	admin_local = &(mesh->getDOFAdmin(i));
	int j = 0;
	for (; j <= mesh->getDim(); j++)
	  if (admin_local->getNumberOfDOFs(j) != (*ndof)[j]) 
	    break;
	if (j > mesh->getDim()) 
	  break;
	admin_local = NULL;
      }
      if (!admin_local) 
	admin_local = mesh->createDOFAdmin(name, *ndof);

      admin = const_cast<DOFAdmin*>(admin_local);
    }

    feSpaces.push_back(this);
  }
  
  FiniteElemSpace::FiniteElemSpace()
  {}

  FiniteElemSpace::~FiniteElemSpace()
  {}

  FiniteElemSpace& FiniteElemSpace::operator=(const FiniteElemSpace& feSpace)
  {
    if (&feSpace == this)
      return *this;

    mesh = new Mesh(feSpace.mesh->getName(), feSpace.mesh->getDim());
    *mesh = *(feSpace.mesh);
    admin = &(const_cast<DOFAdmin&>(mesh->getDOFAdmin(0)));
    basFcts = feSpace.basFcts;

    TEST_EXIT(feSpace.admin == &(feSpace.mesh->getDOFAdmin(0)))
      ("Gut, dass muss ich mir noch mal ueberlegen!\n");

    return *this;
  }

  FiniteElemSpace *FiniteElemSpace::provideFESpace(DOFAdmin *admin,
						   const BasisFunction *basFcts,
						   Mesh *mesh,
						   std::string name_)
  {
    int numSpaces = static_cast<int>(feSpaces.size());

    for (int i = 0; i < numSpaces; i++)
      if (feSpaces[i]->basFcts == basFcts && 
	  feSpaces[i]->mesh == mesh &&
	  (!admin || (admin && feSpaces[i]->admin == admin)))
	return feSpaces[i];

    return new FiniteElemSpace(admin, basFcts, mesh, name_);    
  }

  int FiniteElemSpace::calcMemoryUsage() 
  {
    int result = sizeof(FiniteElemSpace);
    result += mesh->calcMemoryUsage();
    return result;
  }

  void FiniteElemSpace::clear()
  {
    for (int i = 0; i < static_cast<int>(feSpaces.size()); i++) {
      if (feSpaces[i]) {
	delete feSpaces[i];
	feSpaces[i] = NULL;
      }
    }
  }
}