// M.D. Healy CSC 111
// Creature class that navigates through a maze object
// using the algorithm described in psuedocode on pages
// 321-325 of the textbook

import java.io.*;
import java.util.Scanner;

/*
 * The maze file format is as described in the comment statements
 * of the code for my Maze class
*/

public class Creature
{

  public static boolean debug = false;
  // setting this to true enables some debugging output

      public static void main(String[] args)
        {

           // Were we given a maze to open?
           if (args.length == 0)
             {
                System.out.println("Need a filename!");
             }
          else
             {
               Maze myMaze = new Maze(args[0]);


               System.out.println(
                                     "\nTrying to solve Maze file: " +
                                     args[0] + "\n" +
                                     "ROWS:" + myMaze.getNumRows() +
                                     " COLS:" + myMaze.getNumCols() +
                                     "\n" +
                                     "Entrance (+) at " +
                                     myMaze.getEntranceX() +
                                     " , " + myMaze.getEntranceY() +
                                     "\n" + "Exit (-) at " +
                                     myMaze.getExitX() + " , " +
                                     myMaze.getExitY() + "\n"
                                 );

               // display the maze
               System.out.println(myMaze);

               // Attempt to solve the maze
               if (solve(myMaze))
                 {
                    System.out.println( "\nSuccess! " +
                                        "('^' = path; '.' = visited)"
                                      );
                 }
               else
                 {
                    System.out.println("\nFailure! ('.' = visited)");
                 }


       // Mark the entrance and exit of the maze again
       // since the Creature may have stepped on them...
       myMaze.setState(myMaze.getExitX(), myMaze.getExitY(),  Maze.State.EXIT);
       myMaze.setState(
                          myMaze.getEntranceX(),
                          myMaze.getEntranceY(),
                          Maze.State.ENTRANCE
                      );

               // Mark the creature's final location

                  int creatureFinalX = myMaze.getCreatureX();
                  int creatureFinalY = myMaze.getCreatureY();
                      try
                        {
                           myMaze.setState(
                                 creatureFinalX,
                                 creatureFinalY,
                                 Maze.State.CREATURE
                               );
                        }
                      catch (Exception e)
                        {
                           // It blew up!!!
                           System.out.println(
                            "setState blew up, Exception "+
                            e + " was thrown here \n\n" +
                            " final X, Y was " +
                            creatureFinalX + " " + creatureFinalY);
                        }

               // display the maze again...
               System.out.println(myMaze.toString() + "\n\n");

             } // done processing maze
        } // end of Main method

      public static boolean solve(Maze theMaze)
        {
          // try the four directions

          if (move(theMaze , 0 , -1))
            {
              return true;
            }

          if (move(theMaze , -1 , 0))
            {
              return true;
            }

          if (move(theMaze , 1 , 0))
            {
              return true;
            }

          if (move(theMaze , 0 , 1))
            {
              return true;
            }

          // if we get here, all moves failed...
          return false;
        } // end solve method

