#ifndef SRC_BOUNDARYCONDITION_HH
#define SRC_BOUNDARYCONDITION_HH

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

#include <dune/fufem/boundarypatch.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),
      boundaryPatch_(patch),
      boundaryFunction_(function)
  {}

  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;
  }

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

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

  void set(std::shared_ptr<BoundaryPatch> patch, std::shared_ptr<Function> function) {
      boundaryPatch_ = 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_;
  }
};


#include <dune/fufem/geometry/convexpolyhedron.hh>

#include <dune/tectonic/globalfrictiondata.hh>

template <class GridView, class LocalVectorType, int dims>
class FrictionBoundaryCondition : public BoundaryCondition<GridView, dims>{
private:
    using Base = BoundaryCondition<GridView, dims>;
    using LocalVector = LocalVectorType;

    // friction data
    std::shared_ptr<ConvexPolyhedron<LocalVector>> weakeningPatch_;
    std::shared_ptr<GlobalFrictionData<dims>> frictionData_;

public:
  FrictionBoundaryCondition() :
      Base("friction")
  {}

  FrictionBoundaryCondition(std::shared_ptr<typename Base::BoundaryPatch> patch, std::shared_ptr<ConvexPolyhedron<LocalVector>> weakeningPatch, std::shared_ptr<GlobalFrictionData<dims>> frictionData) :
      Base(patch, nullptr, "friction"),
      weakeningPatch_(weakeningPatch),
      frictionData_(frictionData)
  {}

  void setWeakeningPatch(std::shared_ptr<ConvexPolyhedron<LocalVector>> weakeningPatch) {
      weakeningPatch_ = weakeningPatch;
  }

  void setFrictionData(std::shared_ptr<GlobalFrictionData<dims>> frictionData) {
      frictionData_ = frictionData;
  }

  const std::shared_ptr<ConvexPolyhedron<LocalVector>>& weakeningPatch() const {
      return weakeningPatch_;
  }

  const std::shared_ptr<GlobalFrictionData<dims>>& frictionData() const {
      return frictionData_;
  }
};
#endif