#include <assert.h>
#include <math.h>
#include <stdio.h>

#include "growingaxonhead.h"
#include "growinggrid.h"
#include "growingaxonlist.h"
#include "growingcell.h"

CGrowing_Axon_Head::CGrowing_Axon_Head(
				       C3dpoint ipos,
				       CGrowing_Axon_Cell *ihome,
				       int idistance_to_home,
				       int ifollowing_substance_number
				       )
{
    pos=ipos;
    home=ihome;
    distance_to_home=idistance_to_home;

    assert(ifollowing_substance_number >=0); assert(ifollowing_substance_number < NR_OF_SUBSTANCES_TO_FOLLOW);
    following_substance_number=ifollowing_substance_number;

    inactive_turns=0;
    still_growing=true;
};

CGrowing_Axon_Head::~CGrowing_Axon_Head()
{
    // nothing to be done
};

bool CGrowing_Axon_Head::grow_coordinate(
					 CGrowing_Grid &grid,
					 CGrowing_Axon_List *list,
					 float relative_conc,
					 float abs_conc,
					 int x,
					 int y,
					 int z
					 )
{
    assert(x >= 0); assert(x < CUBE_SIZE);
    assert(y >= 0); assert(y < CUBE_SIZE);
    assert(z >= 0); assert(z < CUBE_SIZE);
    assert(relative_conc != -1);

    float free_adjacent = 
	( ((x < CUBE_SIZE-1) && ( ! grid.get_occupied(x+1, y  , z  ))) ? 1 : 0 ) +
	( ((x > 0          ) && ( ! grid.get_occupied(x-1, y  , z  ))) ? 1 : 0 ) +
	( ((y < CUBE_SIZE-1) && ( ! grid.get_occupied(x  , y+1, z  ))) ? 1 : 0 ) +
	( ((y > 0          ) && ( ! grid.get_occupied(x  , y-1, z  ))) ? 1 : 0 ) +
	( ((z < CUBE_SIZE-1) && ( ! grid.get_occupied(x  , y  , z+1))) ? 1 : 0 ) +
	( ((z > 0          ) && ( ! grid.get_occupied(x  , y  , z-1))) ? 1 : 0 );
    
    float tmp;


    if (
	(!grid.get_occupied( x,y,z )) && 
	( (relative_conc * free_adjacent / 5.0 * ( (tmp=1.7 - 1 / pow(2,abs_conc) ) > 1 ? 1 : tmp  )  ) > drand48() ) 
	)
	{
	    grid.set_occupied(x,y,z);
	    list->insert( 
			 new CGrowing_Axon_Head(
						C3dpoint( x,y,z ), 
						home, 
						distance_to_home+1, 
						following_substance_number
						)
			 );
	    //printf(".");
	    return true;
	};
    return false;
};



