package PrintGame;

import java.awt.*;

/**
 * class representing a turing machine.
 */

public class TuringMachine
{
  public final static int SINGLE_STEP = 1;  
  public final static int FASTFORWARD = 100;
  public final static int NORMAL      = 500;
  
  
  private InstructionList instruct_list_;
  private Robot robot_;
  private LabyrinthField lab_field_;
  private int num_steps_;
  private int num_taken_diamonds_;
  private int max_num_diamonds_;
  private int delay_time_;
  private boolean finished_;
  private int mode_;
  private int num_next_steps_;
  

   
//-----------------------------------------------------------------------------  
/**
 * method returns the conditions of robot's environment
 * @return info about the current condition
 */
  protected Condition getFieldConditions()
  {
    int current_row = robot_.getPosRow();
    int current_col = robot_.getPosCol();
    int cell_mark = lab_field_.getCellMark(current_row, current_col);
    boolean wall_ahead = false;
    boolean wall_left = false;
    boolean wall_right = false;
    int state = robot_.getState();
    
    try
    {
      switch (robot_.getCurrentDirection().getDirection())
      {
        case Direction.DIRECTION_UP:
          wall_ahead = lab_field_.isWall(current_row-1, current_col);
          wall_left  = lab_field_.isWall(current_row, current_col-1);
          wall_right = lab_field_.isWall(current_row, current_col+1);
          break;
        case Direction.DIRECTION_DOWN:
          wall_ahead = lab_field_.isWall(current_row+1, current_col);
          wall_left  = lab_field_.isWall(current_row, current_col+1);
          wall_right = lab_field_.isWall(current_row, current_col-1);
          break;
        case Direction.DIRECTION_LEFT:
          wall_ahead = lab_field_.isWall(current_row, current_col-1);
          wall_left  = lab_field_.isWall(current_row+1, current_col);
          wall_right = lab_field_.isWall(current_row-1, current_col);
          break;
        case Direction.DIRECTION_RIGHT:
          wall_ahead = lab_field_.isWall(current_row, current_col+1);
          wall_left  = lab_field_.isWall(current_row-1, current_col);
          wall_right = lab_field_.isWall(current_row+1, current_col);
          break;
      }
    }
    catch (IllegalDirectionException ex)
    {
      System.err.println(ex.getMessage());
    }
    
    Condition temp_condition = new Condition(state, cell_mark, wall_left, wall_right, wall_ahead );
    
    return temp_condition;
  }

//-----------------------------------------------------------------------------  
/**
 * method performes the given action and checks if the robot crashes
 * @param action specifies what to do
 * @return true if no problems occure during the action, false if the robot is crashed
 */  
  protected boolean performAction(Action action)
  {
    int current_row = robot_.getPosRow();
    int current_col = robot_.getPosCol();
    boolean action_performed = false;
    
    
    try
    {
      switch (action.getMovingDirection())
      {
        case Action.MOVE_AHEAD:
          robot_.moveForward();
          break;
        case Action.MOVE_BACK:
          robot_.goBack();
          break;
        case Action.MOVE_LEFT:
          robot_.goLeft();
          break;
        case Action.MOVE_RIGHT:
          robot_.goRight();
          break;
      }
      
    }
    catch (IllegalDirectionException ex)
    {
      System.err.println(ex.getMessage());
    }     
    
    if(lab_field_.isWall(robot_.getPosRow(), robot_.getPosCol()))
    {
      robot_.setPos(current_row, current_col);
      
    }
    else
    {
      robot_.setState(action.GetState());
      lab_field_.setCellMark(current_row, current_col, action.getCellMark());
      action_performed = true;
    }

    return action_performed;
  }

//-----------------------------------------------------------------------------  
/**
 *
 */    
  public void setInstructionList(InstructionList instruct_list)
  {
    instruct_list_ = instruct_list;
  }

//-----------------------------------------------------------------------------  
/**
 *
 */    
  public void setRobot(Robot robot)
  {
    robot_ = robot;
  }

//-----------------------------------------------------------------------------  
/**
 *
 */    
  public void setLabyrinthField(LabyrinthField lab_field)
  {
    lab_field_ = lab_field;
  }

