#include "UserData.h"
#include "parse.h"

void freeUserData(UserData data){
	if(data!=NULL){
		if(data->trmix!=NULL){
			delete data->trmix;
			printf("Transport Deleted!\n");

		}
		if(data->gas!=NULL){
			delete data->gas;
			printf("Gas Deleted!\n");

		}
		if(data->adaptiveGrid){
			if(data->grid->xOld!=NULL){
				delete[] data->grid->xOld;
				printf("old grid array Deleted!\n");
			}
			if(data->grid->x!=NULL){
				delete[] data->grid->x;
				printf("current grid array Deleted!\n");
			}
			if(data->grid!=NULL){
				free(data->grid);
				printf("grid object Freed!\n");
			}
		}
		else{
			if(data->uniformGrid!=NULL){
				delete[] data->uniformGrid;
				printf("uniformGrid deleted!\n");
			}
		}
		if(data->innerMassFractions!=NULL){
			delete[] data->innerMassFractions;
			printf("innerMassFractions array Deleted!\n");
		}
		if(data->output!=NULL){
  			fclose(data->output);
			printf("Output File Cleared from Memory!\n");
		}
		if(data->gridOutput!=NULL){
  			fclose(data->gridOutput);
			printf("Grid Output File Cleared from Memory!\n");
		}
		//if(data->ratesOutput!=NULL){
  		//	fclose(data->ratesOutput);
		//	printf("Rates Output File Cleared from Memory!\n");
		//}
		if(data->globalOutput!=NULL){
  			fclose(data->globalOutput);
			printf("Global Output File Cleared from Memory!\n");
		}
		if(data->timescaleOutput!=NULL){
  			fclose(data->timescaleOutput);
			printf("Characteristic Timescale Output File Cleared from Memory!\n");
		}
		//if(data->rxnROPOutput!=NULL){
  		//	fclose(data->rxnROPOutput);
		//	printf("Reactions Rate of Progress Output File Cleared from Memory!\n");
		//}
		//if(data->spROPOutput!=NULL){
  		//	fclose(data->spROPOutput);
		//	printf("Species Rate of Production Output File Cleared from Memory!\n");
		//}
	}
  	free(data);             /* Free the user data */
	printf("\n\n");
}

