using System;
using System.Collections.Generic;
 
namespace DoFactory.GangOfFour.Command.RealWorld
{
	/// 
	/// MainApp startup class for Real-World 
	/// Command Design Pattern.
	/// 
  class MainApp
  {
    /// 
    /// Entry point into console application.
    /// 
    static void Main()
    {
      // Create user and let her compute
      User user = new User();
 
      // User presses calculator buttons
      user.Compute('+', 100);
      user.Compute('-', 50);
      user.Compute('*', 10);
      user.Compute('/', 2);
 
      // Undo 4 commands
      user.Undo(4);
 
      // Redo 3 commands
      user.Redo(3);
 
      // Wait for user
      Console.ReadKey();
    }
  }
 
  /// 
  /// The 'Command' abstract class
  /// 
  abstract class Command
  {
    public abstract void Execute();
    public abstract void UnExecute();
  }
 
  /// 
  /// The 'ConcreteCommand' class
  /// 
  class CalculatorCommand : Command
  {
    private char _operator;
    private int _operand;
    private Calculator _calculator;
 
    // Constructor
    public CalculatorCommand(Calculator calculator, char @operator, int operand)
    {
      this._calculator = calculator;
      this._operator = @operator;
      this._operand = operand;
    }
 
    // Gets operator
    public char Operator
    {
      set { _operator = value; }
    }
 
    // Get operand
    public int Operand
    {
      set { _operand = value; }
    }
 
    // Execute new command
    public override void Execute()
    {
      _calculator.Operation(_operator, _operand);
    }
 
    // Unexecute last command
    public override void UnExecute()
    {
      _calculator.Operation(Undo(_operator), _operand);
    }
 
    // Returns opposite operator for given operator
    private char Undo(char @operator)
    {
      switch (@operator)
      {
        case '+': return '-';
        case '-': return '+';
        case '*': return '/';
        case '/': return '*';
        default: throw new

         ArgumentException("@operator");
      }
    }
  }
 
  /// 
  /// The 'Receiver' class
  /// 
  class Calculator
  {
    private int _curr = 0;
 
    public void Operation(char @operator, int operand)
    {
      switch (@operator)
      {
        case '+': _curr += operand; break;
        case '-': _curr -= operand; break;
        case '*': _curr *= operand; break;
        case '/': _curr /= operand; break;
      }
      Console.WriteLine("Current value = {0,3} (following {1} {2})", 
						_curr, @operator, operand);
    }
  }
 
  /// 
  /// The 'Invoker' class
  /// 
  class User
  {
    // Initializers
    private Calculator _calculator = new Calculator();
    private List _commands = new List();
    private int _current = 0;
 
    public void Redo(int levels)
    {
      Console.WriteLine("\n---- Redo {0} levels ", levels);
      // Perform redo operations

      for (int i = 0; i < levels; i++)
      {
        if (_current < _commands.Count - 1)
        {
          Command command = _commands[_current++];
          command.Execute();
        }
      }
    }
 
    public void Undo(int levels)
    {
      Console.WriteLine("\n---- Undo {0} levels ", levels);
      // Perform undo operations

      for (int i = 0; i < levels; i++)
      {
        if (_current > 0)
        {
          Command command = _commands[--_current] as Command;
          command.UnExecute();
        }
      }
    }
 
    public void Compute(char @operator, int operand)
    {
      // Create command operation and execute it
      Command command = new CalculatorCommand(_calculator,@operator,operand);
      command.Execute();
 
      // Add command to undo list
      _commands.Add(command);
      _current++;
    }
  }
}