#include <psychlops.h>
#include <stdio.h>
using namespace Psychlops;	// Initially developed with Psychlops Win32 20070313a

class IndependentVariables
{
	int MaxVarNum, MaxStepNum;
	double *VariableData;
	int *VariableStepNumber;
	int conditionNumber;
	int repeatNumber;
	
	Psychlops::Matrix CondMtx;

	public:
	int trialNumber;
	
	IndependentVariables(int n, int m, int repeat): MaxVarNum(n), MaxStepNum(m){
		VariableData = (double *)malloc(sizeof(double)*(n+1)*m);
		VariableStepNumber = (int *)malloc(sizeof(double)*m);
		repeatNumber=repeat;
		trialNumber=-1;
	}
	
	virtual ~IndependentVariables(void){
		free(VariableData);
		free(VariableStepNumber);
	};
	
	virtual int set(int vnum, int arraynum, double *array){
		if(vnum<MaxVarNum+1 && arraynum<MaxStepNum+1){
				  VariableStepNumber[vnum]=arraynum;
				  for(int i=0; i<arraynum; i++){*(VariableData+(vnum+1)*MaxStepNum+i)=*(array+i);}
				  return arraynum;
		}
		else return -1;	
	};
	
	
	virtual int randomize(char* dataname=NULL){
		trialNumber=repeatNumber;
		for(int i=0; i<MaxVarNum; i++)trialNumber*=VariableStepNumber[i];
		if(trialNumber<1){return -1;}
		CondMtx.set(trialNumber, MaxVarNum);
		
		int i,j,a;
		double temp=1, temp2;

		//Make Output Matrix
		
		for(i=0; i<MaxVarNum; i++){
			for(j=0; j<i; j++){temp*=(double)VariableStepNumber[j];}
			for(j=0; j<trialNumber; j++){CondMtx(j+1,i+1)=(int)ceil(j/temp)% VariableStepNumber[i];}
			temp=1;
		}
	
		//Randomize Output Matrix
		for(i=1; i<trialNumber;i++){
			a = Psychlops::random(i-1)+1;
			for(j=1;j<MaxVarNum+1;j++){
				temp2=CondMtx(a,j); 
				CondMtx(a,j)=CondMtx(i,j); 
				CondMtx(i,j)=temp2;
			}
		}
		
		//swtich of the condition matrix save
		if(dataname){	
			std::ofstream output;
			output.open(dataname);
			output << CondMtx << std::endl;
			output.close();
		}
		
	}
	
	virtual double get(int vnum, int trial_now){
		if(vnum<MaxVarNum && (int)trial_now<trialNumber)
			return *(VariableData+(vnum+1)*MaxStepNum+getCondMtx(vnum, trial_now));
		else return -1;
	};
	
	virtual int getCondMtx(int vnum, unsigned int trial_now){
		if(vnum<MaxVarNum && (int)trial_now<trialNumber)
			return CondMtx( trial_now+1, vnum+1);
		else return -1;
	};

	virtual double getIndependent(int vnum, int stepnum){
		if(vnum<MaxVarNum && stepnum<VariableStepNumber[vnum])
			return *(VariableData+(vnum)*MaxStepNum+stepnum);
		else return -1;
	};
	
	
	virtual int getMStep(int vnum){
		if(vnum<MaxVarNum)return VariableStepNumber[vnum];
			else return -1;
	};
}; 

class GaborImage {
private:
public:
	virtual void draw(Image &area,  double ori, double freq, double phase, double Lmean, double *contrast) {
		int center_x = area.getCenter().getX(), center_y = area.getCenter().getY();
		int width = area.getWidth()/2, height = area.getHeight()/2;
		Color tmpcol;
		double sigma = (width)/3;
		double SINORI, COSORI, _x, gr, ga, ph, R, G, B;
		

		COSORI=cos(-ori*PI/180);
		SINORI=sin(-ori*PI/180);
		
		ph=phase*PI/180;
		
		for(int y=-height; y<height; y++) {
			for(int x=-width; x<width; x++) {
				_x=	COSORI * x - SINORI * y;
				ga=exp(-0.5*(pow(x/(sigma),2))-0.5*(pow(y/(sigma), 2)));
				gr=(1+cos(2*PI*freq*_x+ph))*0.5;
				R=Lmean+Lmean*contrast[0]*(gr*ga);
				G=Lmean+Lmean*contrast[1]*(gr*ga);
				B=Lmean+Lmean*contrast[2]*(gr*ga);
				tmpcol.set(R, G, B);
				area.pix(x+width,y+height,tmpcol); 
			}
		}
	}

