/*
* 状态模式
* 允许对象在内部状态改变时改变它的行为,
* 对象看起来好像修改了它的类
*/

public class StatePettern {

    public static void main(String[] args) {
  GumballMachine gumballMachine = new GumballMachine(6);
  
  System.out.println(gumballMachine);
  
  gumballMachine.insterQuarter();
  gumballMachine.turnCrank();
  
  System.out.println(gumballMachine);
  
  gumballMachine.insterQuarter();
  gumballMachine.turnCrank();
  gumballMachine.insterQuarter();
  gumballMachine.turnCrank();
  
  System.out.println(gumballMachine);
    }

}

//管家类,利用组合,将每个状态实现为一个类
class GumballMachine {
    State soldOutState;
    State noQuarterState;
    State hasQuarterState;
    State soldState;

    State state = soldOutState;
    int count = 0;

    public GumballMachine(int numberGumballs) {
  soldOutState = new SoldOutState(this);
  noQuarterState = new NoQuarterState(this);
  hasQuarterState = new HasQuarterState(this);
  soldState = new SoldState(this);

  this.count = numberGumballs;
  if (numberGumballs > 0) {
      state = noQuarterState;
  }
    }

    public void insterQuarter() {
  state.insertQuarter();
    }

    public void ejectQuarter() {
  state.ejectQuarter();
    }

    public void turnCrank() {
  state.turnCrank();
  state.dispense();
    }

    public void setState(State state) {
  this.state = state;
    }

    void releaseBall() {
  System.out.println("A gumball comes rolling out the slot....");
  if (count != 0) {
      count = count - 1;
  }
    }

    public State getNoQuarterState() {
  return noQuarterState;
    }

    public State getHasQuarterState() {
  return hasQuarterState;
    }

    public State getSoldState() {
  return soldState;
    }

    public State getSoldOutState() {
  return soldOutState;
    }

    public int getCount() {
  return count;
    }
  
    public String toString() {  
  return "GumballMachine has " + count +" gumballs left";
    }
}

//状态的抽象类,包含了各个状态的动作
interface State {
    void insertQuarter();

    void ejectQuarter();

    void turnCrank();

    void dispense();
}

//各个状态的具体实现,注意各个状态之间的转化,在不同的状态下,相同的动作会有不同的实现
//每个具体类都包含了一个管家类
class NoQuarterState implements State {
    GumballMachine gumballMachine;

    public NoQuarterState(GumballMachine gumballMachine) {
  this.gumballMachine = gumballMachine;
    }

    public void insertQuarter() {
  System.out.println("You inserted a quarter");
  gumballMachine.setState(gumballMachine.getHasQuarterState());
    }

    public void ejectQuarter() {
  System.out.println("You haven't inserted a quarter");
    }

    public void turnCrank() {
  System.out.println("You turned,but there's no quarter");
    }

    public void dispense() {
  System.out.println("You need to pay first");
    }
}

class HasQuarterState implements State {
    GumballMachine gumballMachine;

    public HasQuarterState(GumballMachine gumballMachine) {
  this.gumballMachine = gumballMachine;
    }

    public void insertQuarter() {
  System.out.println("You can't insert another quarter");
    }

    public void ejectQuarter() {
  System.out.println("Quarter returned");
  gumballMachine.setState(gumballMachine.getNoQuarterState());
    }

    public void turnCrank() {
  System.out.println("You turned....");
  gumballMachine.setState(gumballMachine.getSoldState());
    }

    public void dispense() {
  System.out.println("No gumball dispensed");
    }

}

class SoldState implements State {
    GumballMachine gumballMachine;

    public SoldState(GumballMachine gumballMachine) {
  this.gumballMachine = gumballMachine;
    }

    public void insertQuarter() {
  System.out.println("Please wait,we're already giving you a gumball");
    }

    public void ejectQuarter() {
  System.out.println("Sorry,you already turned the crank");
    }

    public void turnCrank() {
  System.out.println("Turning twice doesn't get you anther gumball!");
    }

    public void dispense() {
  gumballMachine.releaseBall();
  if (gumballMachine.getCount() > 0) {
      gumballMachine.setState(gumballMachine.getNoQuarterState());
  } else {
      System.out.println("Oops,out of gumballs");
      gumballMachine.setState(gumballMachine.getSoldOutState());
  }
    }
}

class SoldOutState implements State {
    GumballMachine gumballMachine;

    public SoldOutState(GumballMachine gumballMachine) {
  this.gumballMachine = gumballMachine;
    }

    public void insertQuarter() {
  System.out.println("You can't insert a quarter,the mechine is sold out");
    }

    public void ejectQuarter() {
  System.out.println("You haven't inserted a quarter");
    }

    public void turnCrank() {
  System.out.println("You turned,but there's no quarter");
    }

    public void dispense() {
  System.out.println("No gumball dispensed");
    }
}