import java.util.*;

public class Walk {


	protected Float[][] board;
	protected int[][] input;
	protected int maximum;
	protected int[] size;
	protected int[][] levycount;
	protected int[] position;
	protected ArrayList<Position> map;
	protected static boolean alternativejumpstyle = false;
	protected static boolean wackelndedirection = false;
	
	public Walk(Board boo) {
		size = boo.size();
		board = boo.board;
		input = boo.input;
		levycount = new int[size[0]][size[1]];
		position = new int[2];
		maximum = boo.boardmax();
		map = new ArrayList<Position>();
		}
	
	public ArrayList<Position> getMap(){
		return map;
	}
	
	public void clear(){
		position[0]=(int) (Math.random()*size[0]);
		position[1]=(int) (Math.random()*size[1]);
		for(int i=0;i<size[0];i++){
			for(int j=0;j<size[1];j++){
				levycount[i][j]=0;
			}
		}
		map.clear();
	}
	
	public int[][] getLevycount(){
		return levycount;
	}
	
	public int getLevycount(int i, int j){
		return levycount[i][j];
	}
	
	public void setLevy(int a, int b, int c){
		levycount[a][b]=c;
	}
	
	public void levyplus(Position hier){
		levycount[hier.getx()][hier.gety()]++;
	}
	
	
	public void setPosition(int i, int j){
		position[0]=i;
		position[1]=j;
	}
	
	public void setPosition(Position maxPos) {
		position[0]=maxPos.getx();
		position[1]=maxPos.gety();
		if(position[0]<1){ position[0]=(int) (Math.random()*this.size[0]);}
		if(position[0]>this.size[0]){ position[0]=(int) (Math.random()*this.size[0]); }
		if(position[1]<1){ position[1]=(int) (Math.random()*this.size[0]);}
		if(position[1]>this.size[1]){ position[1]=(int) (Math.random()*this.size[0]); }
	}
	
	//gibt das maximum aus, das der levywalk gefunden hat
	public Position maxPos(){
		int x=0;
		int y=0;
		int max=0;
		for(int i=0;i<this.size[0];i++){
			for(int j=0;j<this.size[1];j++){
				if(max<this.getLevycount(i, j)){
					max=this.getLevycount(i, j);
					x=i;
					y=j;
				}
			}
		}
		Position pos = new Position(x,y);
		return pos;
	}
	
	public void jump(int k){
		Integer x,y;
		double norm;
		int size1 = Levy.size1;
		int size2 = Levy.size2;
		Random rara = new Random();
		position[0] = rara.nextInt(size1);
		position[1] = rara.nextInt(size2);
		
				
		Random randor = new Random();
		double str = this.strength();
		double z = randor.nextGaussian();
		double[] dir = this.direction();
		double dir1 = dir[0];
		double dir2 = dir[1];
		//double size = Levy.size1;
		
		for(int i=0;i<k;i++){
			z = randor.nextGaussian();
			str = this.strength();
			str=(1-str);
			
			double ab = (str/(z*z));
			Double xab = ab*dir1;
			Double yab = ab*dir2;
			
			xab = position[0] + xab + 0.5;
			yab = position[1] + yab + 0.5;
			x = xab.intValue();
			y = yab.intValue();
			
			if(0<x && x<(size1-1) && 0<y && y<(size2-1)){

				levycount[x][y]++;
				this.setPosition(x, y);
				Position mapo = new Position(x,y);
								
				map.add(mapo);
				dir = this.direction();
				dir1= dir[0];
				dir2= dir[1];
			} else {
				i--;
				dir1= randor.nextGaussian();
				dir2= randor.nextGaussian();
				norm = Math.sqrt(dir1*dir1+dir2*dir2);
				if(norm!=0){
					dir1=dir1/norm;
					dir2=dir2/norm;
				}
			}
		}
	}

	//gehen in die richtung in der die gridpunkte höher werden
	public double[] direction(){
		double[] direction = {0,0};
		
		int x = position[0];
		int y = position[1];
		
		x=Math.max(x, 1);
		x=Math.min(x, this.size[0]-2);
		y=Math.max(y, 1);
		y=Math.min(y, this.size[1]-2);
		
		double v1=input[x+1][y]-input[x-1][y];
		double v2=input[x][y+1]-input[x][y-1];
		//v1=0;
		//v2=0;

		if(v1==0 && v2==0){
			Random randox = new Random();
			double xab = randox.nextGaussian();
			double yab = randox.nextGaussian();
			v1=xab;
			v2=yab;
		}
		double norm = Math.sqrt(v1*v1+v2*v2);
		if(norm!=0){
			direction[0]=v1/norm;
			direction[1]=v2/norm;
		}
		//System.out.println(direction[0]+", "+direction[1]);
		return direction;
	}
	
	//recht viele punkte haben maximalen input-wert, womit str dort 0 ist, also der levy-walk endet. deswegen zumindest immer 0.01 stärke um wegzukommen
	public double strength(){
		double strength = ((this.board[this.position[0]][this.position[1]]));
		if(strength>0.99){
			strength = 0.99;
		}
		if(strength<0.01){
			strength = 0.01;
		}
		return strength;
	}
	
