//#include "phase_elast.hh"
#include <ctype.h>
#include "alberta.h"
#include "newalberta.hh"
#include "do_on_el.hh"
#include "do_elfunc_on_el.hh"
#include  "prototype_fem_elfunc.hh"
#include "fem_elfunc.hh"
#include "fem_base.hh"
#include "build_stat.hh"
#include "build_diff.hh"
#include  "est.hh"
#include "ellipt_est.hh"
#include "parab_est.hh"
#include "master.hh"
#include "graphics.hh"
#include "operator_parts.hh"
#include "elast_problem.hh"
#include "elast.hh"
#include "adapt.hh"
#if USE_NCDF
#include "dxexport.hh"
#endif
#include "periodic.h" 
#include "heat.hh"
#include "heat_problem.hh" //fuer Test-Polynome
#include "th_strain_rhs.hh"
// Konstanten >0
REAL time_est = 0.0;

struct leaf_data
{
  U_CHAR mark;              /*  needed for special refinement ????          */
};// dummy. Mal ganz weghauen.muesste gehen

static	 REAL (*sol_ex)(const REAL_D x);   			/* known temperature soln      */

/*dies dient zum Testen von elast.cc.*/

MESH *mesh;
 ADAPT_INSTAT   *adapt_elast = nil; 
 ADAPT_INSTAT   *adapt_heat = nil;

REAL ph_tw_stat(const REAL_D x) 
{
  return( 0.5		);	
}

static REAL p(REAL x)
{
  return(x*x*(1-x)*(1-x));
}

static REAL pprime(REAL x)
{
  return(2.0*x*(1.0-x)*(1.0-2.0*x));
}
 REAL p2prime(REAL x)
{
        return(12.0*x*x-12.0*x+2.0);
/*  return(2.0*x*(1-x)*(1-2*x));*/
}

  const REAL a=100.0; //  

static REAL    th_ex7(const REAL_D x)
{

  return(723.0 + a*(p2prime(x[0])*p(x[1]) +  p2prime(x[1])*p(x[0])) );
  //better with perlit-start-temperature.
}
static    REAL *grd_th_ex7(const REAL_D x, REAL t)
{
  static REAL_D grad_th = {0.0,  0.0};
//  static REAL c1=0.0;
 //       REAL alpha = 1.55E-5;
 //       if (!c1)...
    grad_th[0] = 0.0;
    grad_th[1] = 0.0;/*dummy*/
 return(grad_th);
}



static const REAL  *u_sol7(const REAL_D x, REAL_D u_x)/*Argumente u Konstrukt von interpol_d vorgegebe
n*/
{
  FUNCNAME("u_sol7");
  static REAL_D u = {0.0,  0.0};
  static REAL c1=0.0;
  const REAL alpha = 1.55E-5;
        if (!c1) {
					REAL lambda = prob_data_s11.physconstants[LAMBDA];
					REAL mu = prob_data_s11.physconstants[MU];
                c1=alpha*(3.0* lambda+ 2.0*mu)/(lambda+ 2.0*mu);
			}
        u[0]=a*pprime(x[0])*p(x[1])*c1;
        u[1]=a*pprime(x[1])*p(x[0])*c1;
        return(u);
}


static REAL u_sol7_0(const REAL_D x)
{ //does not depend on u
 return(u_sol7( x, NULL)[0]);
}
static REAL u_sol7_1(const REAL_D x)
{  //neither
 return(u_sol7( x, NULL)[1]);
}

static const REAL *gu_D_8(const REAL_D x, REAL t, REAL_D g_x)/*vorgegebene Verschiebung f Zugversuch p
rob 8*/
{
  static REAL_D g = {0.0,  0.0};
  REAL          *gx = g_x ? g_x : g;
  /*for (i=0; i<DIM_OF_WORLD; i++)*/
    gx[0] = 0.0;
    gx[1] = 0.01*x[1];

  return(gx);
}

PROB_DATA_DIFF prob_data_heat_polynomial =    
     { 
	nil, nil,        /* Diffusionsmatrix, order0 werden ueberschrieben!!*/
	   nil		//massfunc
	 , 1 		//constflag
	,{0.0, 0.077, 0.084,/*latent_heat pearl, Martensit*/ 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}, //phys. konstanten. Feld is eher f. phase gedacht  
    		/*rhs function:*/
    		nil, th_ex7, nil /*gth_D_6*/,nil /* gth_N_5*/,
    		th_ex7, grd_th_ex7,              /* th_ex, grd_th_ex */
        	};
