import java.util.*;
import java.util.concurrent.TimeUnit;
import java.io.*;
import java.io.File;
import javax.swing.Timer;
import java.awt.event.*;
import java.net.URL;
import javax.sound.sampled.*;
import java.lang.*;
 /**
 *  Clase MyWorld
 *  <p>
 *  Esta clase es la controla las reglas del juego
 *  Controla también las interacciones del mouse con los objetos
 *  Todos los calculos se hacen aquí
 */
public class MyWorld implements ActionListener {
   private PrintStream out;  
   private ArrayList<PlayableElement> elements;  // Elementos en general
   private static ArrayList<Board> boards;   //Lissta de casillas
   private static ArrayList<Board> moves; //Movimientos posibles de las piezas
   private static ArrayList<Board> attacks;  //Ataques posibles de las piezas
   public static ArrayList<PlayableElement> hand1; //Mano1 (Para las cartas)
   public static ArrayList<PlayableElement> hand2; //Mano2 (Para las cartas)
   private Timer passingTime;   
   private double t;        // simulation time
   private double delta_t;        // in seconds
   private double refreshPeriod;  // in seconds
   private MyWorldView view;
   public InfoView info;   //Es para poder ver la informacion de las piezas y cartas al presionar click derecho sobre una
   public static int turn = 1;               //Manejador de turnos (1=negro 0=blanco)
   public static Board IniPos = null; //Para devolver la pieza su posicion anterior
   private static boolean IsSound = false;  //Para saber si esta sonando la cancion de fondo
   private Clip Music = null; //Para agregar musica y sonidos
   private boolean isSelected = false;
   private Piece SelectedPiece = null;
   private Board PreBoard = null;
   private static BotonPass boton = null;
   private boolean visibleinfo = false;
  
/**
    * Constructor de MyWorld
    * Inicializa las listas de los objetos en el mundo, y arranca el juego
    */
   public MyWorld(){
      this(System.out);
        
   }
   public MyWorld(PrintStream output){
      out = output;       //  [s]
      elements = new ArrayList<PlayableElement>();
      boards = new ArrayList<Board>();
      moves = new ArrayList<Board>();
      attacks = new ArrayList<Board>();
      hand1 = new ArrayList<PlayableElement>();
      hand2 = new ArrayList<PlayableElement>();
      view = new MyWorldView(this);
      refreshPeriod = 0.03;
      passingTime = new Timer((int) (refreshPeriod*1000) ,this);
   }
//////////////////////////////////////////////////ADDING ELEMENTS//////////////////////////////////////////////////////////////////////
  /**
    *   Agreaga un objeto al mundo
    *   @param e PlayableElement a agreagar
    */
   public void addElement(PlayableElement e) {
      elements.add(e);
      view.repaint();
   }
   /**
    *   Agrega un BotonPass al juego
    *   @param bo BotonPass a agregar
    */
   public void addElement(BotonPass bo){
      boton = bo;
      view.repaint();
   }
   /**
    *   Agrega una casilla al mundo
    *   @param b casilla a agregar
    */
   public static void addElement(Board b) {
		boards.add(b);
	}
   /**
    *   Agrega la clase InfoView al mundo
    *   @param i corresponde a la clase InfoView
    */
   public void setInfo(InfoView i){
        info = i;
    }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////7   

/////////////////////////////////////////////////////GET ARRAYS//////////////////////////////////////////////////////////////////////   
  /**
    *   Se obtiene la vista del mundo
    *   @return vista del mundo
    */
   public MyWorldView getView() {
      return view;
   }
   /**
    *   Se obtiene la lista de elementos existentes en el mundo
    *   @return lista de elementos (PlayableElement)
    */
   public ArrayList<PlayableElement> getPlayableElements(){
      return elements;
   }
   /**
    *   Se obtiene la lista de casillas existentes en el mundo
    *   @return lista de casillas
    */
   public static ArrayList<Board> getBoards(){
		return boards;
   }
   /**
    *   Se obtiene el botonpass del mundo
    *   @return BotonPass
    */
   public BotonPass getBoton(){
      return boton;
   }
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////   

////////////////////////////////////////////////////VIEWS/////////////////////////////////////////////////////////////////
   /**
    *   Establece el controlador de graficos
    *   @param view de MyWorldView que es el que maneja los graficos del mundo
    */
   public void setView(MyWorldView view) {
		this.view = view;
	}
   /**
    *   Refresca los graficos (los vuelte a pintar)
    */
   public void repaintView(){
		view.repaint();
	}
    
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////SOUNDS AND MUSIC ///////////////////////////////////////////////////////////////////////////////
   /*
    *Activa la musica de fondo
    */
   public void backMusic() {  //Para la musica de fondo
        try {
            // direccion del archivo
            URL url = this.getClass().getClassLoader().getResource("src/backmusic.wav");
            AudioInputStream audioIn = AudioSystem.getAudioInputStream(url);
            // utilizamos sound clip para reproducirlo
            Music = AudioSystem.getClip();
            Music.open(audioIn);
            Music.start();
        } catch (UnsupportedAudioFileException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (LineUnavailableException e) {
            e.printStackTrace();
        }
    }
    /*
    *Activa un sonido al tomar una pieza
    */
   public void pickSound() {  //Sonido al tomar una pieza
        try {
            // direccion del archivo
            URL url = this.getClass().getClassLoader().getResource("src/Pick.wav");
            AudioInputStream audioIn = AudioSystem.getAudioInputStream(url);
            // utilizamos sound clip para reproducirlo
            Clip clip = AudioSystem.getClip();
            clip.open(audioIn);
            clip.start();
        } catch (UnsupportedAudioFileException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (LineUnavailableException e) {
            e.printStackTrace();
        }
    }
    /*
    *Activa un sonido al mover una pieza
    */
   public void putSound() {   //Sonido al dejar una pieza
        try {
            // direccion del archivo
            URL url = this.getClass().getClassLoader().getResource("src/Put2.wav");
            AudioInputStream audioIn = AudioSystem.getAudioInputStream(url);
            // utilizamos sound clip para reproducirlo
            Clip clip = AudioSystem.getClip();
            clip.open(audioIn);
            clip.start();
        } catch (UnsupportedAudioFileException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (LineUnavailableException e) {
            e.printStackTrace();
        }
    }
    /*
    *Activa un sonido al dejar una pieza en el mismo lugar
    */
   public void sameSound() {  //Sonido al moverse una pieza
        try {
            // direccion del archivo
            URL url = this.getClass().getClassLoader().getResource("src/SamePlace.wav");
            AudioInputStream audioIn = AudioSystem.getAudioInputStream(url);
            // utilizamos sound clip para reproducirlo
            Clip clip = AudioSystem.getClip();
            clip.open(audioIn);
            clip.start();
        } catch (UnsupportedAudioFileException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (LineUnavailableException e) {
            e.printStackTrace();
        }
    }
    /*
    *Activa un sonido cuando una pieza ataca a otra
    */
   public void attackSound() {   //sonido de ataque (Tal vez no necesitemos todos los sonidos)
        try {
            // direccion del archivo
            URL url = this.getClass().getClassLoader().getResource("src/Attack.wav");
            AudioInputStream audioIn = AudioSystem.getAudioInputStream(url);
            // utilizamos sound clip para reproducirlo
            Clip clip = AudioSystem.getClip();
            clip.open(audioIn);
            clip.start();
        } catch (UnsupportedAudioFileException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (LineUnavailableException e) {
            e.printStackTrace();
        }
    }
    /*
    *Activa un sonido cuando se cambia de turno
    */
    public void bellSound() { //Este es para avisar que cambio el turno
        try {
            // direccion del archivo
            URL url = this.getClass().getClassLoader().getResource("src/Bell.wav");
            AudioInputStream audioIn = AudioSystem.getAudioInputStream(url);
            // utilizamos sound clip para reproducirlo
            Clip clip = AudioSystem.getClip();
            clip.open(audioIn);
            clip.start();
        } catch (UnsupportedAudioFileException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (LineUnavailableException e) {
            e.printStackTrace();
        }
    }
    /*
    *Activa un sonido cuando muere una pieza
    */
   public void deadSound() {
      try {
            // direccion del archivo
            URL url = this.getClass().getClassLoader().getResource("src/dead.wav");
            AudioInputStream audioIn = AudioSystem.getAudioInputStream(url);
            // utilizamos sound clip para reproducirlo
            Clip clip = AudioSystem.getClip();
            clip.open(audioIn);
            clip.start();
        } catch (UnsupportedAudioFileException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (LineUnavailableException e) {
            e.printStackTrace();
        }

   }
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////    

////////////////////////////////////////////////////CONTROL MYWORLD/////////////////////////////////////////////////////////////////////////
  /**
    *   Inicia el programa y el tiempo
    */
   public void start() {
      if(passingTime.isRunning()) return;
      passingTime.start();
      if (IsSound == false){
         backMusic();  
         IsSound = true; 
      }
   }
   /**
    *   Metodo para restear el juego
    *   Borra los elementos del juego, y los vuelve a crear dejandolos en su posicion inicial
    */
   public void reset(){
      //Borramos todas las piezas  las casillas
      
      boards.clear();
      elements.clear();
      turn = 1;
      //Y las volvemos a colocar
      for (int i=0;i<8;i++){  //Aqui se crea la matriz de casillas para hacer el tamaño del tablero
			for (int j=0;j<14;j++) {
				Vector2D vec = new Vector2D(i,j);
				Board b = new Board( (MyWorldView.WIDTH-1024)/2 + 70*j, (MyWorldView.HEIGHT - 620)/2 + 70*i, vec);
				addElement(b);
            
		
      if (i==5 && j==0){
      
      KingBlack kb =  new KingBlack(new Vector2D((MyWorldView.WIDTH-1024)/2 + 70*j , (MyWorldView.HEIGHT - 620)/2 + 70*i),new Vector2D(i,j));// crea rey negro
	   addElement(kb);
      b.addPiece(kb);
      }
      if (i==5 && j==13){
		KingWhite kw =  new KingWhite(new Vector2D((MyWorldView.WIDTH-1024)/2 + 70*j, (MyWorldView.HEIGHT - 620)/2 + 70*i),new Vector2D(i,j));// crea un rey blanco
		addElement(kw);
      b.addPiece(kw);}
      
      if (i==0 && j==0){
      HorseBlack hb =  new HorseBlack(new Vector2D((MyWorldView.WIDTH-1024)/2 + 70*j, (MyWorldView.HEIGHT - 620)/2 + 70*i ),new Vector2D(i,j));// crea un caballo negro
	   addElement(hb);
      b.addPiece(hb);}
      
      if (i==0 && j==13){
		HorseWhite hw =  new HorseWhite(new Vector2D((MyWorldView.WIDTH-1024)/2 + 70*j, (MyWorldView.HEIGHT - 620)/2 + 70*i),new Vector2D(i,j));// crea un caballo blanco
		addElement(hw);
      b.addPiece(hw);}
      
      if (i==2 && j==0){
      TowerBlack tb =  new TowerBlack(new Vector2D((MyWorldView.WIDTH-1024)/2 + 70*j, (MyWorldView.HEIGHT - 620)/2 + 70*i),new Vector2D(i,j));// crea una torre negra
	   addElement(tb);
      b.addPiece(tb);}
      
      if (i==2 && j==13){
		TowerWhite tw =  new TowerWhite(new Vector2D((MyWorldView.WIDTH-1024)/2 + 70*j, (MyWorldView.HEIGHT - 620)/2 + 70*i),new Vector2D(i,j));// crea una torre blanca
		addElement(tw);
      b.addPiece(tw);}
      
      if (i==4 && j==0){
      QueenBlack qb =  new QueenBlack(new Vector2D((MyWorldView.WIDTH-1024)/2 + 70*j, (MyWorldView.HEIGHT - 620)/2 + 70*i),new Vector2D(i,j));// crea una reina negra
	   addElement(qb);
      b.addPiece(qb);}
      
      if (i==4 && j==13){
		QueenWhite qw =  new QueenWhite(new Vector2D((MyWorldView.WIDTH-1024)/2 + 70*j, (MyWorldView.HEIGHT - 620)/2 + 70*i),new Vector2D(i,j));// crea una reina blanca
		addElement(qw);
      b.addPiece(qw);}
      
      if (i==7 && j==0){
      BishopBlack bb =  new BishopBlack(new Vector2D((MyWorldView.WIDTH-1024)/2 + 70*j, (MyWorldView.HEIGHT - 620)/2 + 70*i),new Vector2D(i,j));// crea un alfil negro
	   addElement(bb);
      b.addPiece(bb);}
      
      if (i==7 && j==13){
		BishopWhite bw =  new BishopWhite(new Vector2D((MyWorldView.WIDTH-1024)/2 + 70*j, (MyWorldView.HEIGHT - 620)/2 + 70*i),new Vector2D(i,j));// crea un alfil blanco
	   addElement(bw);
      b.addPiece(bw);}
      
      }}
   }
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

/////////////////////////////////////////////////////ACTION PERFORMED///////////////////////////////////////////////////////////////////////////////
   /**
    *   Este metodo se activa al iniciarse el programa y siempre que el tiempo este corriendo
    *   @param event parametro entregado por el tiempo
    */
   public void actionPerformed (ActionEvent event) {  
         //Guarda posiciones del puntero del mouse
      	int mouse_x = MouseListener.x;
		   int mouse_y = MouseListener.y;
         if (Music!=null){
            if (Music.isRunning()==false){
                backMusic();
            }
        }
     
         
         /////////////////////////////////////////BotonPass////////////////////////////////////////////////////////////////////
         int botx = boton.getPosition().getX();
         int boty = boton.getPosition().getY();
         
         if(mouse_x>=botx && mouse_x<botx+70 && mouse_y>=boty && mouse_y<boty+70){ //Si el mouse esta dentro del boton
            
            
            //Pasa de turno e indica con el boton en que turno nos encontramos
            if(MouseListener.LeftClick == 1){
               bellSound();
               if (turn == 1){
                  boton.updateImage(2);
                turn = 0;
                
               }
               else {
                  boton.updateImage(1);
               turn = 1;
               }
               
               
               MouseListener.LeftClick = 0;
            }
            else {
            if (turn == 1) boton.updateImage(1);
            if (turn == 0) boton.updateImage(2);
         }

            
            
         }
         else{
            if (turn == 1) boton.updateImage(0);
            if (turn == 0) boton.updateImage(3);
         }
         /////////////////////////////////////Recorre casillas////////////////////////////////////////////////////////////////////////////////////         
         for (int i=0; i<boards.size();i++){
     		Board b = boards.get(i);   //Guarda las casillas existentes en el mundo
         Piece p = b.getPiece(); //Obtenemos la pieza que se encuentra en la casilla
         
    		int pan_x = b.getPosition().getX();    
    		int pan_y = b.getPosition().getY();    
              
         //////////////////////////////Dentro de la casilla//////////////////////////////////////////////////////////
         if (mouse_x>=pan_x && mouse_x<pan_x+70 && mouse_y>=pan_y && mouse_y<pan_y+70){ 
            
            
            //Actualiza los colores de las casillas
            if (b.imagen !=2 && b.imagen !=3 && b.imagen !=4 && b.imagen !=5 && b.imagen !=6 && b.imagen !=7 && b!=IniPos){  //Para que brille el piso cuando pasa el mouse encima
                b.updateImage(1);
            }
            
            if (b.imagen == 3){
               b.updateImage(4);
            }
            
            if (b.imagen == 5){
               b.updateImage(6);
            }
            
            if (isSelected == false && b.imagen != 1){
               b.updateImage(0);
            }
            if (isSelected == false && b.imagen == 2){
               b.updateImage(0);
            }
            
            
            
            //Si clickeamos una casilla verde nos movemos
            if ((b.imagen == 4) && MouseListener.LeftClick==1 && p == null && SelectedPiece != null && isSelected == true){
               SelectedPiece.setPosition(new Vector2D(b.getPosition().getX(), b.getPosition().getY()));
               SelectedPiece.setColRow(b.getRow(), b.getCol());
               b.addPiece(SelectedPiece); //Se agrega la pieza a la casilla que se movio
               SelectedPiece.blockmoves = true; //Se bloquean sus movimientos
               MouseListener.LeftClick = 0;
               isSelected=false;
               putSound();
            }
            //Si clickeamos una casilla naranja nos devolvemos
            if ((b.imagen == 2) && MouseListener.LeftClick==1 && p == null && SelectedPiece != null && isSelected == true){
               SelectedPiece.setPosition(new Vector2D(b.getPosition().getX(), b.getPosition().getY()));
               SelectedPiece.setColRow(b.getRow(), b.getCol());
               b.addPiece(SelectedPiece); //Y la volvemos a agregar a la casilla
               MouseListener.LeftClick = 0;
               isSelected=false;
               sameSound();
            }
               
            //Si clickeamos una casilla roja con una ficha atacamos 
            if (b.imagen == 6 && MouseListener.LeftClick == 1 && p != null && SelectedPiece != null && isSelected == true){
               p.takeDamage(SelectedPiece); //Recibe daño y si su ho es 0 muere
               SelectedPiece.blockattacks = true; //Bloquea los ataques del atacante
               SelectedPiece.setPosition(new Vector2D (SelectedPiece.getPosition().getX(),SelectedPiece.getPosition().getY()+5));
               SelectedPiece.setColRow(SelectedPiece.getRow(), SelectedPiece.getCol());
               PreBoard.addPiece(SelectedPiece); //Retorna al atacante a su antigua casilla
              
               
               attacks = SelectedPiece.Attacks();  //Devuelve el color de la casilla normal luego de atacar
               for(i=0;i<attacks.size();i++){
                  b = attacks.get(i);
                  b.updateImage(0);
               }
               //No de saca la seleccion para que una vez haya atacado pueda inmediatamente moverse
               MouseListener.LeftClick = 0;
               attackSound();
               
            }
            
            
            
            
            //Agarra una pieza. la borra de la casilla y muestra sus posibles movimientos y ataques
            if(b.imagen == 1 && MouseListener.LeftClick==1 && p != null && isSelected == false && p.team == turn){
               b.removePiece();  //Al agarrar la pieza esta deja de pertenecer a la casilla
               moves = p.Moves();
               attacks = p.Attacks();
               b.updateImage(2);
               p.setPosition(new Vector2D (p.getPosition().getX(),p.getPosition().getY()-5));   //Levanta un poco la pieza
               MouseListener.LeftClick=0;
               //Actualiza las casillas de ataque y movimiento dependiendo de si estas estan bloqueadas o no
               if (p.blockmoves == false){
               for(i=0;i<moves.size();i++){
                  b = moves.get(i);
                  b.updateImage(3);
               }
               }
               if (p.blockmoves == true){
                 
               for(i=0;i<moves.size();i++){
                  b = moves.get(i);
                  b.updateImage(0);
                          
               } }
               if (p.blockattacks == false){
               for(i=0;i<attacks.size();i++){
                  b = attacks.get(i);
                  b.updateImage(5);
               }
               }
               if (p.blockattacks == true){
               for(i=0;i<attacks.size();i++){
                  b = attacks.get(i);
                  b.updateImage(0);
               }
               }
               SelectedPiece = p;   //Guarda la pieza seleccionada
               PreBoard = b;  //Guarda la casilla en donde se encontraba la pieza
               isSelected = true;    
               p.Sel = true; 
               pickSound();        
            }
            
            if (p != null && MouseListener.RightClick == 1){
               visibleinfo = true;
               
               info.setPosition(new Vector2D(p.getPosition().getX()+90/2,p.getPosition().getY()-80));
               info.updateImage(p);
               MouseListener.RightClick = 0;
            }
         
         }
         //////////////////////////////Fuera de una Casilla//////////////////////////////////////////////////////////////
         
         
         else {
            //Actualiza las casillas
            if (b.imagen==1 || isSelected == false)
            b.updateImage(0);
            if(b.imagen==4)
            b.updateImage(3);
            if(b.imagen==6)
            b.updateImage(5);
            //Refresca los ataques y movimientos de las piezas cuando se pasa un turno
            if (p!= null && turn == 1 && p.team == 0) {
               p.blockmoves = false;
               p.blockattacks = false;
            }
            if (p!= null && turn == 0 && p.team == 1) {
               p.blockmoves = false;
               p.blockattacks = false;
            }
            //Elimina la pieza de la casilla si esta esta muerta
            if (p != null && p.dead == true) b.removePiece();
            //Si la info esta visible, al clickear cualquier boton la desaparece
            if (visibleinfo == true && (MouseListener.LeftClick == 1 || MouseListener.RightClick == 1)) {
               visibleinfo = false;
               info.setPosition(new Vector2D((MyWorldView.WIDTH-1024)/2 + 70*20, (MyWorldView.HEIGHT - 620)/2 + 70*20));
            }
            //Al morir una pieza se activa un sonido
            if (p != null && p.dead==true) {
               deadSound();
            }
            
         
         }
         /////////////////////////////Independiente dee la posicion de las casillas/////////////////////////////////////////
         }
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


      
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////      
    	
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////7  

}