#ifndef CORRECTIONVECTORACTION_H
#define CORRECTIONVECTORACTION_H
#include "Vector.h"
#include "FreqEnum.h"
#include "AnsiColors.h"

namespace Dmrg {

template<typename ComplexOrRealType,
         typename TargetParamsType,
         bool isComplex = PsimagLite::IsComplexNumber<ComplexOrRealType>::True>
class CorrectionVectorActionBase {

public:

	typedef typename PsimagLite::Real<ComplexOrRealType>::Type RealType;
	typedef typename PsimagLite::Vector<RealType>::Type VectorRealType;

	enum ActionEnum {ACTION_IMAG, ACTION_REAL};

	CorrectionVectorActionBase(const TargetParamsType& tstStruct,
	                           RealType E0,
	                           const VectorRealType& eigs)
	    : tstStruct_(tstStruct),E0_(E0),eigs_(eigs)
	{
		if (tstStruct_.omega().first == PsimagLite::FREQ_REAL)
			return; // <--- EARLY EXIT
		std::cout<<PsimagLite::AnsiColor::red;
		std::cout<<"CorrectionVectorActionBase:: Running matsubara\n";
		std::cout<<PsimagLite::AnsiColor::reset;
	}

	void setReal() const;

	void setImag() const { action_ = ACTION_IMAG; }

	static bool isValueComplex() { return isComplex; }

protected:

	RealType actionWhenMatsubara(SizeType k) const
	{
		RealType sign = (tstStruct_.type() == 0) ? -1.0 : 1.0;
		RealType wn = tstStruct_.omega().second;
		RealType part1 =  (eigs_[k] - E0_)*sign;
		RealType denom = part1*part1 + wn*wn;
		return (action_ == ACTION_IMAG) ? wn/denom : -part1 / denom;
	}

	const TargetParamsType& tstStruct_;
	RealType E0_;
	const VectorRealType& eigs_;
	mutable ActionEnum action_;
};

template<typename ComplexOrRealType,
         typename TargetParamsType,
         bool isComplex = PsimagLite::IsComplexNumber<ComplexOrRealType>::True>
class CorrectionVectorAction {};

template<typename ComplexOrRealType, typename TargetParamsType>
class CorrectionVectorAction<ComplexOrRealType, TargetParamsType, false> : public
        CorrectionVectorActionBase<ComplexOrRealType, TargetParamsType> {

public:

	typedef CorrectionVectorActionBase<ComplexOrRealType, TargetParamsType> BaseType;
	typedef typename BaseType::RealType RealType;

	CorrectionVectorAction(const TargetParamsType& tstStruct,
	                       RealType E0,
	                       const typename BaseType::VectorRealType& eigs)
	    : BaseType(tstStruct, E0, eigs) {}

	ComplexOrRealType operator()(SizeType k) const
	{
		return (BaseType::tstStruct_.omega().first == PsimagLite::FREQ_REAL)
		        ? actionWhenFreqReal(k) : BaseType::actionWhenMatsubara(k);
	}

	void setReal() const
	{
		BaseType::action_ = BaseType::ACTION_REAL;
	}

private:

	ComplexOrRealType actionWhenFreqReal(SizeType k) const
	{
		RealType sign = (BaseType::tstStruct_.type() == 0) ? -1.0 : 1.0;
		RealType part1 =  (BaseType::eigs_[k] - BaseType::E0_)*sign + BaseType::tstStruct_.omega().second;
		const SizeType nFraction = BaseType::tstStruct_.nForFraction();

		if (nFraction==1) {
			RealType denom = part1*part1 + BaseType::tstStruct_.eta()*BaseType::tstStruct_.eta();
			return (BaseType::action_ == BaseType::ACTION_IMAG) ? BaseType::tstStruct_.eta()/denom :
			                                                      -part1/denom;
		} else {
			RealType exponent = 1.0/(2.0*nFraction);
			RealType denom = pow(part1*part1 + BaseType::tstStruct_.eta()*BaseType::tstStruct_.eta(),
			                     exponent);
			RealType denom1 = sqrt(part1*part1 + BaseType::tstStruct_.eta()*BaseType::tstStruct_.eta());
			RealType cosreal = cos(acos(part1/denom1)/nFraction);
			RealType sinreal = sin(acos(part1/denom1)/nFraction);
			return (BaseType::action_ == BaseType::ACTION_IMAG) ? sinreal/denom :
			                                                      cosreal/denom;
		}
	}
};

template<typename ComplexOrRealType, typename TargetParamsType>
class CorrectionVectorAction<ComplexOrRealType, TargetParamsType, true> : public
        CorrectionVectorActionBase<ComplexOrRealType, TargetParamsType>
{

public:

	typedef CorrectionVectorActionBase<ComplexOrRealType, TargetParamsType> BaseType;
	typedef typename BaseType::RealType RealType;

	CorrectionVectorAction(const TargetParamsType& tstStruct,
	                       RealType E0,
	                       const typename BaseType::VectorRealType& eigs)
	    : BaseType(tstStruct, E0, eigs)
	{
		if (firstCall_) {
			std::cout<<PsimagLite::AnsiColor::red;
			std::cerr<<PsimagLite::AnsiColor::red;
			std::cout<<"CorrectionVectorAction:: Complex mode\n";
			std::cerr<<"CorrectionVectorAction:: Complex mode\n";
			std::cout<<PsimagLite::AnsiColor::reset;
			std::cerr<<PsimagLite::AnsiColor::reset;
			firstCall_ = false;
		}
	}

	ComplexOrRealType operator()(SizeType k) const
	{
		return (BaseType::tstStruct_.omega().first == PsimagLite::FREQ_REAL)
		        ? actionWhenFreqReal(k) : BaseType::actionWhenMatsubara(k);
	}

	void setReal() const
	{
		err("CorrectionVectorSkeleton::Action: Cannot set to real\n");
	}

private:

	ComplexOrRealType actionWhenFreqReal(SizeType k) const
	{
		const SizeType nFraction = BaseType::tstStruct_.nForFraction();
		RealType sign = (BaseType::tstStruct_.type() == 0) ? -1.0 : 1.0;
		RealType part1 =  (BaseType::eigs_[k] - BaseType::E0_)*sign +
		        BaseType::tstStruct_.omega().second;
		if (nFraction == 1) {
			RealType denom = part1*part1 + BaseType::tstStruct_.eta()*BaseType::tstStruct_.eta();

			return ComplexOrRealType(part1/denom, -BaseType::tstStruct_.eta()/denom);
		}

		ComplexOrRealType cmplx(part1, BaseType::tstStruct_.eta());
		const RealType oneReal = 1.0;
		return oneReal/pow(cmplx, oneReal/nFraction);

	}

	static bool firstCall_;
};

template<typename ComplexOrRealType, typename TargetParamsType>
bool CorrectionVectorAction<ComplexOrRealType, TargetParamsType, true>::firstCall_ = true;

}
#endif // CORRECTIONVECTORACTION_H