 public void takeAndCalcDiamonds()
 {
   if(lab_field_.containsDiamond(robot_.getPosRow(), robot_.getPosCol()))
   {
     num_taken_diamonds_++;
     lab_field_.removeDiamond(robot_.getPosRow(), robot_.getPosCol());
   }
   
 }

  public void start(Component component)
    throws InterruptedException
  {
    finished_ = false;
    max_num_diamonds_ = lab_field_.getNumDiamonds();
    num_taken_diamonds_ = 0;
    num_steps_ = 0;    
    
//    if (mode_ != SINGLE_STEP)
    {
      run(component);
    }
//    else
    {
//      runSingleStep(component);
    }
  }
 
 
//-----------------------------------------------------------------------------  
/**
 * method processes an instruction list
 * @param component window which contains the labyrinth field
 */    
  public void run(Component component)
    throws InterruptedException
  {
    while(finished_ == false)
    {
      
      
/*      System.out.println("field conditions: "+field_condition.isWallLeft()  
                                            +field_condition.isWallRight()
                                            +field_condition.isWallAhead()
                                            +field_condition.getCellMark()  
                                            +field_condition.isNewArea());*/
      
      
      if (mode_ != SINGLE_STEP)
        runSingleStep(component);
      else if (num_next_steps_ > 0)
      {
        runSingleStep(component);
        num_next_steps_--;
      }
        
    }
 
  }
  
//-----------------------------------------------------------------------------
  protected void runSingleStep(Component component)
    throws InterruptedException
  {
    Instruction current_instruction;
    Condition field_condition;
    field_condition = getFieldConditions();
    int num_instructions = instruct_list_.size();
    boolean condition_found = false;
   
    for(int index=0; index < num_instructions; index++)
    {
      current_instruction = instruct_list_.getInstructionAtPos(index);

/*   System.out.println("instruct conditions: "+current_instruction.getCondition().isWallLeft()  
                                            +current_instruction.getCondition().isWallRight()
                                            +current_instruction.getCondition().isWallAhead()
                                            +current_instruction.getCondition().getCellMark()  
                                            +current_instruction.getCondition().isNewArea());*/
    
        
      if(field_condition.equals(current_instruction.getCondition()))
      {
        if (mode_ != SINGLE_STEP)
        {
          Thread.sleep(delay_time_);
        }
        
        if (!performAction(current_instruction.getAction()))
        {
          finished_ = true;
          System.out.println("Roboter ist in die Wand gekracht!");
        } 
        else
        {  
          takeAndCalcDiamonds();
          num_steps_++;

          if (!checkCellIsStartpoint())
          {
            finished_ = true;
          }
        }
        component.repaint();
        condition_found = true;
        break;
      }
    }
    if(condition_found == false)
    {
      finished_ = true;
      System.out.println("es wurde keine Condition gefunden, " +
      "das Programm ist nicht vollständig");
    }
 }
  
//-----------------------------------------------------------------------------  
/**
 * method checks if the robot's position is the startpoint. 
 */  
  protected boolean checkCellIsStartpoint()
  {
    boolean cell_ok = true;
    int current_row = robot_.getPosRow();
    int current_col = robot_.getPosCol();
    if (lab_field_.isStartpoint(current_row, current_col) && (num_steps_ > 0))
    {
      cell_ok = false;
    }
    return cell_ok;
    
  }
/*  
  public void setSpeed(int delay_time)
  {
    delay_time_ = delay_time;
  }
*/
  
  public void setMode(int mode)
  {
    mode_ = mode;
    if (mode_ != SINGLE_STEP)
    {
      delay_time_ = mode;
    }
  }
  
  public int getNumSteps()
  {
    return num_steps_;
  }

  public int getNumTakenDiamonds()
  {
    return num_taken_diamonds_;
  }
  
  public boolean isFinished()
  {
    return finished_;
  }
  
  public void setNumNextSteps(int num_next_steps)
  {
    num_next_steps_ = num_next_steps;
  }
}