int CGrowing_Axon_Head::grow( 
			      CGrowing_Grid &grid,
			      CGrowing_Axon_List *list
			      )
{
    bool grown_this_turn=false;
    if (still_growing) {
	if ( (following_substance_number != NR_OF_SUBSTANCES_TO_FOLLOW) &&
	     ( grid.get_concentration(
				      pos.x(), pos.y(), pos.z(), 
				      home->get_substance_from_substances_to_follow(following_substance_number)
				      ) 
	       > 
	       home->get_threshold_from_substances_to_follow(following_substance_number) 
	       )
	     )
	    following_substance_number++;
	
	if (following_substance_number >= NR_OF_SUBSTANCES_TO_FOLLOW)
	    still_growing=false;
	else {
	    
	    float relative_concs[6]; //6 neighbours F,B,L,R,A,U
	    if ( pos.x() > 0           )										      
		relative_concs[0] = 
		    grid.get_concentration(
					   pos.x()-1, pos.y(), pos.z(), 
					   home->get_substance_from_substances_to_follow(following_substance_number)
					   );
	    else
		relative_concs[0] = -1;
	    if ( pos.x() < CUBE_SIZE-1 ) 
		relative_concs[1] = 
		    grid.get_concentration(
					   pos.x()+1, pos.y(), pos.z(), 
					   home->get_substance_from_substances_to_follow(following_substance_number)
					   );
	    else
		relative_concs[1] = -1;
	    if ( pos.y() > 0           ) 
		relative_concs[2] = 
		    grid.get_concentration(
					   pos.x(), pos.y()-1, pos.z(), 
					   home->get_substance_from_substances_to_follow(following_substance_number)
					   );
	    else
		relative_concs[2] = -1;
	    if ( pos.y() < CUBE_SIZE-1 ) 
		relative_concs[3] = 
		    grid.get_concentration(
					   pos.x(), pos.y()+1, pos.z(), 
					   home->get_substance_from_substances_to_follow(following_substance_number)
					   );
	    else
		relative_concs[3] = -1;
	    if ( pos.z() > 0           ) 
		relative_concs[4] = 
		    grid.get_concentration(
					   pos.x(), pos.y(), pos.z()-1, 
					   home->get_substance_from_substances_to_follow(following_substance_number)
					   );
	    else
		relative_concs[4] = -1;
	    if ( pos.z() < CUBE_SIZE-1 ) 
		relative_concs[5] = 
		    grid.get_concentration(
					   pos.x(), pos.y(), pos.z()+1, 
					   home->get_substance_from_substances_to_follow(following_substance_number)
					   );
	    else
		relative_concs[5] = -1;
	    
	    float abs_conc=0;
	    for(int i=0; i<6; i++)
		if ( relative_concs[i] > -1 )
		    abs_conc += relative_concs[i];
	    
	    float min_conc=100000; // sollte passen
	    for(int i=0; i<6; i++)
		if ( (relative_concs[i] > -1) && (relative_concs[i] < min_conc) )
		    min_conc = relative_concs[i];
	    assert(min_conc!=100000);
	    
	    for(int i=0; i<6; i++)
		if ( relative_concs[i] > -1 )
		    relative_concs[i] -= min_conc;
	    
	    for(int i=0; i<6; i++)
		if ( relative_concs[i] > -1 )
		  relative_concs[i] = pow(relative_concs[i],home->get_hairiness());
//		    relative_concs[i] = pow(relative_concs[i],);
	    
	    float sum_conc=0;
	    for(int i=0; i<6; i++)
		if ( relative_concs[i] > -1 )
		    sum_conc += relative_concs[i];

	    for(int i=0; i<6; i++)
		if ( relative_concs[i] > -1 )
		    relative_concs[i] = relative_concs[i] / sum_conc;
	    
	    
	    if ( pos.x() > 0           ) if (grow_coordinate(grid, list, relative_concs[0], abs_conc, pos.x()-1, pos.y()  , pos.z()  )) grown_this_turn = true;
	    if ( pos.x() < CUBE_SIZE-1 ) if (grow_coordinate(grid, list, relative_concs[1], abs_conc, pos.x()+1, pos.y()  , pos.z()  )) grown_this_turn = true;
	    if ( pos.y() > 0           ) if (grow_coordinate(grid, list, relative_concs[2], abs_conc, pos.x()  , pos.y()-1, pos.z()  )) grown_this_turn = true;
	    if ( pos.y() < CUBE_SIZE-1 ) if (grow_coordinate(grid, list, relative_concs[3], abs_conc, pos.x()  , pos.y()+1, pos.z()  )) grown_this_turn = true;
	    if ( pos.z() > 0           ) if (grow_coordinate(grid, list, relative_concs[4], abs_conc, pos.x()  , pos.y()  , pos.z()-1)) grown_this_turn = true;
	    if ( pos.z() < CUBE_SIZE-1 ) if (grow_coordinate(grid, list, relative_concs[5], abs_conc, pos.x()  , pos.y()  , pos.z()+1)) grown_this_turn = true;
	    
	};    
	//    	if (grown_this_turn) {
	//    	    printf(".");
	//    	};

	//    	printf("\n");
	if (!grown_this_turn) 
	    if (++inactive_turns >= home->get_max_inactive_turns() )
		still_growing=false;
    };
    
    if (grown_this_turn) return 1;  //magic number alarm, nur temporaer
    if (still_growing) return -1;
    return 0;
};


bool CGrowing_Axon_Head::get_still_growing()
{
    return still_growing; 
};

CGrowing_Axon_Cell *CGrowing_Axon_Head::get_home()
{
    return home;
};

    
int CGrowing_Axon_Head::get_home_number() 
{ 
    return home->get_number(); 
};
    
int CGrowing_Axon_Head::get_distance_to_home() 
{ 
    return distance_to_home; 
};

float CGrowing_Axon_Head::get_distance_to_position(
						   C3dpoint spos
						   ) 
{ 
    return sqrt(pow(pos.x()-spos.x(),2) + pow(pos.y()-spos.y(),2) + pow(pos.z()-spos.z(),2)); 
};