package version10_2_2_1.environment;

import java.awt.Color;
import java.io.BufferedReader;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import version10_2_2_1.Main;

/**
 * Virtual environment for the agent
 * @author simon gay, olivier georgeon
 */
public class Environment{

	public static String WindowTitle = "Vacuum-SG";
	public static final String DEFAULT_BOARD = "Board16x16_boxes.txt";		// default environment configuration
	
    private Main platform;
	
	private EnvironnementFrame m_envFrame;
	private EnvironnementPanel m_envPanel;
	private Block[][] m_blocks;
	public ArrayList<Box> boxList;
	
	private String boardFile="";

	// tactile properties
	public static final int EMPTY        = 0;
	public static final int SMOOTH       = 1;
	public static final int FOOD         = 2;
	public static final int HARD		  	= 3;
	
	// visual properties
	public static final Color FIELD_COLOR = Color.white;
	public static final Color WALL1       = new Color(  0,128,  0);
	public static final Color WALL2       = new Color(  0,230, 92);
	//public static final Color ALGA1       = new Color(220,50, 50);
	//public static final Color ALGA2       = new Color( 46,230,  0);
	public static final Color FISH1       = new Color(150,128,255);
	public static final Color AGENT_COLOR = new Color(128,128,128);
	public static final Color TILE		= new Color(200,50,50);
	public static final Color BOX			= new Color(200,200,0);
	
	
	// blocks
	public static Block empty=new Block(EMPTY, FIELD_COLOR,"empty",false);
	public static Block wall =new Block(HARD , WALL1,"wall1", true);
	public static Block wall2=new Block(HARD , WALL2,"wall2", true);
	//public static Block alga1=new Block(SMOOTH,ALGA1,"alga1", true);
	//public static Block alga2=new Block(SMOOTH,ALGA2,"alga2", true);
	public static Block fish =new Block(FOOD  ,FISH1,"fish", true);
	public static Block tile =new Block(SMOOTH,TILE,"tile",true);
	
	/////////////////////////////////////////////////////////////////////////////////////////
	
	protected int index_new;						// number of created agents (used for id)
	protected int id;								// identification number of the selected agent
	private int index;								// index of the selected agent
	
	protected int m_w;
	protected int m_h;
	
	////////////////////////////////////////////////////////////////////////////////////////
	
	public Environment(Main m){

		boardFile=DEFAULT_BOARD;
		
		platform=m;
		
		boxList=new ArrayList<Box>();
		
		// initialize the environment
		try{
			this.init(boardFile);
		}
		catch (Exception e){
			System.out.println("error initializing the board");
		}
		
		m_envPanel=new EnvironnementPanel(this);
		m_envFrame=new EnvironnementFrame(this,m_envPanel);
	}
	
