#ifndef HL_SIGNEDDISTLEVELS
#define HL_SIGNEDDISTLEVELS

#include <queue>
#include <vector>
#include <time.h>
#include "ElInfo.h"
#include "FixVec.h"
#include "Traverse.h"
#include "ElementLevelSet.h"
#include "BoundaryElementDist.h"
#include "ElementUpdate.h"
#include "ElementUpdate_2d.h"
#include "ElementUpdate_3d.h"
#include "HL_SignedDist.h"
#include "VelocityExt.h"
#include "SMIAdapter.h"
#include "smi.h"

using namespace AMDiS;

typedef struct
{
  int ElNum;
  int VertNum;
} Vert_Struct;

class HL_SignedDistLevels : public HL_SignedDist
{
public:
  HL_SignedDistLevels(const char *name_, 
		      int dim_,
		      bool doVelocityExt = false,
		      Flag velExtType_ = VEL_EXT)
    : HL_SignedDist(name_, dim_, doVelocityExt, velExtType_),
      smiAdapter(NULL)
  {
    FUNCNAME("HL_SignedDistLevels::HL_SignedDistLevels()");

    // ===== Read parameters from init file. =====
    GET_PARAMETER(0, name + "->tolerance", "%f", &tol);
    GET_PARAMETER(0, name + "->count_how_often_saved_in_list", "%d", &count_in_list);
    GET_PARAMETER(0, name + "->save_in_list->the ..th", "%d", &print_in_list);
    GET_PARAMETER(0, name + "->save_in_list->after the ..th traversing of the list", "%d", &print_in_list_2);
    GET_PARAMETER(0, name + "->print_level", "%d", &chosen_level);
    GET_PARAMETER(0, name + "->print_level_yes_no", "%d", &print_level);

    TEST_EXIT(tol > 0)("illegal tolerance !\n");
  }

 protected:
  /**
   * Initializes the boundary: calculation of the distance of boundary 
   * vertices to the interface. 
   * Interface is given by \ref lS_DOF and result is stored in 
   * \ref sD_DOF.
   */
  void initializeBoundary();

  /**
   * function for transvering Mesh to SMi an Adding the quantities
   **/
  void Mesh_to_SMI_and_quantity ();

  /**
   * function for creating the first list, in which elementnumber of 
   * boundary-elements are saved
   */
  void createFirstList(const int elStatus, 
		       const int *elVertStatusVec, 
		       int ElNum, 
		       const int NumVertIntPoints);

  /**
   * Calculates the distance function and stores result in sD_DOF.
   * Requirement: The boundary values are already set in \ref sD_DOF.
   */
  void HL_updateIteration();

  /**
   * function for traversing the list "List_Element"
   * returns 1 if min one element is set in the new list
   * returns 0 if no element is set in the new list
   */ 
  int traverseListElement();

  /**
   * function for creating all levels higher than 1 an saving pairs of 
   * elements and node in the right list
   **/
  void createLevels ();
 
  /**
   * function collects the next neighbours, creates an according list 
   * and saves the pair in it
   * returns true if one or more neighbours exist and false if not
   **/
  bool collectNeighbours_setLevels (const int Element,
				    const int currentIndex,
				    bool *elementInNewListSet);

  /**
   * function for traversing the list of list, named "Level_"
   **/ 
  void traversingListLevel_ ( DOFVector<double> *boundVal_DOF );
 
  /**
   * calls the next neighbours and puts them into the right list
   * returns true if an element had to be saved in an lower level 
   * than the current index
   * this means the loop over all lists Level_[i] has to be repaeated again
   * "Vert" is a local node
   **/
  bool search_and_include_comb(int ElNum, 
			       int Vert, 
			       int *nodeIndices, 
			       const int Index);

  /**
   * checking whether the element "ElNum_in" has to be included into 
   * the second list
   * if yes it will be included
   * returns true if the level of the element is smaler than he current index
   * returns false if the level is greater thean the current index
   * "Vert_1_in" and "Vert_2_in" are localvertizes
   */ 
  bool includeIntoList (int ElNum_in, 
			int VertNum_1_in, 
			int VertNum_2_in, 
			const int Index);
 
  /**
   * gets the neighbour according to the node "VertNum_in" and two of 
   * its nodes (in local koordinates)
   * returns "false" if the called neighbour exists and "true" if not
   * Vert_Up_in is given in global coordinates, the other points are 
   * given in local coordinates
   */ 
  bool getNextNeighbour (const int ElNum_in, 
			 const int Vert_Up_in, 
			 const int VertNum_in, 
			 int &ElNum_out, 
			 int &VertNum_1_out, 
			 int &VertNum_2_out);

  /**
   * function needed in the function "traversingListELVert"
   **/
  int getNext_node_l_r (int elem_l_r_in,
			int neighbour_l_r_in, 
			int node_l_r_in, 
			const int Vert);

  //==================================
  void print_quantity_5 (int cntr);

  /**
   * function for printinq the quantity 6
   * attention: we need at least read_transaction in smi for calling 
   * this function
   **/
  void print_quantity_6 ();
  //====================================

 protected:
  /**
   * Tolerance for Hopf-Lax update iteration loop.
   */
  double tol;

  /**
   * is needed for transfering the mesh to SMI
   */
  SMIAdapter *smiAdapter;

  /**
   * in this list boundary-elements are saved;
   * is needed for creating the list "List_El_Vert"
   */
  queue<int> List_Element;
  
  /**
   * in this list structs filled with element-number and vertex-number 
   * are saved;
   * is needed for traversing the mesh efficiently
   */
  queue<Vert_Struct> Level;
  
  queue<Vert_Struct> helpLevel;
  
  queue<Vert_Struct> List_El_Vert;       //Listen noch einmal kontrollieren!
  
  /**
   * in this vector the lists of different levels are saved
   **/
  vector<queue<Vert_Struct> > Level_;

  /**
   * 0 ->do not count updates
   * 1 ->count updates
   **/ 
  int count_updates; 

  /**
   * 1 -> it will be count how often a node is saved in the second list
   * 0 ->it will not be count
   **/ 
  int count_in_list; 

  int print_in_list;
  int print_in_list_2;
  
  /**
   * level which will be printed in a file
   **/
  int chosen_level;
  
  /**
   * 1->a special level will be printed in a file
   * 0->no level is printed in a file
   **/
  int print_level;
};

#endif  // HL_SIGNEDDISTLEVELS