import java.lang.Math;
import java.util.*;

/**
 * Representacion logica de un cubo rubik, este formato es mas apropiado
 * para analizar el cubo.
 */
public class ModRubik
{
   private final int ARRIBA = 0;
   private final int IZQUIERDA = 1;
   private final int FRONTAL = 2;
   private final int DERECHA = 3;
   private final int TRASERA = 4;
   private final int ABAJO = 5;
   private ArrayList<Cara> caraList;
   
   /**
    * Crea un cubo rubik ordenado.
    */
   public ModRubik()
   {
      //ArrayList<Cara> ca = new ArrayList<Cara>();
      caraList = new ArrayList<Cara>();
      for (int i = 0; i < 6; i++)
      {
         caraList.add(new Cara(i+1));
      }
   }
   
   public ModRubik(ArrayList<Cara> array)
   {
      caraList = array;
   }
   
   public Cara getCara(int pos)
   {
      return caraList.get(pos);
   }
   
   /**
    * Gira la cara superior en 90 grados.
    */
   private void mover_A(int giros)
   {
      int[] tmp = {0,0,0};
      
      caraList.get(ARRIBA).rotar(giros);
      for (int i = 0; i < giros; i++)
      {
         tmp = caraList.get(FRONTAL).getFila(0); //ya es clon
         caraList.get(FRONTAL).setFila(0, caraList.get(DERECHA).getFila(0));
         caraList.get(DERECHA).setFila(0, caraList.get(TRASERA).getFila(0));
         caraList.get(TRASERA).setFila(0, caraList.get(IZQUIERDA).getFila(0));
         caraList.get(IZQUIERDA).setFila(0, tmp);
      }
   }
   
   /**
    * Gira la cara izquierda en 90 grados.
    */
   private void mover_I(int giros)
   {
      int[] tmp = {0,0,0};
      
      caraList.get(IZQUIERDA).rotar(giros);
      for (int i = 0; i < giros; i++)
      {
         tmp = caraList.get(FRONTAL).getColumna(0); //ya es clon
         caraList.get(FRONTAL).setColumna(0, caraList.get(ARRIBA).getColumna(0));
         caraList.get(ARRIBA).setColumna(0, caraList.get(TRASERA).getColumnaInv(2));
         caraList.get(TRASERA).setColumna(2, caraList.get(ABAJO).getColumnaInv(0));
         caraList.get(ABAJO).setColumna(0, tmp);
      }
   }
   
   /**
    * Gira la cara frontal en 90 grados.
    */
   private void mover_F(int giros)
   {
      int[] tmp = {0,0,0};
      
      caraList.get(FRONTAL).rotar(giros);
      for (int i = 0; i < giros; i++)
      {
         tmp = caraList.get(ARRIBA).getFila(2); //ya es clon
         caraList.get(ARRIBA).setFila(2, caraList.get(IZQUIERDA).getColumnaInv(2));
         caraList.get(IZQUIERDA).setColumna(2, caraList.get(ABAJO).getFila(0));
         caraList.get(ABAJO).setFila(0, caraList.get(DERECHA).getColumnaInv(0));
         caraList.get(DERECHA).setColumna(0, tmp);
      }
   }
   
   /**
    * Gira la cara derecha en 90 grados.
    */
   private void mover_D(int giros)
   {
      int[] tmp = {0,0,0};
      
      caraList.get(DERECHA).rotar(giros);
      for (int i = 0; i < giros; i++)
      {
         tmp = caraList.get(FRONTAL).getColumna(2); //ya es clon
         caraList.get(FRONTAL).setColumna(2, caraList.get(ABAJO).getColumna(2));
         caraList.get(ABAJO).setColumna(2, caraList.get(TRASERA).getColumnaInv(0));
         caraList.get(TRASERA).setColumna(0, caraList.get(ARRIBA).getColumnaInv(2));
         caraList.get(ARRIBA).setColumna(2, tmp);
      }
   }
   
   /**
    * Gira la cara trasera en 90 grados.
    */
   private void mover_T(int giros)
   {
      int[] tmp = {0,0,0};
      
      caraList.get(TRASERA).rotar(giros);
      for (int i = 0; i < giros; i++)
      {
         tmp = caraList.get(ARRIBA).getFilaInv(0); //ya es clon
         caraList.get(ARRIBA).setFila(0, caraList.get(DERECHA).getColumna(2));
         caraList.get(DERECHA).setColumna(2, caraList.get(ABAJO).getFilaInv(2));
         caraList.get(ABAJO).setFila(2, caraList.get(IZQUIERDA).getColumna(0));
         caraList.get(IZQUIERDA).setColumna(0, tmp);
      }
   }
   