	// initialize the environment
	public void init(String f) throws Exception{
		int l_w;
		int l_h;
		
		index_new=0;
		boardFile=f;
		
		boxList.clear();
		
		BufferedReader br = null;
		
		try{
			br = new BufferedReader(new FileReader(f));

			List<String> lines = new ArrayList<String>();
			String line = "";
			while ((line = br.readLine()) != null){ 
				line = line.trim();
				if (line.length() != 0)
					lines.add(line); 
			}
			
			l_h = lines.size();
			l_w = (lines.get(0).toString().length() + 1) / 2;
			init(l_w, l_h);
			
			if (l_h <= 0 || l_w <= 0)
				throw new IllegalStateException("Invalid width or height!");
			
			int y = 0;

			for (Iterator<String> i = lines.iterator(); i.hasNext(); )
			{
				line = (String)i.next();
				if (((line.length() + 1) / 2) != l_w)
					throw new 
						IllegalStateException("Width must be consistent!");

				String[] square = line.split(" ");
				
				for (int x = 0; x < l_w; x++)
				{
					m_blocks[x][l_h-1-y]=empty;
					
					// Static fish
					if (square[x].equals("*"))
					{
						m_blocks[x][l_h-1-y]=fish;
					}	
					
					// Ernest agents
					if (square[x].equalsIgnoreCase("^") || square[x].equalsIgnoreCase(">") ||
						square[x].equalsIgnoreCase("v") || square[x].equalsIgnoreCase("<"))
					{
						platform.addAgent(index_new);
						platform.lastAgent().setEnvironment(this);
						
						if (square[x].equalsIgnoreCase("^"))
							platform.agentPosition(x,l_h-1-y,(float) Math.PI/2);
						else if (square[x].equalsIgnoreCase(">"))
							platform.agentPosition(x,l_h-1-y,0);
						else if (square[x].equalsIgnoreCase("v"))
							platform.agentPosition(x,l_h-1-y,(float)-Math.PI/2);
						else if (square[x].equalsIgnoreCase("<"))
							platform.agentPosition(x,l_h-1-y,(float) Math.PI);
						
						id=index_new;
						index=platform.agentIndex(id);
						
						index_new++;
					}
					
					// box
					if (square[x].equals("B"))
					{
						boxList.add(new Box(x,l_h-1-y,this));
					}
					
					// Wall
					if (Character.isLetter(square[x].toCharArray()[0])){
						if (square[x].equalsIgnoreCase("w"))
							m_blocks[x][l_h-1-y]=wall;
						else if (square[x].equalsIgnoreCase("g"))
							m_blocks[x][l_h-1-y]=wall2;
						else
							m_blocks[x][l_h-1-y]=empty;
					}
					
					// alga
					/*if (Character.isDigit(square[x].toCharArray()[0])){
						switch (Integer.parseInt(square[x]) ){
							case 1: m_blocks[x][l_h-1-y]=alga1; break;
							case 2: m_blocks[x][l_h-1-y]=alga2; break;
							default:m_blocks[x][l_h-1-y]=empty;break;
						}
					}*/
					
					// tile
					if (square[x].equalsIgnoreCase("O")){
							m_blocks[x][l_h-1-y]=tile;
					}
				}
				y++;
			}
			
			if (platform.nbAgent()<=0)
				throw new 
					IllegalStateException("error 404 : Agents not found!");
		}
		catch (Exception e)
		{throw e;}
		finally
		{try { br.close(); } catch (Exception e) {}}	
		
		System.out.println("initialized ") ;
	}
	
	// initialize the matrix that characterizes the environment
	public void init(int w,int h){
		m_w=w;
		m_h=h;
		m_blocks=new Block[w][h];
	}
	
	// repaint the environment
	public void drawGrid(){
		m_envFrame.repaint();
	}
	
	// repaint the environment when a modification from the observer is applied
	public void drawGrid(int c){
		
		int l_h = m_h;
		
		// click left : change agent
		if (c == 1){
			int ident=platform.agentId(m_envPanel.getFClickX(), m_envPanel.getFClickY() );
			int index2=platform.agentIndex(m_envPanel.getFClickX(), m_envPanel.getFClickY() );
			if (ident!=-1){
				id=ident;
				index=index2;
				platform.selectAgent(index2).setDisplay();
				
				System.out.println("selected agent #"+id);
			}
		}
		
		// click wheel : add or remove static fish and tile (empty-> fish->tile->empty)
		if (c == 2){
			if (isFood(m_envPanel.getClickX(),l_h-1-m_envPanel.getClickY() )){
				m_blocks[m_envPanel.getClickX()][l_h-1-m_envPanel.getClickY() ]= tile;
			}
			else{
				if (isEmpty(m_envPanel.getClickX(),l_h-1-m_envPanel.getClickY() )){
					m_blocks[m_envPanel.getClickX()][l_h-1-m_envPanel.getClickY() ]= fish;
				}
				else{
					if (isTile(m_envPanel.getClickX(),l_h-1-m_envPanel.getClickY() )){
						m_blocks[m_envPanel.getClickX()][l_h-1-m_envPanel.getClickY() ]= empty;
					}
				}
			}
		}
		
		// click right : add or remove wall
		if (c == 3){
			if (isWall(m_envPanel.getClickX(),l_h-1-m_envPanel.getClickY() )){
				m_blocks[m_envPanel.getClickX()][l_h-1-m_envPanel.getClickY() ]= empty;

			}
			else{
				if (isEmpty(m_envPanel.getClickX(),l_h-1-m_envPanel.getClickY() )){
					m_blocks[m_envPanel.getClickX()][l_h-1-m_envPanel.getClickY() ]= wall;
				}
			}
		}

		// click right + shift : add or remove box
		if (c == 6){
			boolean found=false;
			int i=0;
			while (i<boxList.size() && !found){
				float d=(boxList.get(i).getpx()-m_envPanel.getClickX()) * (boxList.get(i).getpx()-m_envPanel.getClickX())
				        +(l_h-1-boxList.get(i).getpy()-m_envPanel.getClickY()) * (l_h-1-boxList.get(i).getpy()-m_envPanel.getClickY());
				if (d<1) found=true;
				else i++;
			}
			if (found){
				boxList.remove(i);
			}
			else{
				boxList.add(new Box(m_envPanel.getClickX(),l_h-1-m_envPanel.getClickY(),this));
			}
		}
		
		m_envFrame.repaint();
	}
	
	
	public String getBoardFileName() {
		return boardFile;
	}
	
