package Matrix2P;

import Common.GraphFrame;

import javax.swing.*;
import java.util.ArrayList;
import java.awt.*;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;

/**
 * Author: Max Buegler - max@sarstech.com
 * Date: Sep 28, 2009
 * Time: 5:44:26 PM
 */
public class FictitiousPlay implements GameAlgorithm{
    private int firstActionPl1, firstActionPl2, stepcount;
    private boolean stop=false;
    public String getDescriptor() {
        return "FictitiousPlay [n="+stepcount+" a=("+(firstActionPl1+1)+","+(firstActionPl2+1)+") ]";
    }

    public void settings(ActionListener ul) {
        final ActionListener ul2=ul;
        final JFrame settingsFrame=new JFrame("Fictitious play settings");
                settingsFrame.setLayout(new GridBagLayout());
                GridBagConstraints cons=new GridBagConstraints();
                cons.weightx=0.5;
                cons.weighty=0.5;
                cons.gridx=0;
                cons.gridy=0;
                cons.gridheight=1;
                cons.gridwidth=1;
                cons.fill=GridBagConstraints.HORIZONTAL;
                JPanel aboutPanel=new JPanel();
                aboutPanel.setBorder(BorderFactory.createCompoundBorder(
                                BorderFactory.createTitledBorder("About"),
                                BorderFactory.createEmptyBorder(5,5,5,5)));
                    aboutPanel.setLayout(new GridLayout(1,1));
                    JTextArea about=new JTextArea();
                    about.setEditable(false);
                    about.setCursor(null);
                    about.setOpaque(false);
                    about.setFocusable(false);
                    about.setLineWrap(true);
                    about.setText("Performs a Fictitious Play where players play based on observations.");
                    aboutPanel.add(about);
                settingsFrame.add(aboutPanel,cons);

                JPanel settingsPanel=new JPanel();
                    settingsPanel.setLayout(new GridLayout(4,2));
                    settingsPanel.add(new JLabel("1st action pl 1"));
                    final JTextField a1=new JTextField((firstActionPl1+1)+"");
                    settingsPanel.add(a1);
                    settingsPanel.add(new JLabel("1st action pl 2"));
                    final JTextField a2=new JTextField((firstActionPl2+1)+"");
                    settingsPanel.add(a2);
                    settingsPanel.add(new JLabel("Number of games"));
                    final JTextField g=new JTextField(stepcount+"");
                    settingsPanel.add(g);

                    JPanel button1=new JPanel();
                        button1.setLayout(new GridLayout(1,2));
                        JButton ok=new JButton("OK");
                        ok.addActionListener(new ActionListener(){
                            public void actionPerformed(ActionEvent e) {
                                try{
                                    firstActionPl1=Integer.parseInt(a1.getText())-1;
                                    if (firstActionPl1<0)throw new Exception();
                                }catch(Exception ex){
                                    JOptionPane.showMessageDialog(settingsFrame,"Please enter a positive integer value for '1st Action pl 1'");
                                    return;
                                }
                                try{
                                    firstActionPl2=Integer.parseInt(a2.getText())-1;
                                    if (firstActionPl2<0)throw new Exception();
                                }catch(Exception ex){
                                    JOptionPane.showMessageDialog(settingsFrame,"Please enter a positive integer value for '1st Action pl 2'");
                                    return;
                                }
                                try{
                                    stepcount=Integer.parseInt(g.getText());
                                }catch(Exception ex){
                                    JOptionPane.showMessageDialog(settingsFrame,"Please enter a positive integer value for 'Number of games'");
                                    return;
                                }
                                ul2.actionPerformed(null);
                                settingsFrame.setVisible(false);
                            }
                        });
                        button1.add(new JLabel());
                        button1.add(ok);
                        settingsPanel.add(button1);
                    JPanel button2=new JPanel();
                        button2.setLayout(new GridLayout(1,2));
                        JButton cancel=new JButton("Cancel");
                        cancel.addActionListener(new ActionListener(){
                            public void actionPerformed(ActionEvent e) {
                                settingsFrame.setVisible(false);
                            }
                        });
                        button2.add(cancel);
                        button2.add(new JLabel());
                        settingsPanel.add(button2);
                cons=new GridBagConstraints();
                cons.weightx=0.5;
                cons.weighty=0.5;
                cons.gridx=0;
                cons.gridy=1;
                cons.gridheight=3;
                cons.gridwidth=1;
                cons.fill=GridBagConstraints.HORIZONTAL;
                settingsFrame.add(settingsPanel,cons);



                settingsFrame.setSize(400,400);
                settingsFrame.setVisible(true);

    }