UserData allocateUserData(FILE *input){

	UserData data;
  	data = (UserData) malloc(sizeof *data);

	if(!data){
		printf("Allocation Failed!\n");
		return(NULL);
	}
	setSaneDefaults(data);

	int ier;

	ier=parseNumber<size_t>(input, "basePts"	, MAXBUFLEN, &data->npts);
	if(ier==-1 || data->npts<=0){
		printf("Enter non-zero basePts!\n");
		return(NULL);
	}

	ier=parseNumber<double>(input, "domainLength", MAXBUFLEN, &data->domainLength);
	if(ier==-1 || data->domainLength<=0.0e0){
		printf("domainLength error!\n");
		return(NULL);
	}

	ier=parseNumber<double>(input, "Rd", MAXBUFLEN, &data->Rd);
	if(ier==-1 || data->Rd<=0.0e0){
		printf("Rd error!\n");
		return(NULL);
	}

	ier=parseNumber<int>(input, "constantPressure"	, MAXBUFLEN, &data->constantPressure);
	if(ier==-1 || (data->constantPressure!=0 && data->constantPressure!=1)){
		printf("constantPressure error!\n");
		return(NULL);
	}

	ier=parseNumber<int>(input, "problemType"	, MAXBUFLEN, &data->problemType);
	if(ier==-1 || (data->problemType!=0 
		       && data->problemType!=1
			&& data->problemType!=2
			&& data->problemType!=3)){
		printf("include valid problemType!\n");
		printf("0: premixed combustion with NO equilibrated ignition kernel\n");
		printf("1: premixed combustion WITH equilibrated ignition kernel\n");
		printf("2: arbitrary initial conditions\n");
		printf("3: Restart\n");
		return(NULL);
	}

	ier=parseNumber<int>(input, "quasiSteady"	, MAXBUFLEN, &data->quasiSteady);
	if(ier==-1 || (data->quasiSteady!=0 
		       && data->quasiSteady!=1)){
		printf("include valid quasiSteady!\n");
		printf("0: The droplet surface recedes and the droplet losses mass\n");
		printf("1: The droplet surface does not move and the droplet mass is constant\n");
		return(NULL);
	}

	ier=parseNumber<double>(input, "dPdt"		, MAXBUFLEN, &data->dPdt);

	ier=parseNumber<double>(input, "Rg"		, MAXBUFLEN, &data->Rg);
	if(data->Rg < 0.0){
		printf("Rg must be greater than 0");
		return(NULL);
	}

	ier=parseNumber<int>   (input, "reflectProblem"	, MAXBUFLEN, &data->reflectProblem);
	if(data->reflectProblem!=0 && data->reflectProblem!=1){
		printf("Invalid entry for reflectProblem! Can be only 1 or 0.\n");
		return(NULL);
	}
	
	ier=parseNumber<double>(input, "mdot"		, MAXBUFLEN, &data->mdot);
	
	ier=parseNumber<double>(input, "initialTemperature", MAXBUFLEN,
			&data->initialTemperature);
	if(ier==-1 || data->initialTemperature<=0.0e0){
		printf("Enter positive initialTemperature in K!\n");
		return(NULL);
	}


	ier=parseNumber<double>(input, "initialPressure", MAXBUFLEN,
			&data->initialPressure);
	if(ier==-1 || data->initialTemperature<=0.0e0){
		printf("Enter positive initialPressure in atm!\n");
		return(NULL);
	}

	ier=parseNumber<int>   (input, "metric"	, MAXBUFLEN, &data->metric);
	if(data->metric!=0 && data->metric!=1 && data->metric!=2){
		printf("Invalid entry for metric!\n");
		printf("0: Cartesian\n");
		printf("1: Cylindrical\n");
		printf("2: Spherical\n");
		return(NULL);
	}

	ier=parseNumber<double>(input, "QDot", MAXBUFLEN, &data->maxQDot);
	if(ier==-1 && data->problemType==0){
		printf("Need to specify QDot for problemType 0!\n");
		return(NULL);
	}

	ier=parseNumber<double>(input, "kernelSize", MAXBUFLEN, &data->kernelSize);
	if(ier==-1 && data->problemType==0){
		printf("Need to specify kernelSize for problemType 0!\n");
		return(NULL);
	}

	ier=parseNumber<double>(input, "ignTime", MAXBUFLEN, &data->ignTime);
	if(ier==-1 && data->problemType==0){
		printf("Need to specify ignTime for problemType 0!\n");
		return(NULL);
	}

	ier=parseNumber<double>(input, "mixingWidth", MAXBUFLEN,
			&data->mixingWidth);
	if(ier==-1){
		printf("Need to specify mixingWidth!\n");
		return(NULL);
	}
	
	ier=parseNumber<double>(input, "shift", MAXBUFLEN, &data->shift);

	ier=parseNumber<double>(input, "firstRadius", MAXBUFLEN, &data->firstRadius);
	
	ier=parseNumber<double>(input, "wallTemperature", MAXBUFLEN, &data->wallTemperature);

	ier=parseNumber<int>   (input, "dirichletInner"	, MAXBUFLEN,
			&data->dirichletInner);
	if(data->dirichletInner!=0 && data->dirichletInner!=1){
		printf("dirichletInner can either be 0 or 1!\n");
		return(NULL);
	}

	ier=parseNumber<int>   (input, "dirichletOuter"	, MAXBUFLEN,
			&data->dirichletOuter);
	if(data->dirichletOuter!=0 && data->dirichletOuter!=1){
		printf("dirichletOuter can either be 0 or 1!\n");
		return(NULL);
	}

	ier=parseNumber<int>   (input, "adaptiveGrid"	, MAXBUFLEN,
			&data->adaptiveGrid);
	if(ier==-1 || (data->adaptiveGrid!=0 && data->adaptiveGrid!=1)){
		printf("specify adaptiveGrid as 0 or 1!\n");
		return(NULL);
	}

	ier=parseNumber<int>   (input, "moveGrid"	, MAXBUFLEN,
			&data->moveGrid);
	if(ier==-1 || (data->moveGrid!=0 && data->moveGrid!=1)){
		printf("specify moveGrid as 0 or 1!\n");
		return(NULL);
	}

	ier=parseNumber<double>   (input, "isotherm"	, MAXBUFLEN,
			&data->isotherm);
	if(ier==-1){
		printf("specify temperature of isotherm!\n");
		return(NULL);
	}

	ier=parseNumber<double>(input, "gridOffset", MAXBUFLEN, &data->gridOffset);

	ier=parseNumber<int>   (input, "nSaves"	, MAXBUFLEN, &data->nSaves);
	if(data->nSaves<0 ){
		printf("nSaves must be greater than 0!\n");
		return(NULL);
	}
	
	ier=parseNumber<int>   (input, "writeRates"	, MAXBUFLEN,
			&data->writeRates);
	if(data->writeRates!=0 && data->writeRates!=1){
		printf("writeRates must either be 0 or 1!\n");
		return(NULL);
	}

	ier=parseNumber<int>   (input, "writeEveryRegrid", MAXBUFLEN,
			&data->writeEveryRegrid);
	if(data->writeEveryRegrid!=0 && data->writeEveryRegrid!=1){
		printf("writeEveryRegrid must either be 0 or 1!\n");
		return(NULL);
	}

	ier=parseNumber<int>   (input, "setConstraints"	, MAXBUFLEN,
			&data->setConstraints);
	if(data->setConstraints!=0 && data->setConstraints!=1){
		printf("setConstraints must either be 0 or 1!\n");
		return(NULL);
	}

	ier=parseNumber<int>   (input, "suppressAlg"	, MAXBUFLEN,
			&data->suppressAlg);
	if(data->suppressAlg!=0 && data->suppressAlg!=1){
		printf("suppressAlg must either be 0 or 1!\n");
		return(NULL);
	}

	ier=parseNumber<int>   (input, "dryRun"	, MAXBUFLEN,
			&data->dryRun);
	if(data->dryRun!=0 && data->dryRun!=1){
		printf("dryRun must either be 0 or 1!\n");
		return(NULL);
	}

	ier=parseNumber<double>   (input, "finalTime"	, MAXBUFLEN,
			&data->finalTime);

	ier=parseNumber<double>   (input, "relativeTolerance"	, MAXBUFLEN,
			&data->relativeTolerance);
	ier=parseNumber<double>   (input, "radiusTolerance"	, MAXBUFLEN,
			&data->radiusTolerance);
	ier=parseNumber<double>   (input, "temperatureTolerance", MAXBUFLEN,
			&data->temperatureTolerance);
	ier=parseNumber<double>   (input, "pressureTolerance", MAXBUFLEN,
			&data->pressureTolerance);
	ier=parseNumber<double>   (input, "massFractionTolerance", MAXBUFLEN,
			&data->massFractionTolerance);
	ier=parseNumber<double>   (input, "bathGasTolerance", MAXBUFLEN,
			&data->bathGasTolerance);
	ier=parseNumber<double>   (input, "MdotTolerance", MAXBUFLEN,
			&data->MdotTolerance);

	char chem[MAXBUFLEN],mix[MAXBUFLEN],tran[MAXBUFLEN];

	ier=parseNumber<char>(input, "chemistryFile"	, MAXBUFLEN, chem);
	if(ier==-1){
		printf("Enter chemistryFile!\n");
		return(NULL);
	}else{
		try{
			data->gas = new Cantera::IdealGasMix(chem);
			data->nsp=data->gas->nSpecies(); //assign no: of species

    		} catch (Cantera::CanteraError& err) {
		    printf("Error:\n");
		    printf("%s\n",err.what());
		    return(NULL);
    		}
	}

	ier=parseNumber<char>(input, "transportModel", MAXBUFLEN, tran);
	if(ier==-1){
		printf("Enter transportModel!\n");
		return(NULL);
	}else{
		try{
  			data->trmix = Cantera::newTransportMgr(tran, data->gas);
		}catch (Cantera::CanteraError& err) {
			printf("Error:\n");
			printf("%s\n",err.what());
			return(NULL);
    		}
	}

	ier=parseNumber<char>(input, "mixtureComposition", MAXBUFLEN, mix);
	if(ier==-1){
		printf("Enter mixtureComposition!\n");
		return(NULL);
	}else{
		if(data->gas!=NULL){
			try{
				data->gas->setState_TPX(data->initialTemperature,
							data->initialPressure*Cantera::OneAtm,
							mix);
			}catch (Cantera::CanteraError& err) {
		    		printf("Error:\n");
		    		printf("%s\n",err.what());
		    		return(NULL);
    			}
		}
	}

	//ier=parseNumber<char>(input, "dropletComposition", MAXBUFLEN, data->dropSpec);
	//if(ier==-1){
	//	printf("Enter composition of droplet!\n");
	//	return(NULL);
	//}

    ier=parseDrop(input,"dropletComposition",data->dropSpec,data->dropMole,MAXBUFLEN);
    ier=parseDrop(input,"dropletDensity",data->dropSpec,data->dropDens,MAXBUFLEN);


	ier=parseNumber<int>   (input, "nThreads", MAXBUFLEN, &data->nThreads);
	if(data->nThreads<0 ){
		printf("nThreads must be greater than 0!\n");
		return(NULL);
	}
    

	ier=parseNumber<double>(input, "PCAD", MAXBUFLEN, &data->PCAD);
    ier=parseNumber<double>(input,"RGTC", MAXBUFLEN, &data->RGTC);
    ier=parseNumber<int>(input,"JJRG", MAXBUFLEN, &data->JJRG);
    ier=parseNumber<double>(input,"deltaT", MAXBUFLEN, &data->deltaT);
	
    data->nr=0;
	//data->np=data->nr+1;
	data->nt=data->nr+1;
	data->ny=data->nt+1;
	data->np=data->ny+data->nsp;
	data->nm=data->np+1;
				
	data->nvar=data->nsp+4;		 //assign no: of variables (R,T,P,Mdot,nsp species)


    double MW[2]; 
    for(int i=0;i<=1;i++){
        int speciesIndex = data->gas->speciesIndex(data->dropSpec[i]);
        double weight = data->gas->molecularWeight(speciesIndex);
        MW[i]= weight;
    }

    double massSum = 0.0; 
    for(int i=0;i<=1;i++){
        massSum = massSum + data->dropMole[i] * MW[i];
    }
    data->dropRho = 0.0;
    for(int i=0;i<=1;i++){
        data->dropMassFrac[i] = data->dropMole[i]*MW[i]/massSum;
        data->dropRho = data->dropRho + data->dropMassFrac[i]*data->dropDens[i];
    }
    printf("Density of droplet is: %.3f [kg/m^3]\n",data->dropRho);
	//Mass of droplet
	//data->massDrop=1.0/3.0*pow(data->Rd,3)*997.0; //TODO: The density of the droplet should be a user input
    //data->massDrop=1.0/3.0*pow(data->Rd,3)*684.00; //TODO: The density of the droplet(n-heptane) should be a user input
    data->massDrop=1.0/3.0*pow(data->Rd,3)*data->dropRho;

	if(!data->adaptiveGrid){
		data->uniformGrid = new double [data->npts];
		data->neq=data->nvar*data->npts;
	}
	else{
		data->grid=(UserGrid) malloc(sizeof *data->grid);
		ier=getGridSettings(input,data->grid);
		if(ier==-1)return(NULL);

		ier=initializeGrid(data->grid);
		if(ier==-1)return(NULL);

		ier=reGrid(data->grid, data->grid->position);
		if(ier==-1)return(NULL);


//        /**************** TEST THE data->grid->xOld *******************/
//        double* ptr = data->grid->xOld ; 
//        printf("allocateUserData function is called,Start print the first 5 elements of the xOld array : \n");
//        printf("1st:%.6f, 2nd:%.6f, 3rd:%.6f, 4th:%.6f, 5th:%.6f.\n",ptr[0],ptr[1],ptr[2],ptr[3],ptr[4]);

		data->npts=data->grid->nPts;
		data->neq=data->nvar*data->npts;
	}

	data->output=fopen("output.dat","w");
	data->globalOutput=fopen("globalOutput.dat","w");
	data->gridOutput=fopen("grid.dat","w");
	data->timescaleOutput=fopen("timeScale.dat","w") ;
    data->rxnROPOutput=fopen("rxnROP.dat","w");
    data->spROPOutput=fopen("spROP.dat","w");
	//data->ratesOutput=fopen("rates.dat","w");
	
	data->innerMassFractions = new double [data->nsp];
	//data->wdot_mole = new double [data->nsp] ; 
	//data->wdot_mass = new double [data->nsp] ; 
	data->time_scale = new double [(data->npts) * (data->nsp)] ; 
	//data->MW = new double [data->nsp] ; 

	return(data);
}

