package montecarlo;

import java.util.Random;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

public class Markov {
    
    private int nStates;       //number of states
    double[][] p;    //transition-probability matrix
    double[][] pnew;
    double[][] Q;    //cumulative transition-probability matrix
    public Histogram histogram;
    private Random random = new Random();
    private int currentState = 0;
    private double[][] p3 = {{0.1,0.5,0.4},{0.9,0.1,0.0},{0.4,0.2,0.4}};
    
    public Markov() {
        this(3);     //default
//        p[0][0]=0.1; p[0][1]=0.5; p[0][2]=0.4; 
//        p[1][0]=0.9; p[1][1]=0.1; p[1][2]=0.0;
//        p[2][0]=0.4; p[2][1]=0.1; p[2][2]=0.4;
        computeQ();
    }
    public Markov(int n) {
        setNStates(n);
    }
    
    void computeQ() {     //computes cumulative transition-probability matrix
        for(int i=0; i<nStates; i++) {
            Q[i][0] = p[i][0];
            for(int j=1; j<nStates; j++) {Q[i][j] = Q[i][j-1] + p[i][j];}
        }
    }
    
    public double[][] getP() {return p;}
    
    public void setCurrentState(int k) {currentState = k;}
    public int getCurrentState() {return currentState;}
    
    public int getNStates() {return nStates;}
    public void setNStates(int n) {
        nStates = n;
        p = new double[n][n];
        pnew = new double[n][n];
        Q = new double[n][n];
        for(int i=0; i<n; i++) {
            for(int j=0; j<n; j++) {
                p[i][j] = (n==3) ? p3[i][j] : 1.0/(double)n;  //special default for nStates=3
                pnew[i][j] = p[i][j];
            }
        }
        computeQ();
        histogram = new Histogram(n,0.5,(double)n+0.5);
        currentState = 0;
    }
    
    public void doStep() {
        double r = random.nextDouble();
        for(int i=0; i<nStates; i++) {
            if(r < Q[currentState][i]) {
                currentState = i;
                break;
            }
        }
        histogram.addValue((double)currentState+1.0);
    }
    
    /**
     * Radiobutton list indicating current state of Markov chain.  
     * Call update method to cause display to reflect current state
     * Selection of radio button changes state to chosen value
     */
    public class StateDisplay extends Panel implements ItemListener {
        
        Checkbox[] buttons;
        CheckboxGroup group = new CheckboxGroup();
        
        public StateDisplay() {
            setupDisplay(nStates);
		    setLayout(new GridLayout(10,1,0,0));
        }
        
        public void setupDisplay(int n) {
            this.removeAll();
            buttons = new Checkbox[n];  
            for(int i=0; i<n; i++) {
                buttons[i] = new Checkbox();
		        buttons[i].setCheckboxGroup(group);
		        buttons[i].setLabel("State "+Integer.toString(i+1));
		        this.add(buttons[i]);
		        buttons[i].addItemListener(this);
		    }
		    update();
		    doLayout();
		}
		
		public void update() {buttons[currentState].setState(true);}
		
		public void itemStateChanged(ItemEvent evt) {   //event handler to cause selection of button to change state
		    if(evt.getStateChange() != ItemEvent.SELECTED) return;
		    Checkbox box = (Checkbox)evt.getItemSelectable();
		    for(int i=0; i<nStates; i++) {
		        if(box == buttons[i]) {
		            currentState = i;
		            System.out.println("new state " + i);
		            return;
		        }
		    }
		}
	}
     
    /**
     * Tabular, editable display of transition probabilities for Markov process
     */
    public class Matrix extends Panel {
        MyTextField[][] fields;
        Panel matrixPanel = new Panel();
        Panel buttonPanel = new Panel();
        Button resetButton = new Button("Revert to last values");
        Button acceptButton = new Button("Accept new values");
        public Matrix() {
            fields = new MyTextField[nStates][nStates];
            for(int i=0; i<nStates; i++) {
                for(int j=0; j<nStates; j++) {
                    fields[i][j] = new MyTextField(i,j,p[i][j]);
                    matrixPanel.add(fields[i][j]);
                }
            }
		    matrixPanel.setLayout(new GridLayout(nStates,nStates,0,0));
		    resetButton.setBackground(new java.awt.Color(204,204,153));
		    acceptButton.setBackground(new java.awt.Color(204,204,153));
		    buttonPanel.add(resetButton);
		    buttonPanel.add(acceptButton);
//		    buttonPanel.setLayout(new GridLayout(1,2,0,0));
		    setLayout(new BorderLayout(5,5));
		    add(matrixPanel,"North");
		    add(buttonPanel,"South");
		    resetButton.setVisible(false);
		    acceptButton.setVisible(false);
		    resetButton.addActionListener(new ActionListener() {
		        public void actionPerformed(java.awt.event.ActionEvent event) {Matrix.this.revert();}
		    });
		    acceptButton.addActionListener(new ActionListener() {
		        public void actionPerformed(java.awt.event.ActionEvent event) {Matrix.this.accept();}
		    });
        }
        
        public void accept() {
            for(int i=0; i<nStates; i++) {
                for(int j=0; j<nStates; j++) {
                    p[i][j] = pnew[i][j];
                }
            }
            computeQ();
            for(int i=0; i<nStates; i++) {  //renormalize
                for(int j=0; j<nStates; j++) {
                    p[i][j] /= Q[i][nStates-1];
                    fields[i][j].setText((new Float(p[i][j])).toString());
                }
            }
            computeQ();
            acceptButton.setVisible(false);
            resetButton.setVisible(false);
            histogram.reset();
            getParent().doLayout();
        }
        public void revert() {
            System.out.println("inside revert");
            for(int i=0; i<nStates; i++) {
                for(int j=0; j<nStates; j++) {
                    pnew[i][j] = p[i][j];
                    fields[i][j].setText((new Float(p[i][j])).toString());
                }
            }
            acceptButton.setVisible(false);
            resetButton.setVisible(false);
            repaint();
//            getParent().doLayout();
        }
            
    }
    
    /**
     * Textfield class used by Matrix
     * Changes awt.TextField to process editing of values
     */
    class MyTextField extends TextField implements TextListener {
        int i,j;
        double myP;
        MyTextField(int k1, int k2, double value) {
            i = k1; j=k2;
            myP = value;
            setText((new Float(value)).toString());
    		addTextListener(this);
		}
		
		public void textValueChanged(TextEvent event) {  //event handler for change of text value
		    double myP = Double.valueOf(this.getText()).doubleValue();
		    if(myP < 0.0 ) {//|| myP > 1.0) {
		        myP = Math.max(0.0,myP);
 //   		    myP = Math.min(1.0,myP);
                setText((new Float(myP)).toString());
            }
            pnew[i][j] = myP;
            ((Matrix)getParent().getParent()).resetButton.setVisible(true);
            ((Matrix)getParent().getParent()).acceptButton.setVisible(true);
            getParent().getParent().getParent().doLayout();  //matrixPanel.matrix.parent
		}
    }
}