/* -*- 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/>.
 *
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>


#include <mia/core.hh>
#include <mia/core/errormacro.hh>
#include <mia/3d/vfio.hh>


NS_MIA_USE; 
using namespace std; 

const SProgramDescription g_description = {
        {pdi_group, "Miscellaneous programs"}, 
	{pdi_short, "3D vector field creation."}, 
	{pdi_description, "This program is used to create a file containing a 3D vector field."}, 
	{pdi_example_descr, "Create a field of type 2 with fun 3.0 and size 128x128x128."}, 
	{pdi_example_code, "-o field.v -s '<128,128,128>' -t 2 -f 3.0"}
}; 

typedef unsigned int uint32; 

void FillField0(C3DFVectorfield& Field, const C3DBounds& Size, float /*fun*/)
{
	auto ri = Field.begin();
	float xdef = Size.x / 32.0;
	float ydef = Size.y / 32.0;
	float zdef = Size.z / 32.0;
	float xHelp = M_PI/(Size.x-1);
	float yHelp = M_PI/(Size.y-1);
	float zHelp = M_PI/(Size.z-1);
	
	for (uint32 z = 0; z < Size.z; z++){
		float zfactor = sin(zHelp * z);
		for (uint32 y = 0; y < Size.y; y++){
			float yfactor=sin(yHelp * y) * zfactor;
			for (uint32 x = 0; x < Size.x; x++,ri++){
				float factor = sin(xHelp * x) * yfactor;
				(*ri).x = factor * xdef;     ;
				(*ri).y = factor * ydef;
				(*ri).z = factor * zdef;
			}
		}
	}
}

void FillField1(C3DFVectorfield& Field,const C3DBounds& Size, float fun)
{
	float xfun = fun * Size.x / 32.0; 
	float yfun = fun * Size.y / 32.0; 
	float zfun = fun * Size.z / 32.0; 

	auto  ri = Field.begin();
	float xHelp = M_PI/(Size.x-1);
	float yHelp = M_PI/(Size.y-1);
	float zHelp = M_PI/(Size.z-1);
	
	for (uint32 z = 0; z < Size.z; z++){
		float zfactor = sin(zHelp * z) * sin(fun * zHelp * z);
		for (uint32 y = 0; y < Size.y; y++){
			float yfactor= sin (yHelp * y) * sin(fun * yHelp * y ) * zfactor ;
			for (uint32 x = 0; x < Size.x; x++,ri++){
				float factor = (sin(xHelp * x) * sin(fun * xHelp * x)) * yfactor;
				ri->x = factor * xfun;     
				ri->y = factor * yfun;
				ri->z = factor * zfun;
			}
		}
	}
}

void FillField2(C3DFVectorfield& Field,const C3DBounds& Size,float fun)
{
	auto  ri = Field.begin();
	float xdef = Size.x / 4.0;
	float ydef = Size.y / 4.0;
	float zdef = Size.z / 4.0;
	float xHelp = 2 * M_PI/(Size.x-1);
	float yHelp = 2 * M_PI/(Size.y-1);
	float zHelp = 2 * M_PI/(Size.z-1);
	
	for (uint32 z = 0; z < Size.z; z++){
		float zfactor = sin(zHelp * z) * cos(fun * zHelp *z);
		for (uint32 y = 0; y < Size.y; y++){
			float yfactor=(sin( yHelp *y)* cos(fun* yHelp *y) )* zfactor ;
			for (uint32 x = 0; x < Size.x; x++,ri++){
				float factor = (sin( xHelp *x) * cos(fun * xHelp * x)) * yfactor;
				(*ri).x = factor * xdef;     ;
				(*ri).y = factor * ydef;
				(*ri).z = factor * zdef;
			}
		}
	}
}

int do_main(int argc, char *argv[])
{

	C3DBounds Size(64,64,64);
	
	short fieldtype = 0;
	float funfactor = 1.0;
	
	string out_filename;

	
	CCmdOptionList options(g_description);

	options.add(make_opt( out_filename, "out-file", 'o', "output file for the vector field", 
			      CCmdOptionFlags::required_output, &C3DVFIOPluginHandler::instance()));
	options.add(make_opt( Size, "size", 's', "size of the vector field"));
	options.add(make_opt( fieldtype, "ftype", 'y', "vector field type")); 
	options.add(make_opt( funfactor, "fun", 'f', "just some parameter to vary the field ;-)")); 
	
	if (options.parse(argc, argv) != CCmdOptionList::hr_no)
		return EXIT_SUCCESS; 


	C3DFVectorfield Field(Size);
	switch (fieldtype){
	case 0:FillField0(Field,Size,funfactor);break;
	default:FillField1(Field,Size,funfactor);break;
	}
	
	if (!C3DVFIOPluginHandler::instance().save(out_filename, Field)){
		throw create_exception<runtime_error>( "Unable to save result vector field to ", out_filename, "\n"); 
	}
	return EXIT_SUCCESS;	

}


#include <mia/internal/main.hh>
MIA_MAIN(do_main)