void setSaneDefaults(UserData data){
	data->domainLength=1.0e-02;
	data->Rd=100.0e-06;
	data->constantPressure=1;
	data->problemType=1;
	data->quasiSteady=1;
	data->dPdt=0.0e0;
	data->reflectProblem=0;
	data->mdot=0.0;
	data->initialTemperature=300.0;
	data->initialPressure=1.0;
	data->metric=0;
	data->ignTime=1e-09;
	data->maxQDot=0.0e0;
	data->maxTemperature=300.0e0;
	data->mixingWidth=1e-04;
	data->shift=3.0e-03;
	data->firstRadius=1e-04;
	data->wallTemperature=298.0e0;
	data->dirichletInner=0;
	data->dirichletOuter=0;
	data->adaptiveGrid=0;
	data->moveGrid=0;
	data->gridOffset=0.0e0;
	data->Rg=1.0;
	data->isotherm=1000.0;
	data->nSaves=30;
	data->writeRates=0;
	data->writeEveryRegrid=0;
	data->relativeTolerance=1e-06;
	data->radiusTolerance=1e-08;
	data->temperatureTolerance=1e-06;
	data->pressureTolerance=1e-06;
	data->massFractionTolerance=1e-09;
	data->bathGasTolerance=1e-06;
	data->finalTime=1e-02;
	data->tNow=0.0e0;
	data->setConstraints=0;
	data->suppressAlg=1;
	data->regrid=0;
	data->gridDirection=1;
	data->dryRun=0;
	data->nThreads=1;

	data->flamePosition[0]=0.0e0;
	data->flamePosition[1]=0.0e0;
	data->flameTime[0]=0.0e0;
	data->flameTime[1]=0.0e0;
	data->nTimeSteps=0;
    data->PCAD=0.75;
    data->RGTC=1.0;
    data->JJRG=0;
    data->deltaT=200.0;
}