      // dx and dy are each -1, 0, or 1
      public static boolean move(Maze theMaze , int dx , int dy)
        {

            // Debugging output showing intermediate stages
            // turned on or off by a boolean flag
            if (debug)
              {
                if ((theMaze.getMoveCount() % 20) == 1)
                  {
                    System.out.println("\n" + "X,Y, Move-count: " +
                    theMaze.getCreatureX() + " , " +
                    theMaze.getCreatureY() + " , " +
                    theMaze.getMoveCount() + "\n" +
                    theMaze);
                  }
              }

            // Are we already at the exit?
            if (
                 (
                    theMaze.getCreatureX() ==
                    theMaze.getExitX()
                 ) &&
                 (
                    theMaze.getCreatureY() ==
                    theMaze.getExitY()
                 )
               )
               {
                  return true;
               }

            boolean success = true;
            boolean inBounds = true;
            boolean canGoThere = false;
            boolean tryFailed = false;

            int x = theMaze.getCreatureX();
            int y = theMaze.getCreatureY();
            int xMax = theMaze.getNumCols()-1;
            int yMax = theMaze.getNumRows()-1;


            // First we check whether the proposed new
            // position falls within bounds
            if ((x + dx) < 0)
              {inBounds=false;}

            if ((y + dy) < 0)
              {inBounds=false;}

            if ((x + dx) > xMax)
              {inBounds = false;}

            if ((y + dy) > yMax)
              {inBounds = false;}

            if (inBounds)
              {

                // Ok, the proposed new position does
                // fall within bounds.  So now we check
                // whether that square is clear:

                Maze.State tempState = Maze.State.WALL;
                      try {
                          tempState = theMaze.getState(x + dx , y + dy);
                        }
                      catch (Exception e)
                        {
                           // It blew up!!!
                           System.out.println(
                            "setState blew up, Exception " +
                            e + " was thrown here \n\n" +
                            " this X, Y was " +
                            x + " " + y + " step: " + dx + " , " + dy);

                            tryFailed = true;
                        }

                canGoThere = false;

                if (tempState == Maze.State.CLEAR)
                   {
                     canGoThere = true;
                   }

                if (tempState == Maze.State.EXIT)
                   {
                     canGoThere = true;
                   }

                if (tempState == Maze.State.ENTRANCE)
                   {
                     canGoThere = true;
                   }

                 if (tryFailed)
                      {
                         canGoThere = false;
                      }

              } // end if inBounds
            else
              {
                 canGoThere = false;
              }  // end if NOT inBounds

               if (canGoThere) {
                  // go there
                  theMaze.setCreatureX(x + dx);
                  theMaze.setCreatureY(y + dy);

                  // mark it as on the path
                  theMaze.setState(
                                     x + dx,
                                     y + dy,
                                     Maze.State.PATH
                                  );

                  // initialize to wildly-illegal values
                  // so that failure to set them correctly
                  // will be detected
                  int dx1 = 9999;
                  int dx2 = 9999;
                  int dx3 = 9999;
                  int dy1 = 9999;
                  int dy2 = 9999;
                  int dy3 = 9999;

                  // Directions to try
                  // 
                  // we always try our current direction
                  // first

                    if (dx > 0)
                       {
                          // Going RIGHT
                          // First try RIGHT
                          dx1 = 1; dy1 = 0;
                          // Then try UP
                          dx2 = 0; dy2 = -1;
                          // Then try DOWN
                          dx3 = 0; dy3 = 1;
                       }

                    else if (dx < 0)
                       {
                          // Going LEFT
                          // First try LEFT
                          dx1 = -1; dy1 = 0;
                          // Then try DOWN
                          dx2 = 0;  dy2 = 1;
                          // Then try UP
                          dx3 = 0;  dy3 = -1;
                       }

                    else if (dy > 0)
                       {
                          // Going DOWN
                          // First try DOWN
                          dx1 = 0;  dy1 = 1;
                          // Then try RIGHT
                          dx2 = 1;  dy2 = 0;
                          // Then try LEFT
                          dx3 = -1; dy3 = 0;
                       }

                    else if (dy < 0)
                       {
                          // Going UP
                          dx1 = 0; dy1 = -1;
                          // Then try LEFT
                          dx2 = -1; dy2 = 0;
                          // Then try RIGHT
                          dx3 = 1; dy3 = 0;
                       }
                    else
                       {
                           System.out.println(
                                               "Illegal dx, dy:"
                                               + dx + " " + dy);
                           System.exit(1);  // like die() in Perl...
                       }

                    // try first direction
                    success = move(theMaze, dx1 , dy1);
                    if(!success) {
                      // try second direction
                        {success = move(theMaze, dx2 , dy2);}
                      if(!success) {
                        // try third direction
                          {success = move(theMaze, dx3 , dy3);}
                          if(!success) {

                            //mark it as VISITED
                            theMaze.setState(
                                              theMaze.getCreatureX(),
                                              theMaze.getCreatureY(),
                                              Maze.State.VISITED
                                  );

                            //backtrack
                            theMaze.setCreatureX(theMaze.getCreatureX()-dx);
                            theMaze.setCreatureY(theMaze.getCreatureY()-dy);

                          } // end backtracking
                        } // end trying third direction
                      } // end trying second direction
                 } // end if can go there
               else {
                 // can't go there
                 success=false;
                 } // end else

            return success;

        } // end move method

} // end of Creature class