PROB_DATA_DIFF prob_data_heat_const =    
        {
        nil, nil,        /* Diffusionsmatrix, order0 werden ueberschrieben!!*/
	   nil		//massfunc
	   , 1 		//constflag
	,{0.0, 0.077, 0.084,/*latent_heat pearl, Martensit*/    0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0
		}, //phys. konstanten. Feld is eher f. phase gedacht  
    		nil, th0, nil /*gth_D_6*/,nil /* liefert Nullen*/,
    		th0, grd_th_ex7,              /* th_ex, grd_th_ex nicht dafuer gemacht aber ok*/
        };


 void init_problem_zugversuch(const PROB_DATA_DIFF* (* prob_datas_elast_ptr)[DIM*DIM])
{
  FUNCNAME("init_problem_zugversuch");
	int i =0,j=0;
 	init_problem_standard_steel();
//	prob_data_s11.g_Dirich = ;
// alle rhs 0, kommt alles inne allgem. RHS
 	prob_data_s11.g_Neumann = nil;
(* prob_datas_elast_ptr)[i++]	= (const PROB_DATA_DIFF *) &prob_data_s11;
(* prob_datas_elast_ptr)[i++]	= (const PROB_DATA_DIFF *) &prob_data_s12;
if (DIM==3)
 	(* prob_datas_elast_ptr)[i++]	= (const PROB_DATA_DIFF *)&prob_data_s13;
  (* prob_datas_elast_ptr)[i++]	= (const PROB_DATA_DIFF *) &prob_data_s21;
  (* prob_datas_elast_ptr)[i++]	= (const PROB_DATA_DIFF *) &prob_data_s22;
  if (DIM==3)
  {
 	(* prob_datas_elast_ptr)[i++]	= (const PROB_DATA_DIFF *)&prob_data_s23;
 	(* prob_datas_elast_ptr)[i++]	= (const PROB_DATA_DIFF *)&prob_data_s31;
 	(* prob_datas_elast_ptr)[i++]	= (const PROB_DATA_DIFF *)&prob_data_s32;
 	(* prob_datas_elast_ptr)[i++]	= (const PROB_DATA_DIFF *)&prob_data_s33;
  }
	for (i=0; i<DIM; i++)
		{
		for (j=0;j<DIM;j++)//  Spalte
			{
			MSG("phase %d %d diffcoeff[0][0]: %f, [0][1]: %f\n",i,j, (* prob_datas_elast_ptr)[i*DIM+j]->diffmat->getcomp(0,0),
					(* prob_datas_elast_ptr)[i*DIM+j]->diffmat->getcomp(0,1) );
			}
		}//nur fuer konstante Koeffs
}


static void init_problem_polynomial(const PROB_DATA_DIFF* (* prob_datas_elast_ptr)[DIM*DIM])
{
  FUNCNAME("init_problem_polynomial");
	int i=0,j=0;
 	init_problem_standard_steel();
 	prob_data_s11.g_Dirich = nil;
 	prob_data_s11.g_Neumann = nil;
	prob_data_s22.g_Dirich = nil;
 	prob_data_s22.g_Neumann = nil;
	prob_data_s11.sol_0	=prob_data_s11.sol_ex	= u_sol7_0;	//war original andersrum!
  prob_data_s22.sol_0	=prob_data_s22.sol_ex	= u_sol7_1;
if (DIM==3)
  prob_data_s33.sol_0	=prob_data_s33.sol_ex	= 0;

// alle rhs 0, kommt alles inne RHS
(* prob_datas_elast_ptr)[i++]	= (const PROB_DATA_DIFF *) &prob_data_s11;
(* prob_datas_elast_ptr)[i++]	= (const PROB_DATA_DIFF *) &prob_data_s12;
if (DIM==3)
 	(* prob_datas_elast_ptr)[i++]	= (const PROB_DATA_DIFF *)&prob_data_s13;
  (* prob_datas_elast_ptr)[i++]	= (const PROB_DATA_DIFF *) &prob_data_s21;
  (* prob_datas_elast_ptr)[i++]	= (const PROB_DATA_DIFF *) &prob_data_s22;
  if (DIM==3)
  {
 	(* prob_datas_elast_ptr)[i++]	= (const PROB_DATA_DIFF *)&prob_data_s23;
 	(* prob_datas_elast_ptr)[i++]	= (const PROB_DATA_DIFF *)&prob_data_s31;
 	(* prob_datas_elast_ptr)[i++]	= (const PROB_DATA_DIFF *)&prob_data_s32;
 	(* prob_datas_elast_ptr)[i++]	= (const PROB_DATA_DIFF *)&prob_data_s33;
  }
	for (i=0; i<DIM; i++)
		{
		for (j=0;j<DIM;j++)//  Spalte
			{
			MSG("elast %d %d diffcoeff[0][0]: %f, [0][1]: %f\n",i,j, (* prob_datas_elast_ptr)[i*DIM+j]->diffmat->getcomp(0,0),
					(* prob_datas_elast_ptr)[i*DIM+j]->diffmat->getcomp(0,1) );
			}
		}//nur fuer konstante Koeffs
}