	///////////////////////////////////////////////////////////////////////////////////////
	// get properties of elements
	public boolean isEmpty(float x, float y){
		if (Math.round(x)<0 || Math.round(y)<0 || Math.round(x)>=m_w || Math.round(y)>=m_h) return true;			// outside is empty
		return m_blocks[Math.round(x)][Math.round(y)].isEmpty();
	}
	public boolean isWall(float x, float y){ 
		if (Math.round(x)<0 || Math.round(y)<0 || Math.round(x)>=m_w || Math.round(y)>=m_h) return false;
		return 	(m_blocks[Math.round(x)][Math.round(y)].isWall()); 
	}
	public boolean isFood(float x, float y){
		if (Math.round(x)<0 || Math.round(y)<0 || Math.round(x)>=m_w || Math.round(y)>=m_h) return false;
		return 	(m_blocks[Math.round(x)][Math.round(y)].isFood()); 
	}
	public boolean isAlga(float x, float y){
		if (Math.round(x)<0 || Math.round(y)<0 || Math.round(x)>=m_w || Math.round(y)>=m_h) return false;
		return 	(m_blocks[Math.round(x)][Math.round(y)].isAlga()); 
	}
	public boolean isTile(float x, float y){ 
		if (Math.round(x)<0 || Math.round(y)<0 || Math.round(x)>=m_w || Math.round(y)>=m_h) return false;
		return 	(m_blocks[Math.round(x)][Math.round(y)].isTile()); 
	}
	
	public boolean isBox(float x, float y){ 
		if (x<0 || y<0 || x>=m_w || y>=m_h) return false;
		for (int i=0;i<boxList.size();i++){
			double d= (x-boxList.get(i).getpx())*(x-boxList.get(i).getpx())+(y-boxList.get(i).getpy())*(y-boxList.get(i).getpy());
			if (d<0.01) return true;
		}
		return false;
	}
	
	public Color seeBlock(float x, float y){ 
		return 	(m_blocks[Math.round(x)][Math.round(y)].seeBlock()); 
	}
	public int touchBlock(float x, float y){ 
		return 	(m_blocks[Math.round(x)][Math.round(y)].touchBlock()); 
	}
	public boolean isVisible(float x, float y){ 
		return 	(m_blocks[Math.round(x)][Math.round(y)].isVisible()); 
	}
	
	public boolean isWalkthroughable(float x, float y){ 
		return m_blocks[Math.round(x)][Math.round(y)].isWalkthroughable(); 
	}
	
	public void setBlock(float x, float y,Block b){
		m_blocks[Math.round(x)][Math.round(y)]=b;
	}
	
	
	public int getWidth()
	{ return m_w; }

	public int getHeight()
	{ return m_h; }
	
	
	public Main platform(){
		return platform;
	}
	
	public int getIndex(){
		return index;
	}
	public void setIndex(int i){
		index=i;
	}
	
}
