#ifndef SRC_BOUNDARYCONDITION_HH
#define SRC_BOUNDARYCONDITION_HH

#include <dune/common/bitsetvector.hh>
#include <dune/common/function.hh>

#include <dune/fufem/boundarypatch.hh>

#include <dune/tectonic/utils/tobool.hh>

template <class GridView, int dims>
class BoundaryCondition {
public:
  using BoundaryPatch = BoundaryPatch<GridView>;

private:
  using Function = Dune::VirtualFunction<double, double>;

  const std::string tag_; // store type of boundary condition, e.g. dirichlet, neumann, friction, etc

  std::shared_ptr<BoundaryPatch> boundaryPatch_;
  std::shared_ptr<Dune::BitSetVector<dims>> boundaryNodes_;
  std::shared_ptr<Function> boundaryFunction_;

public:
  BoundaryCondition(const std::string& tag = "") :
      tag_(tag)
  {}

  BoundaryCondition(std::shared_ptr<BoundaryPatch> patch, std::shared_ptr<Function> function, const std::string& tag = "") :
      tag_(tag),
      boundaryFunction_(function)
  {
      setBoundaryPatch(patch);
  }

  void setBoundaryPatch(const GridView& gridView, std::shared_ptr<Dune::BitSetVector<dims>> nodes) {
      boundaryNodes_ = nodes;
      boundaryPatch_ = std::make_shared<BoundaryPatch>(gridView, *nodes);
  }

  void setBoundaryPatch(std::shared_ptr<BoundaryPatch> patch) {
      boundaryPatch_ = patch;

      auto nodes = patch->getVertices();
      boundaryNodes_ = std::make_shared<Dune::BitSetVector<dims>>(nodes->size(), false);
      for (size_t i=0; i<nodes->size(); i++) {
          if (toBool((*nodes)[i])) {
             for (size_t d=0; d<dims; d++) {
                 (*boundaryNodes_)[i][d] = true;
             }
          }
      }
  }

  void setBoundaryPatch(const BoundaryPatch& patch) {
      auto patch_ptr = std::make_shared<BoundaryPatch>(patch);
      setBoundaryPatch(patch_ptr);
  }

  void setBoundaryFunction(std::shared_ptr<Function> function) {
      boundaryFunction_ = function;
  }

  void set(std::shared_ptr<BoundaryPatch> patch, std::shared_ptr<Function> function) {
      setBoundaryPatch(patch);
      boundaryFunction_ = function;
  }

  const std::string& tag() const {
      return tag_;
  }

  const std::shared_ptr<BoundaryPatch>& boundaryPatch() const {
      return boundaryPatch_;
  }

  const std::shared_ptr<Dune::BitSetVector<dims>>& boundaryNodes() const {
      return boundaryNodes_;
  }

  const std::shared_ptr<Function>& boundaryFunction() const {
      return boundaryFunction_;
  }
};

#endif