import java.util.*;
import java.io.*;
// the main maze class.

class Maze
{
    protected int nrows;	// number of rows in maze
    protected cell map[][];	// array of cell arrays; 
    protected Position start;	// location of the start
    protected Position finish;	// coordinates of the finish

   public Maze(String filename)
   {
      setupMaze(filename);
   }

    public void visit(Position p)
    // pre: p is a position within the maze
    // post: cell at position p is set to visited
    {
	if (isValid(p))
	{
	    map[p.row()][p.col()].visited = true;
	}
    }

    public boolean isVisited(Position p)
    // pre: p is a position within the maze
    // post: returns true if the position has been visited
    {
	return isValid(p) && map[p.row()][p.col()].visited;
    }

    public Position start()
    // post: returns start position
    {
	return start;
    }

    public Position finish()
    // post: returns finish position
    {
	return finish;
    }

    protected boolean isValid(Position p)
    // post: returns true iff p is a location within the maze
    {
	int row = p.row();
	int col = p.col();
	return (row >= 0) && (row < nrows) &&
	       (col >= 0) && (col < map[row].length);
    }

    public boolean isClear(Position p)
    // post: returns true iff p is a clear location within the maze
    {
	return isValid(p) && map[p.row()][p.col()].clear;
    }
   
   public void setupMaze(String filename)
    // pre: filename is the name of a maze file. # is a wall.
    //      's' marks the start, 'f' marks the finish.
    // post: reads and constructs maze from file <filename>
    {
	BufferedReader rstream = null; // eventually, the reader

	try // exception occurs on FileInputStream or File
	{
	    rstream = new BufferedReader(new FileReader(filename )); 
	}
	catch (FileNotFoundException e)
	{
	    System.out.println(e);
	}
	try
	{
	    // we read # of rows.  the # of cols depends on line length
	    String x = rstream.readLine();
	    nrows = Integer.parseInt(x);
	    //nrows = 10;
	    System.out.println(nrows);


	    // allocate rows.  Understand this.
	    map = new cell[nrows][]; // allocate row pointers

	    // for each row, read it.
	    for (int row=0; row<nrows; row++) { // read rows
		// read in a line
		String s = rstream.readLine();
		System.out.println(s);
		// allocate array of cell refs for each line
		int len = s.length();
		map[row] = new cell[len];
		// now, initialize each cell reference to a cell
		for (int col=0; col<len; col++) {
		    char c = s.charAt(col);
		    map[row][col] = new cell();
		    // process each character
		    switch(c) {
		      case 's':
			start = new Position(row,col); 
			break;
		      case 'f': 
			finish = new Position(row,col); 
			break;
		      case ' ':
		      case '.': // means visited, but clear
			break;
		      case '#': 
		      default:
			map[row][col].clear = false;
		    }
		}
	    }

	}
	catch (Exception e)
	{
	    System.out.println("IOException on maze read.");
	}
	/*Assert.condition((start != null) && (finish != null),
	  "start & finish must be defined in maze.");*/
    }

    public String toString()
    // post: returns string representing maze state
    //       "." is used to mark visited non-start/finish locations
    {
	// a mutable string
	StringBuffer s = new StringBuffer();

	s.append(nrows).append('\n');
	for (int row=0; row < nrows; row++) {
	    for (int col=0; col < map[row].length; col++) {
		Position here = new Position(row,col);
		// determine character to represent location
		// note --- no implementation dependent actions
		if (here.equals(start)) s.append('s');
		else if (here.equals(finish)) s.append('f');
		else if (isVisited(here)) s.append('.');
		else if (isClear(here)) s.append(' ');
		else s.append('#');
	    }
	    s.append('\n');
	}
	return s.toString();
    }

    /*    public Iterator neighbors(Position p)
    // pre: p is a valid, clear position
    // post: returns valid and clear neighbors in compass order
    {
	Assert.pre(isValid(p),"Seek neighbors of clear positions.");
	Vector v = new Vector(4);
	if (isClear(p.north())) v.addElement(p.north());
	if (isClear(p.east())) v.addElement(p.east());
	if (isClear(p.south())) v.addElement(p.south());
	if (isClear(p.west())) v.addElement(p.west());
	// v.elements: an iterator over v
	return v.elements();
    }
    */

   
}   




