// ============================================================================
// ==                                                                        ==
// == AMDiS - Adaptive multidimensional simulations                          ==
// ==                                                                        ==
// ============================================================================
// ==                                                                        ==
// ==  crystal growth group                                                  ==
// ==                                                                        ==
// ==  Stiftung caesar                                                       ==
// ==  Ludwig-Erhard-Allee 2                                                 ==
// ==  53175 Bonn                                                            ==
// ==  germany                                                               ==
// ==                                                                        ==
// ============================================================================
// ==                                                                        ==
// ==  http://www.caesar.de/cg/AMDiS                                         ==
// ==                                                                        ==
// ============================================================================

/** \file ComponentTraverseInfo.h */

#ifndef AMDIS_COMPONENTTRAVERSEINFO_H
#define AMDIS_COMPONENTTRAVERSEINFO_H

#include <vector>

#include "FiniteElemSpace.h"

namespace AMDiS {

  class SingleComponentInfo
  {      
  public:
    SingleComponentInfo()
      : rowFESpace(NULL),
	colFESpace(NULL),
	auxFESpaces(0),
	status(0)
      {}
    
    void setFESpace(FiniteElemSpace *row, FiniteElemSpace *col = NULL) {
      rowFESpace = row;
      colFESpace = col;
    }
    
    void setAuxFESpaces(std::vector<const FiniteElemSpace*> feSpaces) {
      auxFESpaces = feSpaces;
    }

    void addAuxFESpace(const FiniteElemSpace *fe) {
      auxFESpaces.push_back(fe);
    }
    
    bool hasFESpace() {
      return rowFESpace != NULL;
    }

    void updateStatus();
    
    int getNumAuxFESpaces() {
      return auxFESpaces.size();
    }
    
    FiniteElemSpace *getRowFESpace() {
      return rowFESpace;
    }
    
    FiniteElemSpace *getColFESpace() {
      return colFESpace;
    }
    
    const FiniteElemSpace *getAuxFESpace(int i) {
      return ((i < static_cast<int>(auxFESpaces.size())) ? auxFESpaces[i] : NULL);
    }

    void setAuxFESpace(const FiniteElemSpace* fe, int pos) {
      auxFESpaces[pos] = fe;
    }

    int getStatus() {
      return status;
    }
    
  protected:      
    FiniteElemSpace *rowFESpace;
    
    FiniteElemSpace *colFESpace;
    
    std::vector<const FiniteElemSpace*> auxFESpaces;

    /// Status of the component, see the possible status flags below.
    int status;

  public:
    /// Single component status flag: empty component, no fe spaces
    static const int EMPTY;

    /// Single component status flag: row = col, no aux
    static const int EQ_SPACES_NO_AUX;

    /// Single component status flag: row = col = aux
    static const int EQ_SPACES_WITH_AUX;

    /// Single component status flag: row = col, different aux
    static const int EQ_SPACES_WITH_DIF_AUX;

    /// Single component status flag: row, col, no aux
    static const int DIF_SPACES_NO_AUX;

    /// Single component status flag: row, col, aux either equal to row or to col
    static const int DIF_SPACES_WITH_AUX;

    /// Single component status flag: row, col, aux (at least 3 different fe spaces)
    static const int DIF_SPACES_WITH_DIF_AUX;
  };

  
  class ComponentTraverseInfo
  {
  public: 
    ComponentTraverseInfo(int n) 
      : nComponents(n)
    {
      resize(n);
    }

    void resize(int n) {
      nComponents = n;

      matrixComponents.resize(n);
      vectorComponents.resize(n);

      for (int i = 0; i < n; i++) {
	matrixComponents[i].resize(n);
      }
    }

    void updateStatus() {
      for (int i = 0; i < nComponents; i++) {
	for (int j = 0; j < nComponents; j++) {
	  matrixComponents[i][j].updateStatus();
	}
	vectorComponents[i].updateStatus();
      }
    }

    SingleComponentInfo &getMatrix(int row, int col) {
      return matrixComponents[row][col];
    }

    SingleComponentInfo &getVector(int row) {
      return vectorComponents[row];
    }

    int getStatus(int row, int col) {
      return matrixComponents[row][col].getStatus();
    }

    int getStatus(int row) {
      return vectorComponents[row].getStatus();
    }

    const FiniteElemSpace* getAuxFESpace(int row, int col) {
      return matrixComponents[row][col].getAuxFESpace(0);
    }

    const FiniteElemSpace* getAuxFESpace(int row) {
      return vectorComponents[row].getAuxFESpace(0);
    }

    void fakeFESpace(const FiniteElemSpace *feOld, 
		     const FiniteElemSpace *feNew)
    {
      for (int i = 0; i < nComponents; i++) {
	for (int j = 0; j < nComponents; j++) {
	  if (matrixComponents[i][j].getAuxFESpace(0) == feOld) {
	    matrixComponents[i][j].setAuxFESpace(feNew, 0);
	  }
	}

	if (vectorComponents[i].getAuxFESpace(0) == feOld) {
	  vectorComponents[i].setAuxFESpace(feNew, 0);
	}
      }
    }

  protected:
    int nComponents;

    std::vector<std::vector<SingleComponentInfo> > matrixComponents;

    std::vector<SingleComponentInfo> vectorComponents;
  };

}

#endif