/* -*- mia-c++  -*-
 *
 * This file is part of MIA - a toolbox for medical image analysis 
 * Copyright (c) Leipzig, Madrid 1999-2017 Gert Wollny
 *
 * MIA 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 3 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 MIA; if not, see <http://www.gnu.org/licenses/>.
 *
 */

#include <mia/2d/cstkernel.hh>
#include <mia/core/dictmap.hh>

NS_BEGIN(cstkernel_2dvector)
NS_MIA_USE;
using namespace std; 


enum EBoundary { bc_unknown, bc_neumann, bc_dirichlet}; 
enum EPointLoc { pl_unknwon, pl_mid, pl_grid}; 

typedef TDictMap<EBoundary> CBoundaryCondMap; 
typedef TDictMap<EPoint>    CPointLocMap; 

const CBoundaryCondMap::Table[] = {
	{ "neumann", bc_neumann}, 
	{ "dirichlet", bc_dirichlet}, 
	{ 0, bc_unknown}
}; 

const CPointLocMap::Table[] ={
	{ "mid", pl_mid}, 
	{ "grid", pl_grid}, 
	{ 0, pl_unknown}
}; 

class CCST2DCurvKernel: public CCST2DImageKernel {
public: 
	CCST2DCurvKernel(ffwf_r2r_kind forward, EBoundary boundary, EPoint point);
private: 
	class PrivatePlan: public PCST2DVectorKernel::CPlan {
	public: 
		PrivatePlan(fftwf_r2r_kind forward, std::vector<int> size); 
	private: 
		virtual void do_execute(C2DFVector *buffer) const; 
		
		vector<float> m_l1; 
		vector<float> m_l2;
		float m_step_n_weight; 

	}; 
	virtual PCST2DVectorKernel::CPlan *do_prepare(fftwf_r2r_kind fw_kind, const std::vector<int>& size);

	EBoundary m_boundary; 
	EPoint m_point; 
}; 


CCST2DCurvKernel::PrivatePlan::PrivatePlan(fftwf_r2r_kind forward, std::vector<int> size):
	PCST2DVectorKernel::CPlan( forward, size),
	m_l1(size[0]), 
	m_l2(size[1]), 
{
	double i_start = (boundary == bc_dirichlet)  ? 1 : 0; 

	double n_l1 = m_l1.size(); 
	double n_l2 = m_l2.size(); 

	if (point == pl_grid) 
		n_l1 += (boundary == bc_dirichlet) ? 1.0 : -1.0; 
		
	double i = i_start; 
	for (vector<float>::iterator l = m_l1.begin(); l != m_l1.end(); ++l, ++i) 
		*l = 2.0 * cos( (M_PI * i ) / n_l1 ) - 2.0 ;

	i = i_start; 
	for (vector<float>::iterator l = m_l2.begin(); l != m_l2.end(); ++l, ++i)
		*l = 2.0 * cos( (M_PI * i ) / n_l2 ) - 2.0 ;
}

void CCST2DCurvKernel::PrivatePlan::do_execute(C2DFVector *buffer) const
{
	assert(2 == get_size().size()); 
	
	size_t sx = get_size()[0]; 
	size_t sy = get_size()[1]; 

	vector<float>::const_iterator l2 = m_l2.begin(); 
	for (size_t y = 0; y < sy; ++y, ++l2) {
		vector<float>::const_iterator l1 = m_l1.begin(); 
		for (size_t x = 0; x < sx; ++x, ++buffer, ++l1) {
			float l = 1.0 +  m_step_n_weight  * ( *l1 + *l2 ); 
			*buffer = (l != 0.0f) ? 0.0f : *buffer / l; 
		}
}


NS_END




