/*************************************************************************************

    Grid physics library, www.github.com/paboyle/Grid

    Source file: ./lib/qcd/action/fermion/CompactWilsonCloverFermion5D.h

    Copyright (C) 2020 - 2025

    Author: Daniel Richtmann <daniel.richtmann@gmail.com>
    Author: Nils Meyer <nils.meyer@ur.de>
    Author: Christoph Lehner <christoph@lhnr.de>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

    See the full license in the file "LICENSE" in the top level distribution directory
    *************************************************************************************/
/*  END LEGAL */

#pragma once

#include <Grid/qcd/action/fermion/WilsonFermion5D.h>
#include <Grid/qcd/action/fermion/WilsonCloverTypes.h>
#include <Grid/qcd/action/fermion/WilsonCloverHelpers.h>
#include <Grid/qcd/action/fermion/CloverHelpers.h>

NAMESPACE_BEGIN(Grid);

// see Grid/qcd/action/fermion/CompactWilsonCloverFermion.h for description

template<class Impl, class CloverHelpers>
class CompactWilsonCloverFermion5D : public WilsonFermion5D<Impl>,
				     public WilsonCloverHelpers<Impl>,
				     public CompactWilsonCloverHelpers<Impl> {
  /////////////////////////////////////////////
  // Sizes
  /////////////////////////////////////////////

public:

  INHERIT_COMPACT_CLOVER_SIZES(Impl);

  /////////////////////////////////////////////
  // Type definitions
  /////////////////////////////////////////////

public:

  INHERIT_IMPL_TYPES(Impl);
  INHERIT_CLOVER_TYPES(Impl);
  INHERIT_COMPACT_CLOVER_TYPES(Impl);

  typedef WilsonFermion5D<Impl>            WilsonBase;
  typedef WilsonCloverHelpers<Impl>        Helpers;
  typedef CompactWilsonCloverHelpers<Impl> CompactHelpers;

  /////////////////////////////////////////////
  // Constructors
  /////////////////////////////////////////////

public:

  CompactWilsonCloverFermion5D(GaugeField& _Umu,
			       GridCartesian         &FiveDimGrid,
			       GridRedBlackCartesian &FiveDimRedBlackGrid,
			       GridCartesian         &FourDimGrid,
			       GridRedBlackCartesian &FourDimRedBlackGrid,
			       const RealD _mass,
			       const RealD _csw_r = 0.0,
			       const RealD _csw_t = 0.0,
			       const RealD _cF = 1.0,
			       const ImplParams& impl_p = ImplParams());

  /////////////////////////////////////////////
  // Member functions (implementing interface)
  /////////////////////////////////////////////

public:

  virtual void Instantiatable() {};
  int          ConstEE()     override { return 0; };
  int          isTrivialEE() override { return 0; };

  void Dhop(const FermionField& in, FermionField& out, int dag) override;

  void DhopOE(const FermionField& in, FermionField& out, int dag) override;

  void DhopEO(const FermionField& in, FermionField& out, int dag) override;

  void DhopDir(const FermionField& in, FermionField& out, int dir, int disp) override;

  void DhopDirAll(const FermionField& in, std::vector<FermionField>& out) /* override */;

  void M(const FermionField& in, FermionField& out) override;

  void Mdag(const FermionField& in, FermionField& out) override;

  void Meooe(const FermionField& in, FermionField& out) override;

  void MeooeDag(const FermionField& in, FermionField& out) override;

  void Mooee(const FermionField& in, FermionField& out) override;

  void MooeeDag(const FermionField& in, FermionField& out) override;

  void MooeeInv(const FermionField& in, FermionField& out) override;

  void MooeeInvDag(const FermionField& in, FermionField& out) override;

  void Mdir(const FermionField& in, FermionField& out, int dir, int disp) override;

  void MdirAll(const FermionField& in, std::vector<FermionField>& out) override;

  void MDeriv(GaugeField& force, const FermionField& X, const FermionField& Y, int dag) override;

  void MooDeriv(GaugeField& mat, const FermionField& U, const FermionField& V, int dag) override;

  void MeeDeriv(GaugeField& mat, const FermionField& U, const FermionField& V, int dag) override;

  /////////////////////////////////////////////
  // Member functions (internals)
  /////////////////////////////////////////////

  void MooeeInternal(const FermionField&        in,
                     FermionField&              out,
                     const CloverDiagonalField& diagonal,
                     const CloverTriangleField& triangle);

  /////////////////////////////////////////////
  // Helpers
  /////////////////////////////////////////////

  void ImportGauge(const GaugeField& _Umu) override;

  /////////////////////////////////////////////
  // Helpers
  /////////////////////////////////////////////

private:

  template<class Field>
  const MaskField* getCorrectMaskField(const Field &in) const {
    if(in.Grid()->_isCheckerBoarded) {
      if(in.Checkerboard() == Odd) {
        return &this->BoundaryMaskOdd;
      } else {
        return &this->BoundaryMaskEven;
      }
    } else {
      return &this->BoundaryMask;
    }
  }

  template<class Field>
  void ApplyBoundaryMask(Field& f) {
    const MaskField* m = getCorrectMaskField(f); GRID_ASSERT(m != nullptr);
    GRID_ASSERT(m != nullptr);
    CompactHelpers::ApplyBoundaryMask(f, *m);
  }

  /////////////////////////////////////////////
  // Member Data
  /////////////////////////////////////////////

public:

  RealD csw_r;
  RealD csw_t;
  RealD cF;
  int n_rhs;
  
  bool fixedBoundaries;

  CloverDiagonalField Diagonal,    DiagonalEven,    DiagonalOdd;
  CloverDiagonalField DiagonalInv, DiagonalInvEven, DiagonalInvOdd;

  CloverTriangleField Triangle,    TriangleEven,    TriangleOdd;
  CloverTriangleField TriangleInv, TriangleInvEven, TriangleInvOdd;

  FermionField Tmp;

  MaskField BoundaryMask, BoundaryMaskEven, BoundaryMaskOdd;
};

NAMESPACE_END(Grid);