static REAL get_time_est(MESH *mesh, ADAPT_INSTAT *adapt)
{REAL a=time_est;
  time_est=0.0; //irgendwie musser auf 0. normal in estimate, aber da hab ich ja nir den von
  				//Phase
  return(a);
}
//Pfusch... tschuldigung!
static void (*solve_ptr)(MESH *mesh);
static void solve(MESH *mesh)
{
  FUNCNAME("solve");
  TEST_EXIT(adapt_heat,"no adapt_heat\n");
  TEST_EXIT(adapt_elast,"no adapt_elast\n");
  

  if (adapt_heat->adapt_space->solve)
    adapt_heat->adapt_space->solve(mesh);

  if (solve_ptr)
    (*solve_ptr)(mesh);

  return;
}
static void (*solve0_ptr)(MESH *mesh);
static void solve0(MESH *mesh)
{
  FUNCNAME("solve0");
  TEST_EXIT(adapt_heat,"no adapt_heat\n");
  TEST_EXIT(adapt_elast,"no adapt_elast\n");
  

  if (adapt_heat->adapt_initial->solve)
    adapt_heat->adapt_initial->solve(mesh);

  if (solve0_ptr)
    (*solve0_ptr)(mesh);

  return;
}

// graphik anstatt close_p_timestep aus phase.cc +
static void close_timestep(MESH *mesh, ADAPT_INSTAT *adapt)
{
  FUNCNAME("close_p_timestep");
	const DOF_REAL_VEC *const * const deformation = get_deformation();
//	const DOF_REAL_VEC *const  stress = get_stress();
	const DOF_REAL_VEC *const  temperature = get_temperature();
 
 static const DOF_REAL_VEC * *  vecs_for_save=MEM_CALLOC(DIM+4,const DOF_REAL_VEC *);
  	static const DOF_REAL_VEC  * const * const  uh_estimate	= get_uh_estimate();
   static int  nwrite=-5, iwrite=0, count=0, i;
   static char path[128] = "";
   static char filename[128] = "";
 static  DOF_REAL_VEC * exact_sol_vec = get_dof_real_vec("exact_sol_vec",deformation[0]->fe_space) ;
   
   interpol( sol_ex, exact_sol_vec); // Scheisse so
   calc_stress();
    if (nwrite < 0) 
	{
      nwrite=0;
      GET_PARAMETER(1, "write data", "%d", &nwrite);
      GET_PARAMETER(1, "data path", "%s", path);
    }
    
#if USE_NCDF
    if (nwrite>0) 
	{
      if ((iwrite % nwrite) == 0) 
		{
		if (vecs_for_save[0]== nil)
			{for (i=0; i<DIM; ++i )
				{vecs_for_save[i] = deformation[i];
				}
			for (i=0; i<DIM; ++i )
				{vecs_for_save[i + DIM] = uh_estimate[i];
				}
			}
		sprintf(filename,"%s%s%05d",path,"results_test_",count++ );
	 	if ( (i = dxexport(mesh, filename,  adapt->time, vecs_for_save,  2*DIM )))
					MSG("dxexport failed! Err Nr: %d\n",i);
		}
	}
 #endif
		graphics(mesh, nil,  deformation[0],  	nil,	2);
		graphics(mesh,  adapt_elast->adapt_space->get_el_est/*( EL *el)*/,  nil,
               nil,    1);
//geht, debuggen		graphics(mesh,  get_plot_subdom/*( EL *el)*/,  nil,
//                nil,    1);
		graphics(mesh, nil, deformation[1]/*exact_sol_vec*/,  	nil,	3);
		graphics(mesh, nil, get_stress(),  	nil,	4);
//		graphics(mesh, nil, temperature,  	nil,	4);



    static FILE *file_ph00=nil,/* *file_ph01=nil;
    static FILE *file_ph10=nil,*/ *file_ph11=nil;
   
    if (!file_ph00)
      {      
      if (!(file_ph00  = fopen("ph00.xg", "w"))){	perror("ph00.xg");}
//      if (!(file_ph01  = fopen("ph01.xg", "w"))){	perror("ph01.xg");}
//      if (!(file_ph10  = fopen("ph10.xg", "w"))){	perror("ph10.xg");}
      if (!(file_ph11  = fopen("ph11.xg", "w"))){	perror("ph11.xg");}
	}
    fprintf(file_ph00, "%.6le %.6le\n",get_time(),dof_max(deformation[0])
	    );
    fprintf(file_ph11, "%.6le %.6le\n",get_time(),dof_max(deformation[1]));
  fflush(nil);
  return;
}


