
import java.util.ArrayList;
import java.util.Scanner;


// Define a "lowest common denominator"
interface AbsBallot {
  public void doVote();   // select yes/no on item
  public void tellVote(int lev);
}

// Item implements the "lowest common denominator"
// on ballot it is a single issue to vote yes/no on

class Issue implements AbsBallot {
  Scanner keybd = new Scanner(System.in);

  static int num=0;
  private int id;
  private String text = "issue";
  private boolean choice=false;
  
  public Issue(String nm) { id=++num; text=nm; }
  
  public void doVote() { 
    String x;
    System.out.print("issue: " + text + "? y/n "); 
    x = keybd.nextLine().toLowerCase(); 
    boolean c = (x.equals("y") || x.equals("yes") );
    cast(c);
  }
  
  public void cast(boolean c){
	  this.choice = c;
  }
  
  public boolean tally(){
	  return choice;
  }
  
  public void tellVote(int lev) { 
    for (int i=0; i<lev; i++) { System.out.print("   "); }
    System.out.println("issue: " + text + ", choice: "+tally()); 
  }
}



// Ballot implements the "lowest common denominator"
// collection of items, a single level of election
class Ballot implements AbsBallot {

  private static int num=0;
  private int id;
  private String text = "collection";
  private ArrayList issues = new ArrayList();

  public Ballot(String nm) { id=++num; text=nm; }
  
  public void add(Object obj) { issues.add(obj); }
  
  public void doVote() { 
    System.out.println("voting ballot: "+ text);
    for (int i = 0; i < issues.size(); ++i) {
      // Leverage the "lowest common denominator"
      AbsBallot bobj = (AbsBallot)issues.get(i); 
      bobj.doVote();
    }
  }  

  public void tellVote(int lev) {
    AbsBallot cobj;
    for (int i=0; i<lev; i++) { System.out.print("   "); }
    System.out.println("\nreporting ballot: " + text);
    for (int i = 0; i < issues.size(); ++i) {
      // Leverage the "lowest common denominator"
      cobj = (AbsBallot)issues.get(i);
      cobj.tellVote(lev+1);
    }
    System.out.println(" ");
  }    
}
 
class BallotFactory {
  // singleton pattern
  private static BallotFactory theOne=null; 
  
  private BallotFactory() { }
  
  public static BallotFactory makeTheOne() {  
    if (theOne==null) { theOne = new BallotFactory(); } 
    return theOne;
  }
  
  public AbsBallot genBallot(String type, String msg) {
    if (type=="leaf") {  return new Issue(msg); } 
    else { return new Ballot(msg); }
  }
    
}

abstract class Tally {
    protected Tally next;
    Tally(Tally next) {
        this.next = next;
    }    
    void doNext(AbsBallot c) {
        if(next != null) {
           next.handle(c);
        }
    }
    abstract void handle(AbsBallot c);
}

class IssueTally extends Tally {
    IssueTally(Tally next) {
        super(next);
    }
    void handle(AbsBallot c) {
        if(c instanceof Issue ){
         c.tellVote(0);
         }
        doNext(c);
    }
}

class BallotTally extends Tally {
    BallotTally(Tally next) {
        super(next);
    }    
    void handle(AbsBallot c) {
        if(c instanceof Ballot) {
            c.tellVote(0);
        }
        doNext(c);
    }
}


//================================================================
//  the app... the code using the pattern structured part
//================================================================

public class BallotComposApp1 {
  public static void main(String[] args) {
  
    BallotFactory bfac = BallotFactory.makeTheOne();
        
    Ballot b1 = (Ballot)bfac.genBallot("inner","Federal");
    Ballot b2 = (Ballot)bfac.genBallot("inner","State");
    Ballot b3 = (Ballot)bfac.genBallot("inner","Local");
    
    Issue f1 = (Issue)bfac.genBallot("leaf","Ben for US Senate");
    Issue f2 = (Issue)bfac.genBallot("leaf","Arlene for President");
    Issue f3 = (Issue)bfac.genBallot("leaf","Jim for US House");  
    b1.add(f1);
    b1.add(f2);
    b1.add(f3);
    
    Issue s1 = (Issue)bfac.genBallot("leaf","fishing limit 3 a day");
    Issue s2 = (Issue)bfac.genBallot("leaf","speed limit set to 55mph");
    b1.add(b2);
    b2.add(s1);
    b2.add(s2);
    
    Issue l1 = (Issue)bfac.genBallot("leaf","cell tower height limits");
    Issue l2 = (Issue)bfac.genBallot("leaf","bob for dog catcher");
    Issue l3 = (Issue)bfac.genBallot("leaf","school bond $2M");
    b3.add(l1);
    b3.add(l2);
    b3.add(l3);
    b2.add(b3);
    
    System.out.println("doing the vote");
    b1.doVote();
    
    
    Tally handler = new IssueTally(
                            new BallotTally(null
                            ));
                            
    System.out.println("Tally for issue\n");
    handler.handle(f1);
    System.out.println("Tally for ballot\n");
    handler.handle(b2);
    System.out.println("Tally for issue\n");
    handler.handle(s2);

    //System.out.println("\nreporting the vote");
    //b1.tellVote(0);
    
    //b2.reportVote();
  }
}