import java.awt.*;
import java.awt.event.*;
import java.util.Random;

class MMCanvas extends Canvas
    implements MouseListener, ActionListener, ItemListener
{
  int myCols=4;
  int myPoss=6;
  int myWidth=30;
  int myHeight=30;
  int myCount=10;

  public MMCanvas(Label inLabel) {
    myMessageLabel=inLabel;
    myGuesses=new int[myCols][myCount];
    myGrades=new int[2][myCount];
    myWork=new boolean[myPoss][myPoss][myPoss][myPoss];
    myCode=new int[myCols];
    myCB=new Checkbox[myPoss+1];

    myBoldFont=new Font("Dialog",Font.BOLD,14);
    myBigFont=new Font("Dialog",Font.PLAIN,18);
    myColors=new Color[myPoss+2];
    myColors[0]=Color.gray;
    myColors[1]=Color.red;
    myColors[2]=Color.pink;
    myColors[3]=Color.yellow;
    myColors[4]=Color.green;
    myColors[5]=Color.blue;
    myColors[6]=Color.magenta;
    myColors[7]=Color.black;
    myColor=1;

    myStoCol=new String[myPoss];
    myStoCol[0]="R";
    myStoCol[1]="O";
    myStoCol[2]="Y";
    myStoCol[3]="G";
    myStoCol[4]="B";
    myStoCol[5]="P";

    addMouseListener(this);
    init();
  }

  private void init()  {
    int i1,i2,i3,i;
    for(i=0;i<myPoss;i++)
      for(i1=0;i1<myPoss;i1++)
        for(i2=0;i2<myPoss;i2++)
          for(i3=0;i3<myPoss;i3++) 
            myWork[i][i1][i2][i3]=true;            

    myTurn=0;
    myBOHasWon=false;
    setMessage(null);

    // Choose new code
    Random r=new Random();
    for(i=0;i<myCols;i++) {
      myCode[i]=r.nextInt()%myPoss;
      if(myCode[i]<0) 
        myCode[i]=-myCode[i];
      myCode[i]++;
    }


    for(i=0;i<myCols;i++) {
	for(i1=0;i1<myCount;i1++)
        myGuesses[i][i1]=0;		
    }
  }

  private void grade()  {
    int aCode[]=new int[myCols];
    int theCode[]=new int[myCols];
    int correct=0;
    int wrong=0;

    for(int i=0;i<myCols;i++) {
      aCode[i]=myGuesses[i][myTurn];
      theCode[i]=myCode[i];

      // Check for exact matches
      if(aCode[i]==theCode[i]) {
        correct++;
        aCode[i]=-1;
        theCode[i]=-2;
      }
    }

    // Check for matches in different places
    for(int i=0;i<myCols;i++) {
      for(int j=0;j<myCols;j++) {
        if(aCode[i]==theCode[j]) {
          wrong++;
          aCode[i]=-1;
          theCode[j]=-2;
        }
      }
    }

    if(correct==myCols) {
      myBOHasWon=true;
      setMessage("Won!");
    }

    myGrades[0][myTurn]=wrong;
    myGrades[1][myTurn]=correct;
  }

  private void countValues() {
    boolean result;
    boolean hinted=false;
    int sum=0;
    int i,i1,i2,i3;
    int oCode[]=new int[4];

    ListFrame f=null;
    if(myBOListPoss)
      f=new ListFrame();

    for(i=0;i<myPoss;i++) {
      oCode[0]=i+1;
      for(i1=0;i1<myPoss;i1++) {
        oCode[1]=i1+1;
        for(i2=0;i2<myPoss;i2++) {
          oCode[2]=i2+1;
          for(i3=0;i3<myPoss;i3++) {
            if(myWork[i][i1][i2][i3]) {
              oCode[3]=i3+1;
              result=tryGuess(oCode);
              myWork[i][i1][i2][i3]=result;
              if(result) {
                if(!myBOHasWon && !hinted) {
		      setMessage("Hint:  "+myStoCol[i]+myStoCol[i1]+
		          myStoCol[i2]+myStoCol[i3]);  
			hinted=true;

		      if(!myBOShowCount && !myBOListPoss)
			  return;
                }

                if(myBOListPoss) {
                  f.addItem(myStoCol[i]+myStoCol[i1]+myStoCol[i2]+myStoCol[i3]);
                  //System.out.println(myStoCol[i]+myStoCol[i1]+myStoCol[i2]+myStoCol[i3]);
                }

                sum++;
              }
            }
          }
        }
      }
    }

    if(myBOShowCount) {
      if(myMessage==null)
        setMessage("Possible:"+sum);
      else
        setMessage("Possible:"+sum+"  "+myMessage);
    }
  }

  private void setMessage(String inMessage)  {
     myMessage=inMessage;
     myMessageLabel.setText(inMessage);
     Container c=myMessageLabel.getParent();

     if(c!=null)
        c.layout();
  }

  private boolean tryGuess(int aCode[])  {
    int bCode[]=new int[myCols];
    int cCode[]=new int[myCols];
    int correct=0;
    int wrong=0;

    for(int t=0;t<myTurn;t++) {
      // get expendable copies
      correct=myGrades[1][t];
      wrong=myGrades[0][t];

      for(int i=0;i<myCols;i++) {
        bCode[i]=aCode[i];
        cCode[i]=myGuesses[i][t];

        // compare exact
        if(bCode[i]==cCode[i]) {
          correct--;
          if(correct<0)
            return false;
          bCode[i]=-1;
          cCode[i]=-2;
        }
      }
      if(correct!=0)
        return false;

      // compare right values
      for(int i=0;i<myCols;i++) {
        for(int j=0;j<myCols;j++) {
          if(bCode[i]==cCode[j]) {
            wrong--;
            if(wrong<0)
              return false;
            bCode[i]=-1;
            cCode[j]=-2;
          }
        }
      }
      if(wrong!=0)
        return false;
    }
    return true;
  }

  public void setCB(Checkbox cb, int i) {
    myCB[i]=cb;
  }

  public void paint(Graphics g) {
     // draw header
     g.setFont(myBoldFont);
     g.drawString("Guess", 0, myHeight*3/4);
     g.drawString("Wrong Place", myWidth*7/2, myHeight*3/4);
     g.drawString("Correct", myWidth*7, myHeight*3/4);

     // paint guesses
     int i,i1;
     for(i=0;i<myCols;i++) {
  	  for(i1=0;i1<myCount;i1++) {
	     g.setColor(myColors[myGuesses[i][i1]]);
	     g.fillOval(i*myWidth,(i1+1)*myHeight,myWidth-2,myHeight-2);
	  }
     }

     // paint grades
     g.setColor(myColors[myPoss+1]);
//    g.setFont(myBoldFont);
     for(i=0;i<myTurn;i++) {
	  g.drawString(Integer.toString(myGrades[0][i]),myWidth*6,(i*2+3)*myHeight/2);
	  g.drawString(Integer.toString(myGrades[1][i]),myWidth*7,(i*2+3)*myHeight/2);
     }

     // paint selected row
     g.drawString("<",myWidth*4,(2*myTurn+3)*myHeight/2);

     // paint message
/*
     if(myMessage!=null) {
        g.setFont(myBigFont);
        g.drawString(myMessage,myWidth*2,(2*myCount+3)*myHeight/2);
     }
*/
  }

  public void mouseExited( MouseEvent event ) {
  }

  public void mouseClicked( MouseEvent event ) {
  }

  public void mouseEntered( MouseEvent event ) {
  }

  public void mousePressed( MouseEvent event ) {
    int x=event.getX()/myWidth;
    int y=event.getY()/myHeight-1;

    if(y==myTurn && x>-1 && x<myCols) {
      myGuesses[x][y]=myColor;
      invalidate();
      repaint();
    }
  }

  public void mouseReleased( MouseEvent event ) {
  }


  public void actionPerformed(ActionEvent e) {
    String s=e.getActionCommand();

    if(s.equals("Enter")) {
      // Enter key
      if(myBOHasWon)
        setMessage("Won!");
      else {
	  boolean finished=true;
	  for(int i=0;i<myCols && finished;i++) {
	    if(myGuesses[i][myTurn]==0)
	      finished=false;
	  }

	  if(!finished)
	    setMessage("Finish your turn first");
	  else if(myTurn<myCount) {
          setMessage(null);
          grade();
          myTurn++;

          if(!myBOHasWon && (myBOShowCount || myBOHints || myBOListPoss)) {
            countValues();
          }
        }
        else  {
          setMessage("Lost!");
        }  
      }

      invalidate();
      repaint();
    }
    else if(s.equals("Restart")) {
      init();
      invalidate();
      repaint();
    }
    else
System.out.println(s+":"+e);
  }

  public void itemStateChanged(ItemEvent e) {
    Object o=e.getSource();

    if(o instanceof Checkbox) {
      for(int i=0;i<=myPoss;i++) {
        if(o==myCB[i]) {
          myColor=i;
          return;
        }
      }
    }
    else if(o instanceof CheckboxMenuItem) {
      String s=(String)e.getItem();
      CheckboxMenuItem cmi=(CheckboxMenuItem)o;
      boolean value=cmi.getState();

      if(s=="Hints")
        myBOHints=value;
      else if(s=="List Possibilities")
        myBOListPoss=value;
      else if(s=="Show Count")
        myBOShowCount=value;
    }
  }

  public Dimension getPreferredSize() {
     Dimension d=super.getPreferredSize();

     if(d.height<350)
        d.height=350;

     if(d.width<270) {
        d.width=270;
     }

     return d;
  }

  public Dimension getMinimumSize() {
     Dimension d=super.getMinimumSize();

     if(d.height<350)
        d.height=350;

     if(d.width<270) {
        d.width=270;
     }

     return d;
  }

  private int myTurn;
  private int myGuesses[][];
  private int myGrades[][];
  private boolean myWork[][][][];
  private int myCode[];
  private Color myColors[];
  private int myColor;
  private Font myBoldFont;
  private Font myBigFont;
  private String myMessage;
  private String myStoCol[];
  private Checkbox myCB[];
  private Label myMessageLabel;

  private boolean myBOHints=false;
  private boolean myBOShowCount=true;
  private boolean myBOListPoss=false;
  private boolean myBOHasWon=false;
}