package counter;



import logic.*;



public class CounterFactory implements ICounterFactory {

  

  public static final CounterFactory Singleton = new CounterFactory();

  private CounterFactory() {}

  

  private static abstract class ACounterState {

    abstract ICounter decrement();    

    abstract ICounter increment();

    abstract Object execute(ICounterAlgo algo, Object... param);

  }

  

  

  public ICounter makeCounter(final int aCount) {

    return new ICounter() {

      private IBooleanAlgo setCountAlgo = new IBooleanAlgo() {

        public Object trueCase(IBoolean host, Object... param) {

          counterState = zeroState; 

          return null;

        }

        public Object falseCase(IBoolean host, Object... param) {

          counterState = nonZeroState;

          return null;

        }

      };

            

      private ICounter thisCounter = this; // hack to get self-reference

      

      private ACounterState zeroState = new ACounterState() {

        ICounter decrement() {

          count = -1;

          counterState = nonZeroState;

          return thisCounter;

        }

        

        ICounter increment(){

          count = 1;

          counterState = nonZeroState;

          return thisCounter;

        }

        

        Object execute(ICounterAlgo algo, Object... param) {

          return algo.zeroCase(thisCounter,param);

        }

      };

      

      private ACounterState nonZeroState = new ACounterState() {

        ICounter decrement() {

          return setCount(--count);

        }

        

        ICounter increment(){

          count++;

          return thisCounter;

        }

        

        Object execute(ICounterAlgo algo, Object... param) {

          return algo.nonZeroCase(thisCounter,param);

        }

      };  

      

      private int count = aCount;

      private ACounterState counterState = zeroState;

      

      /**

       * Initializer block

       * Must come after the fields are initialized so to avoid null ptr errors.

       */

      {

        setCount(aCount); // initialize the counter

      }



      

      public ICounter decrement() {

        return counterState.decrement();

      }

      

      public ICounter increment() {

        return counterState.increment();

      }

      

      public int getCount() {

        return count;

      }

      

      public ICounter setCount(int newCount) { 

        count = newCount;

        BooleanFactory.Singleton.makeBoolean(0==count).execute(setCountAlgo);

        return this;

      }

      

      public Object execute(ICounterAlgo algo, Object... param) {

        return counterState.execute(algo, param);

      }

      

    };    

  }

}