   /**
    * Gira la cara de abajo en 90 grados.
    */
   private void mover_B(int giros)
   {
      int[] tmp = {0,0,0};
      
      caraList.get(ABAJO).rotar(giros);
      for (int i = 0; i < giros; i++)
      {
         tmp = caraList.get(FRONTAL).getFila(2); //ya es clon
         caraList.get(FRONTAL).setFila(2, caraList.get(IZQUIERDA).getFila(2));
         caraList.get(IZQUIERDA).setFila(2, caraList.get(TRASERA).getFila(2));
         caraList.get(TRASERA).setFila(2, caraList.get(DERECHA).getFila(2));
         caraList.get(DERECHA).setFila(2, tmp);
      }
   }
   
   /**
    * Gira el cubo completo en 90 grados sobre el eje X.
    */
   private void girar_X(int giros)
   {
      for (int i = 0; i < giros; i++)
      {
         caraList.get(FRONTAL).rotar(1);
         caraList.get(TRASERA).rotar(3);
         Cara tmp = caraList.get(ARRIBA);
         caraList.set(ARRIBA, caraList.get(IZQUIERDA).rotar(1));
         caraList.set(IZQUIERDA, caraList.get(ABAJO).rotar(1));
         caraList.set(ABAJO, caraList.get(DERECHA).rotar(1));
         caraList.set(DERECHA, tmp.rotar(1));
      }
   }
   
   /**
    * Gira el cubo completo en 90 grados sobre el eje Y.
    */
   private void girar_Y(int giros)
   {
      for (int i = 0; i < giros; i++)
      {
         caraList.get(DERECHA).rotar(1);
         caraList.get(IZQUIERDA).rotar(3);
         Cara tmp = caraList.get(FRONTAL);
         caraList.set(FRONTAL, caraList.get(ABAJO));
         caraList.set(ABAJO, caraList.get(TRASERA).rotar(2));
         caraList.set(TRASERA, caraList.get(ARRIBA).rotar(2));
         caraList.set(ARRIBA, tmp);
      }
   }
   
   /**
    * Gira el cubo completo en 90 grados sobre el eje Z.
    */
   private void girar_Z(int giros)
   {
      for (int i = 0; i < giros; i++)
      {
         caraList.get(ARRIBA).rotar(1);
         caraList.get(ABAJO).rotar(3);
         Cara tmp = caraList.get(FRONTAL);
         caraList.set(FRONTAL, caraList.get(DERECHA));
         caraList.set(DERECHA, caraList.get(TRASERA));
         caraList.set(TRASERA, caraList.get(IZQUIERDA));
         caraList.set(IZQUIERDA, tmp);
      }
   }
   
   public void mover(String comando)
   {
      if (comando.equals("A "))
         mover_A(1);
      else
      if (comando.equals("A' "))
         mover_A(3);
      else
      if (comando.equals("I "))
         mover_I(1);
      else
      if (comando.equals("I' "))
         mover_I(3);
      else
      if (comando.equals("F "))
         mover_F(1);
      else
      if (comando.equals("F' "))
         mover_F(3);
      else
      if (comando.equals("D "))
         mover_D(1);
      else
      if (comando.equals("D' "))
         mover_D(3);
      else
      if (comando.equals("T "))
         mover_T(1);
      else
      if (comando.equals("T' "))
         mover_T(3);
      else
      if (comando.equals("B "))
         mover_B(1);
      else
      if (comando.equals("B' "))
         mover_B(3);
      else
      if (comando.equals("X "))
         girar_X(1);
      else
      if (comando.equals("X' "))
         girar_X(3);
      else
      if (comando.equals("Y "))
         girar_Y(1);
      else
      if (comando.equals("Y' "))
         girar_Y(3);
      else
      if (comando.equals("Z "))
         girar_Z(1);
      else
      if (comando.equals("Z' "))
         girar_Z(3);
      
      //System.out.print(toString()); //TEST
   }
   
   public void seqMov(String seq)
   {
      int pos = 0;
      StringBuffer buf;
      
      while (pos < seq.length())
      {
         buf = new StringBuffer();
         while (seq.charAt(pos) != ' ' && seq.charAt(pos) != 0)
         {
            buf.append(seq.charAt(pos));
            pos++;
         }
         mover(buf.toString() + " ");
         pos++;
      }
   }
   