	virtual void draw(Image &area,  double ori, double freq, double phase, double Lmean, double contrast) {
		int center_x = area.getCenter().getX(), center_y = area.getCenter().getY();
		int width = area.getWidth()/2, height = area.getHeight()/2;
		Color tmpcol;
		double sigma = (width)/3;
		
		double SINORI, COSORI, _y, gr, ga, ph;
		
		COSORI=cos(ori*PI/180);
		SINORI=sin(ori*PI/180);
		
		ph=phase*PI/180;
		
		for(int y=-height; y<height; y++) {
			for(int x=-width; x<width; x++) {
				_y=	SINORI * x + COSORI * y;
				ga=exp(-0.5*(pow(x/(sigma),2))-0.5*(pow(y/(sigma), 2)));
				gr=(1+cos(2*PI*freq*_y+ph))*0.5;
				tmpcol.set(Lmean+Lmean*contrast*(gr*ga));
				area.pix(x+width,y+height,tmpcol);
			}
		}
	}
};

void psychlops_main() {

	Canvas sampleA(Canvas::fullscreen);
	IndependentVariables invar(2,5,5);

	double gaborOrientation[5]={0,1,2,3,4}; //Gaborの方位
	double duration[3]={100,250,500}; //刺激の提示時間
	
	invar.set(0, 5, gaborOrientation);
	invar.set(1, 3, duration);
	invar.randomize("ConditionMatrix.dat");

	GaborImage gaborIMG;
	Psychlops::Image gIMG[10];
	double gIMGsize=100, ori;
	for(int i=0; i<10; i++){
		if(i<5){ ori=90.0-pow(2.0, i);}
			else{ ori=90.0+pow(2.0,i-4);}
		gIMG[i].set(gIMGsize, gIMGsize);
		gaborIMG.draw(gIMG[i], 60.0, 1.0/30.0, 0.0, 0.5, 0.2);
		gIMG[i].centering();
	}


	const int trialNum=2*5*1;
	int answer[trialNum], orientationCondition[trialNum], durationCondition[trialNum], ans;
	int ori_now, dura_now;
	int direction;
	char trial_header[64];

	for(int trial=0; trial<trialNum; trial++){
	//各試行に対する変数の取得
		direction=Psychlops::random(2); //0:right 1:left
		ori_now=invar.get(0,trial)+5*direction;
		dura_now=sampleA.getRefreshRate()*(invar.get(1,trial)/1000);

	//被験者のキー入力待ち
		sampleA.clear(0.5);
		sampleA.var(ori_now,100,100);
		for(int i=0; i<64; i++) trial_header[i] = 0;// 文字列の初期化
		sprintf(trial_header, "%s%d%s%d","Trial: ",  trial, " /", trialNum );//試行番号の埋め込み
		sampleA.message(trial_header, sampleA.getHcenter(), sampleA.getVcenter()-100, Color::green);//キー待ちメッセージの描画
		sampleA.flip();
		while(!Input::get(Keyboard::spc));

	//刺激描画
		sampleA.clear(0.5);
		gIMG[0].draw();
		sampleA.flip(dura_now);
		sampleA.clear(0.5);
		sampleA.flip();

	//反応の取得と格納
		while(1){
			if(Input::get(Keyboard::j)){ans=0;break;} //"j"なら右(0)
			if(Input::get(Keyboard::f)){ans=1;break;}//"f"なら左(1)
			if(Input::get(Keyboard::esc)){exit(0);}
		}
		if(ans==direction)answer[trial]=1;
			else answer[trial]=0;
		orientationCondition[trial]=invar.get(0, trial);
		durationCondition[trial]=invar.get(1, trial);
	}

	sampleA.clear(0.5);
	sampleA.message("Press space key to exit.", sampleA.getHcenter(), sampleA.getVcenter()-100, Color::green);
	Data::savearray("ExptData.dat", "Duration /t Orientation /t Answer", trialNum, durationCondition, orientationCondition, answer );
}