	public void rescale(float sca){
		for(int i=0;i<this.size[0];i++){
			for(int j=0;j<this.size[1];j++){
				this.board[i][j]=(float) (this.board[i][j]*sca);
			}
		}
	}
	
	//gibt die k größten werte aus. 
	//Brauch ich das noch?
	public void printmax(int k){
		int si = this.size[0]*this.size[1];
		int[] es = new int[si];
		List<Integer> est = new ArrayList<Integer>();
		for(int i=0;i<this.size[0];i++){
			for(int j=0;j<this.size[1];j++){
				est.add(this.getLevycount()[i][j]);
			}
		}
		for(int i = 0;i<40000;i++){
			es[i]=est.get(i);
		}
		Arrays.sort(es);
		for(int i = 0;i<k;i++){
			System.out.println(es[40000-i-1]);
		}
	}
	

	public Board connect(int level,Position pos, Walksort waso){
		int size1 = this.size[0];
		int size2 = this.size[1];
		int[][] grid = new int[size1][size2];			
		Board boo = new Board(grid);
		boo.clear();
		for(int i=0;i<size1;i++){
			for(int j=0;j<size2;j++){
				Gridlist list = waso.getSortEntry(i, j);
				//System.out.println(list.length());
				if(list.count(pos)>level){
					boo.inputPlus(i, j);
				}
				
			}
		}
		
		boo.input2board();
		return boo;
	}
	
	//logs from where the levy walk jumps to a certain position/maximum on the grid;
	public Board connect(int level,Position pos){
		int size1 = this.size[0];
		int size2 = this.size[1];
		int[][] grid = new int[size1][size2];			
		Board boo = new Board(grid);
		boo.clear();
		for(int i=1;i<this.getMap().size();i++){
			if(this.getMap().get(i).equal(pos)){
				boo.inputPlus(this.getMap().get(i-1));
				//boo.inputPlus(this.getMap().get(i-2));
			}
		}
		if(grid[pos.getx()][pos.gety()]<level){
			grid[pos.getx()][pos.gety()]=2;
		}
		for(int j=0;j<size2;j++){
			for(int i=0;i<size1;i++){
				if(grid[i][j]<level){
					grid[i][j]=0;
				}
			}
		}
		boo.input2board();
		return boo;
	}
	
	//zähl verbindungen zwischen walk und einer menge von positionen
	public Board connect(int level, Position[] poslist, Walksort waso){
		int size1=this.size[0];
		int size2=this.size[1];
		
		int[][] grid = new int[size1][size2];	
		Board boo = new Board(grid);
		boo.clear();
		for(int i=0;i<size1;i++){
			for(int j=0;j<size2;j++){
				Gridlist list = waso.getSortEntry(i, j);
				if(list.count(poslist)>level){
					boo.inputPlus(i, j);
				}
			}
		}
		boo.input2board();
		return boo;
	}
	
	//zähl verbindungen zwischen walk und einer menge von positionen
	public Board connect(int level, Position[] poslist){
		int[][] grid = new int[this.size[0]][this.size[1]];
		Board boo = new Board(grid);
		boo.clear();
		for(int i=1;i<this.getMap().size();i++){
			for(int j=0;j<poslist.length;j++){
				if(this.getMap().get(i).equal(poslist[j])){
					boo.inputPlus(this.getMap().get(i-1));
				}
			}
		}
		for(int i=0;i<boo.size()[0];i++){
			for(int j=1;j<boo.size()[1];j++){
				if(grid[i][j]<level){
					grid[i][j]=0;
				}
			}
		}
		boo.input2board();
		return boo;
	}


	public Board clusterfind(int connections, int stop, Walksort waso) {

		boolean test = false;
		int pospos=0;
		int poscont=0;

		int j=1;
		Position pos = this.maxPos();
		int here = this.getLevycount(pos.xwert, pos.ywert);
		double grenze = Levy.grenze;
				//20*Levy.jump/(Levy.size1*Levy.size2);
		if(here<grenze){
			Board neu = new Board(this.size[0],this.size[1]);
			neu.clear();
			return neu;
		}
				
		Board neu = this.connect(Board.connections,pos, waso);
		pospos = neu.positivPositions();
		if(Levy.plot>1){
			neu.plot("black", "connect 4", 10, 280);
		}
		
		while(test == false){
			poscont=pospos;
			connections = neu.concalc(pospos, Board.connections,connections);
			
			neu = this.connect(connections,neu.board2Position(), waso);
			pospos=neu.positivPositions();
			if(poscont>=pospos){
				test=true;
			}
			
			if(Levy.plot>1){
				String s = ""+j;
				neu.plot("black", s, 10, 280);
			}
			j++;
			if(j>stop){
				test=true;
			}
		}
		if(Levy.plot>1){
			neu.plot("black", "Added Cluster", 10, 10);
		}
		return neu;
	}
	
}