static void init_dof_admin(MESH *mesh)
{
  init_elast_dof_admin(mesh);
  init_heat_dof_admin(mesh);
  init_subdom_dof_admin(mesh);
  init_err_dof_admin(mesh);
#if USE_NCDF
  init_export_dof_admin(mesh);
#endif
  return;
}
#define N_PROB 2
static int  init_problem(  const fem_elfunc** rhs_el_func_ptr
		,const PROB_DATA_DIFF* (* prob_datas_elast_ptr)[DIM*DIM]
		,const PROB_DATA_DIFF **return_prob_data_heat)
{
  FUNCNAME("init_problem");
//  char  filename[156],filename2[156];
  int    pn = 0,  rhs_nr =100;
  GET_PARAMETER(2, "problem number", "%d", &pn);
  TEST_EXIT(pn >= 0 && pn < N_PROB,"problem %d not defined\n", pn);
  switch (pn)
  {
  case 0:		
	init_problem_zugversuch(prob_datas_elast_ptr);
	*return_prob_data_heat= (const PROB_DATA_DIFF *)&prob_data_heat_const;
    break;
  case 1:
	init_problem_polynomial(prob_datas_elast_ptr);
	*return_prob_data_heat= (const PROB_DATA_DIFF *)&prob_data_heat_polynomial;
    break;
  default:
	MSG("No such problem");
	return(1);
  }
GET_PARAMETER(1, "common elast rhs", "%d", &rhs_nr);
  switch (rhs_nr)
  {
  case 0: 
  	MSG("You will be using *prob_datas_elast_ptr rhs (rhs_nr=%d)\n", rhs_nr);
  	*rhs_el_func_ptr = nil;		
    break;
  case 1:
  	MSG("You will be adding th_strain_rhs to rhs(rhs_nr=%d)\n", rhs_nr);
	*rhs_el_func_ptr = new th_strain_rhs(get_temperature(), *prob_datas_elast_ptr, -1 /*quad_degree*/);
    break;
  default:
	ERROR_EXIT("No such problem");
  }
  return(0);
}

int main(int argc, char **argv)
{
  FUNCNAME("main");
  int    k;
  /*    char        geom[128] = "300x300+0+0";*/
  const fem_elfunc*  rhs_el_func;
  const  PROB_DATA_DIFF*  (prob_datas_elast)[DIM_OF_WORLD*DIM_OF_WORLD];
  const  PROB_DATA_DIFF* prob_data_heat= nil;
  char  filename[156];
  FLAGS periodic = 0U;
/****************************************************************************/
/*  first of all, init parameters of the init file                          */
/****************************************************************************/
MSG("started\n");
  init_parameters(0, "elast_test.dat");
  for (k = 1; k+1 < argc; k += 2)
    ADD_PARAMETER(4, argv[k], argv[k+1]);
  
/****************************************************************************/
/*  get a mesh, and read the macro triangulation from file                  */
/****************************************************************************/

  GET_PARAMETER(1, "macro file name", "%s", filename);
  GET_PARAMETER(2, "periodic", "%d", &periodic);

  MACRO_DATA *data = read_macro(filename);

   if (periodic) 	mesh = GET_MESH(DIM, "ALBERTA mesh", data, NULL,init_wall_trafos);
	else 				mesh = GET_MESH(DIM, "ALBERTA mesh", data, NULL, NULL );
  free_macro_data(data);

  init_dof_admin(mesh);
 

  init_problem( &rhs_el_func, &prob_datas_elast, &prob_data_heat); 
  sol_ex= prob_datas_elast[0]->sol_ex; 			/* known temperature soln      */

 adapt_heat = get_adapt_instat(DIM,"adapt_heat", "adapt_heat",
				1, adapt_heat);

  init_heat(adapt_heat, prob_data_heat);

 
/****************************************************************************/
/*  init adapt structure and start adaptive method                          */
/****************************************************************************/
  adapt_elast = get_adapt_instat(DIM,"adapt_elast", "adapt_master",
				 1, adapt_elast);
  init_elast(adapt_elast, rhs_el_func, &prob_datas_elast);
  init_master(mesh, adapt_elast);
 // init_phase(adapt_, &prob_datas_)
  adapt_elast->close_timestep = close_timestep;
  adapt_elast->adapt_space->marking     = okmarking;
  adapt_elast->set_time  = set_time;	//in master.cc
  adapt_elast->get_time_est   = get_time_est; // time_est: globale var,  steht summe 
  //			aller return-werte (Teiproblem-time-ests) von parab_est::estimator drin
  solve_ptr = adapt_elast->adapt_space->solve;	
  adapt_elast->adapt_space->solve  = solve;	
  solve0_ptr = adapt_elast->adapt_initial->solve;	
  adapt_elast->adapt_initial->solve  = solve0;	

 
#if LOCALDEBUG
	MSG("mesh->parametric: %h\n",
		mesh->parametric);
#endif
  adapt_method_instat(mesh, adapt_elast);

  WAIT_REALLY;
  return(0);
}