   public void desordenar()
   {
      String mov;
      int _mov, veces, i, j;
      
      for (i = 0; i < 24; i++)
      {
         _mov = (int)(Math.random() * 6);
         veces = (int)(Math.random() * 3 + 1);
         switch (_mov)
         {
            case 0: mov = "A "; break;
            case 1: mov = "I "; break;
            case 2: mov = "F "; break;
            case 3: mov = "D "; break;
            case 4: mov = "T "; break;
            case 5: mov = "B "; break;
            default: mov = "A ";
         }
         
         for (j = 0; j < veces; j++)
            mover(mov);
      }
   }
   
   public ModRubik clone()
   {
      int c;
      ArrayList<Cara> array = new ArrayList<Cara>();
      
      for (c = 0; c < 6; c++)
         array.add(caraList.get(c).clone());
      
      return new ModRubik(array);
   }
   
   public String toString()
   {
      StringBuffer string = new StringBuffer();
      int[] t = {0,0,0};
      
      string.append("        *-------*\n");
      t = caraList.get(ARRIBA).getFila(0);
      string.append("        | " + t[0] + " " + t[1] + " " + t[2] + " |\n");
      t = caraList.get(ARRIBA).getFila(1);
      string.append("        | " + t[0] + " " + t[1] + " " + t[2] + " |\n");
      t = caraList.get(ARRIBA).getFila(2);
      string.append("        | " + t[0] + " " + t[1] + " " + t[2] + " |\n");
      
      string.append("*-------*-------*-------*-------*\n");
      
      t = caraList.get(IZQUIERDA).getFila(0);
      string.append("| " + t[0] + " " + t[1] + " " + t[2] + " ");
      t = caraList.get(FRONTAL).getFila(0);
      string.append("| " + t[0] + " " + t[1] + " " + t[2] + " ");
      t = caraList.get(DERECHA).getFila(0);
      string.append("| " + t[0] + " " + t[1] + " " + t[2] + " ");
      t = caraList.get(TRASERA).getFila(0);
      string.append("| " + t[0] + " " + t[1] + " " + t[2] + " |\n");
      
      t = caraList.get(IZQUIERDA).getFila(1);
      string.append("| " + t[0] + " " + t[1] + " " + t[2] + " ");
      t = caraList.get(FRONTAL).getFila(1);
      string.append("| " + t[0] + " " + t[1] + " " + t[2] + " ");
      t = caraList.get(DERECHA).getFila(1);
      string.append("| " + t[0] + " " + t[1] + " " + t[2] + " ");
      t = caraList.get(TRASERA).getFila(1);
      string.append("| " + t[0] + " " + t[1] + " " + t[2] + " |\n");
      
      t = caraList.get(IZQUIERDA).getFila(2);
      string.append("| " + t[0] + " " + t[1] + " " + t[2] + " ");
      t = caraList.get(FRONTAL).getFila(2);
      string.append("| " + t[0] + " " + t[1] + " " + t[2] + " ");
      t = caraList.get(DERECHA).getFila(2);
      string.append("| " + t[0] + " " + t[1] + " " + t[2] + " ");
      t = caraList.get(TRASERA).getFila(2);
      string.append("| " + t[0] + " " + t[1] + " " + t[2] + " |\n");
      
      string.append("*-------*-------*-------*-------*\n");
      
      t = caraList.get(ABAJO).getFila(0);
      string.append("        | " + t[0] + " " + t[1] + " " + t[2] + " |\n");
      t = caraList.get(ABAJO).getFila(1);
      string.append("        | " + t[0] + " " + t[1] + " " + t[2] + " |\n");
      t = caraList.get(ABAJO).getFila(2);
      string.append("        | " + t[0] + " " + t[1] + " " + t[2] + " |\n");
      string.append("        *-------*\n");
      
      return string.toString();
   }
   
   //Test
   public static void main(String[] args)
   {
      ModRubik cubo = new ModRubik();
      Scanner teclado = new Scanner (System.in);
      String input;
      
      do
      {
         System.out.println(cubo.toString());
         
         System.out.print("Ingrese accion: ");
         input = teclado.next();
         
         if (input.equals("salir"))
            break;
         
         if (input.equals("mix"))
            cubo.desordenar();
         else
            cubo.mover(input + " ");
      }while(true);
      
      return;
   }
}