    public FictitiousPlay() {
        firstActionPl1=0;
        firstActionPl2=0;
        stepcount=1000;
    }

    public void stop() {
        stop=true;
    }

    public void run(GameMatrix m,ActionListener fl){
        stop=false;
        GraphFrame p1f=new GraphFrame("Player 1 FictitiousPlay",stepcount);
        GraphFrame p2f=new GraphFrame("Player 2 FictitiousPlay",stepcount);
        p1f.setLocation(0,0);
        p2f.setLocation(0,300);
        ArrayList<FictitiousStep> steps=new ArrayList<FictitiousStep>();
        steps.add(new FictitiousStep(firstActionPl1,firstActionPl2,new double[m.getActionsP1()],new double[m.getActionsP2()]));
        double payoffp1=m.getP1()[firstActionPl1][firstActionPl2];
        double payoffp2=m.getP2()[firstActionPl1][firstActionPl2];
        System.out.println("P1Payoff sum: "+payoffp1);
        System.out.println("P2Payoff sum: "+payoffp2);
        System.out.println();

        for (int x=0;x<stepcount;x++){
            if (stop){
                fl.actionPerformed(null);
                return;
            }
            System.out.println("Step "+(x+1));
            double[] op1=new double[m.getActionsP1()];
            double[] op2=new double[m.getActionsP2()];
            for (int s=0;s<steps.size();s++){
                op1[steps.get(s).getP1()]++;
                op2[steps.get(s).getP2()]++;
            }
            for (int a=0;a<m.getActionsP1();a++)op1[a]/=steps.size();
            for (int a=0;a<m.getActionsP2();a++)op2[a]/=steps.size();
            int maxap1=0;
            double maxExpp1=Integer.MIN_VALUE;
            for (int a=0;a<m.getActionsP1();a++){
                double exp=0;
                for (int a2=0;a2<m.getActionsP2();a2++){
                    exp+=m.getP1()[a][a2]*op2[a2];
                }
                exp/=m.getActionsP2();
                if (exp>maxExpp1){
                    maxExpp1=exp;
                    maxap1=a;
                }
            }
            int maxap2=0;
            double maxExpp2=Integer.MIN_VALUE;
            for (int a=0;a<m.getActionsP2();a++){
                double exp=0;
                for (int a2=0;a2<m.getActionsP1();a2++){
                    exp+=m.getP2()[a2][a]*op1[a2];
                }
                exp/=m.getActionsP1();
                if (exp>maxExpp2){
                    maxExpp2=exp;
                    maxap2=a;
                }
            }
            steps.add(new FictitiousStep(maxap1,maxap2,op1,op2));
            p1f.addData(op1);
            p2f.addData(op2);
            if(x%20==0){
                p1f.repaint();
                p2f.repaint();
            }
            payoffp1+=m.getP1()[maxap1][maxap2];
            payoffp2+=m.getP2()[maxap1][maxap2];
            System.out.println("P1Payoff sum: "+payoffp1);
            System.out.println("P2Payoff sum: "+payoffp2);
            System.out.println();

        }
        fl.actionPerformed(null);

    }
    class FictitiousStep{
        private int p1,p2;
        private double[] op1,op2;

        FictitiousStep(int p1, int p2, double[] op1, double[] op2) {
            this.p1 = p1;
            this.p2 = p2;
            this.op1 = op1;
            this.op2 = op2;
            System.out.print("P1Action: "+(p1+1)+" P1Observation (");
            for (int x=0;x<op1.length;x++){
                System.out.print(op1[x]);
                if (x<op1.length-1)System.out.print(", ");
            }
            System.out.println(") ");
            System.out.print("P2Action: "+(p2+1)+" P2Observation (");
            for (int x=0;x<op2.length;x++){
                System.out.print(op2[x]);
                if (x<op2.length-1)System.out.print(", ");
            }
            System.out.println(") ");
        }

        public int getP1() {
            return p1;
        }

        public int getP2() {
            return p2;
        }

        public double[] getOp1() {
            return op1;
        }

        public double[] getOp2() {
            return op2;
        }
    }

}
