package Matrix3P;

import Common.GraphFrame;

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

/**
 * Author: Max Buegler - max@sarstech.com
 * Date: Oct 6, 2009
 * Time: 11:34:00 AM
 */
public class RegretMatching3P implements GameAlgorithm3P{
    private int firstActionPl1, firstActionPl2,firstActionPl3, stepcount;
    private double cp1,cp2,cp3,e;
    private boolean stop=false;

    public String getDescriptor() {
        return "Regret Matching [n="+stepcount+"  a=("+(firstActionPl1+1)+","+(firstActionPl2+1)+","+(firstActionPl3+1)+")   c=("+cp1+","+cp2+","+cp2+")  e="+e+" ]";
    }

    public void stop() {
        stop=true;
    }

    public void settings(ActionListener ul) {
        final ActionListener ul2=ul;
        final JFrame settingsFrame=new JFrame("Regret Matching 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("Plays using regret matching.");
                    aboutPanel.add(about);
                settingsFrame.add(aboutPanel,cons);

                JPanel settingsPanel=new JPanel();
                    settingsPanel.setLayout(new GridLayout(9,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("1st action pl 3"));
                    final JTextField a3=new JTextField((firstActionPl3+1)+"");
                    settingsPanel.add(a3);
                    settingsPanel.add(new JLabel("Number of games"));
                    final JTextField g=new JTextField(stepcount+"");
                    settingsPanel.add(g);
                    settingsPanel.add(new JLabel("c value for pl 1"));
                    final JTextField c1=new JTextField(cp1+"");
                    settingsPanel.add(c1);
                    settingsPanel.add(new JLabel("c value for pl 2"));
                    final JTextField c2=new JTextField(cp2+"");
                    settingsPanel.add(c2);
                    settingsPanel.add(new JLabel("c value for pl 3"));
                    final JTextField c3=new JTextField(cp3+"");
                    settingsPanel.add(c3);
                    settingsPanel.add(new JLabel("Epsilon for sampler"));
                    final JTextField ep=new JTextField(e+"");
                    settingsPanel.add(ep);

                    JPanel button1=new JPanel();
                        button1.setLayout(new GridLayout(1,2));
                        JButton ok=new JButton("OK");
                        ok.addActionListener(new ActionListener(){
                            public void actionPerformed(ActionEvent ae) {
                                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{
                                    firstActionPl3=Integer.parseInt(a2.getText())-1;
                                    if (firstActionPl3<0)throw new Exception();
                                }catch(Exception ex){
                                    JOptionPane.showMessageDialog(settingsFrame,"Please enter a positive integer value for '1st Action pl 3'");
                                    return;
                                }

                                try{
                                    stepcount=Integer.parseInt(g.getText());
                                    if (stepcount<0)throw new Exception();
                                }catch(Exception ex){
                                    JOptionPane.showMessageDialog(settingsFrame,"Please enter a positive integer value for 'Number of games'");
                                    return;
                                }
                                try{
                                    cp1=Double.parseDouble(c1.getText());
                                    if (cp1<0||cp1>1)throw new Exception();
                                }catch(Exception ex){
                                    JOptionPane.showMessageDialog(settingsFrame,"Please enter a positive real value between 0 and 1 for 'c value for pl 1'");
                                    return;
                                }
                                try{
                                    cp2=Double.parseDouble(c2.getText());
                                    if (cp2<0||cp2>1)throw new Exception();
                                }catch(Exception ex){
                                    JOptionPane.showMessageDialog(settingsFrame,"Please enter a positive real value between 0 and 1 for 'c value for pl 2'");
                                    return;
                                }
                                try{
                                    cp3=Double.parseDouble(c2.getText());
                                    if (cp3<0||cp3>1)throw new Exception();
                                }catch(Exception ex){
                                    JOptionPane.showMessageDialog(settingsFrame,"Please enter a positive real value between 0 and 1 for 'c value for pl 3'");
                                    return;
                                }

                                try{
                                    e=Double.parseDouble(ep.getText());
                                    if (e<0||e>1)throw new Exception();
                                }catch(Exception ex){
                                    JOptionPane.showMessageDialog(settingsFrame,"Please enter a positive real value between 0 and 1 for 'Epsilon for sampler'");
                                    return;
                                }
                                settingsFrame.setVisible(false);
                                ul2.actionPerformed(null);
                            }
                        });
                        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);

    }

    private Random rnd;
    public RegretMatching3P(){
        firstActionPl1=0;
        firstActionPl2=0;
        firstActionPl3=0;
        cp1=0.1;
        cp2=0.1;
        cp3=0.1;
        e=0;
        stepcount=1000;
    }

    public void run(GameMatrix3P m, ActionListener fl){
        rnd=new Random();
        stop=false;
        GraphFrame p1f=new GraphFrame("Player 1 RegretMatching",stepcount);
        GraphFrame p2f=new GraphFrame("Player 2 RegretMatching",stepcount);
        GraphFrame p3f=new GraphFrame("Player 3 RegretMatching",stepcount);
        p1f.setLocation(0,0);
        p2f.setLocation(0,300);
        p3f.setLocation(0,600);        
        ArrayList<RegretStep> steps=new ArrayList<RegretStep>();
        double[] mp1=new double[m.getActionsP1()];
        double[] mp2=new double[m.getActionsP2()];
        double[] mp3=new double[m.getActionsP2()];
        for (int x=0;x<mp1.length;x++)
            if (x==firstActionPl1)mp1[x]=1;
                else mp1[x]=0;
        for (int x=0;x<mp2.length;x++)
            if (x==firstActionPl2)mp2[x]=1;
                else mp2[x]=0;
        for (int x=0;x<mp3.length;x++)
            if (x==firstActionPl3)mp3[x]=1;
                else mp3[x]=0;
        steps.add(new RegretStep(firstActionPl1,firstActionPl2,firstActionPl3,mp1,mp2,mp3));
        double p1payoff=0,p2payoff=0,p3payoff=0;
        p1payoff+=m.getP1()[firstActionPl1][firstActionPl2][firstActionPl3];
        p2payoff+=m.getP2()[firstActionPl1][firstActionPl2][firstActionPl3];
        p3payoff+=m.getP3()[firstActionPl1][firstActionPl2][firstActionPl3];
        System.out.println("P1Payoff sum: "+p1payoff);
        System.out.println("P2Payoff sum: "+p2payoff);
        System.out.println("P3Payoff sum: "+p3payoff);

        double[] avgPP1=new double[m.getActionsP1()];
        double[] avgPP2=new double[m.getActionsP2()];
        double[] avgPP3=new double[m.getActionsP3()];

        for (int x=0;x<avgPP1.length;x++){
            if (x==firstActionPl1)avgPP1[x]=1;
            else avgPP1[x]=0;
        }
        for (int x=0;x<avgPP2.length;x++){
            if (x==firstActionPl2)avgPP2[x]=1;
            else avgPP2[x]=0;
        }
        for (int x=0;x<stepcount;x++){
            if (stop){
                fl.actionPerformed(null);
                return;
            }
            System.out.print("Step "+(x+1)+": ");
            double u1=p1payoff/steps.size();
            double u2=p2payoff/steps.size();
            double u3=p3payoff/steps.size();
            int a1=steps.get(steps.size()-1).getP1();
            int a2=steps.get(steps.size()-1).getP2();
            int a3=steps.get(steps.size()-1).getP3();
            double[] rP1=new double[m.getActionsP1()];
            double[] rP2=new double[m.getActionsP2()];
            double[] rP3=new double[m.getActionsP3()];
            double[] pP1=new double[m.getActionsP1()];
            double[] pP2=new double[m.getActionsP2()];
            double[] pP3=new double[m.getActionsP3()];
            double rsump1=0;
            for (int k=0;k<m.getActionsP1();k++){
                if (k!=a1){
                    double v=0;
                    for (int s=0;s<steps.size();s++){
                        v+=m.getP1()[k][steps.get(s).getP2()][steps.get(s).getP3()];
                    }
                    v/=steps.size();
                    System.out.print("Vp1("+(k+1)+")="+v+"   -   ");
                    if (v-u1>=0)rP1[k]=v-u1;
                    else rP1[k]=0;
                    System.out.print("Rp1("+(k+1)+")="+rP1[k]+"   -   ");
                }
                else rP1[k]=0;
                rsump1+=rP1[k];

            }
            boolean improvementp1=false;
            for (int k=0;k<m.getActionsP1();k++)if (rP1[k]>0)improvementp1=true;
            double normalisationP1=0;
            if (!improvementp1){
                for (int k=0;k<m.getActionsP1();k++){
                    if (k==a1)pP1[k]=1;
                        else pP1[k]=0;
                    if (pP1[k]<0)pP1[k]=0;
                    normalisationP1+=pP1[k];
                }
            }
            else{
                for (int k=0;k<m.getActionsP1();k++){
                    if (k==a1)pP1[k]=1-(rsump1*cp1);
                        else pP1[k]=cp1*rP1[k];
                    if (pP1[k]<0)pP1[k]=0;
                    normalisationP1+=pP1[k];
                }
            }
            for (int k=0;k<m.getActionsP1();k++){
                pP1[k]/=normalisationP1;
                avgPP1[k]+=pP1[k];
            }
            int nextActionP1=sample(pP1,e);

            double rsump2=0;
            for (int k=0;k<m.getActionsP2();k++){
                if (k!=a2){
                    double v=0;
                    for (int s=0;s<steps.size();s++){
                        v+=m.getP2()[steps.get(s).getP1()][k][steps.get(s).getP3()];
                    }
                    v/=steps.size();
                    System.out.print("Vp2("+(k+1)+")="+v+"   -   ");
                    if (v-u2>=0)rP2[k]=v-u2;
                    else rP2[k]=0;
                    System.out.print("Rp2("+(k+1)+")="+rP2[k]+"   -   ");
                }
                else rP2[k]=0;
                rsump2+=rP2[k];

            }
            boolean improvementp2=false;
            for (int k=0;k<m.getActionsP2();k++)if (rP2[k]>0)improvementp2=true;
            double normalisationP2=0;
            if (!improvementp2){
                for (int k=0;k<m.getActionsP2();k++){
                    if (k==a2)pP2[k]=1;
                    else pP2[k]=0;
                    if (pP2[k]<0)pP2[k]=0;
                    normalisationP2+=pP2[k];
                }
            }
            else{
                for (int k=0;k<m.getActionsP2();k++){
                    if (k==a2)pP2[k]=1-(rsump2*cp2);
                        else pP2[k]=cp2*rP2[k];
                    if (pP2[k]<0)pP2[k]=0;
                    normalisationP2+=pP2[k];
                }
            }
            for (int k=0;k<m.getActionsP2();k++){
                pP2[k]/=normalisationP2;
                avgPP2[k]+=pP2[k];
            }
            int nextActionP2=sample(pP2,e);

            double rsump3=0;
            for (int k=0;k<m.getActionsP3();k++){
                if (k!=a3){
                    double v=0;
                    for (int s=0;s<steps.size();s++){
                        v+=m.getP3()[steps.get(s).getP1()][steps.get(s).getP2()][k];
                    }
                    v/=steps.size();
                    System.out.print("Vp3("+(k+1)+")="+v+"   -   ");
                    if (v-u3>=0)rP3[k]=v-u3;
                    else rP3[k]=0;
                    System.out.println("Rp3("+(k+1)+")="+rP3[k]);
                }
                else rP3[k]=0;
                rsump3+=rP3[k];

            }
            boolean improvementp3=false;
            for (int k=0;k<m.getActionsP3();k++)if (rP3[k]>0)improvementp3=true;
            double normalisationP3=0;
            if (!improvementp3){
                for (int k=0;k<m.getActionsP3();k++){
                    if (k==a3)pP3[k]=1;
                    else pP3[k]=0;
                    if (pP3[k]<0)pP3[k]=0;
                    normalisationP3+=pP3[k];
                }
            }
            else{
                for (int k=0;k<m.getActionsP3();k++){
                    if (k==a3)pP3[k]=1-(rsump3*cp3);
                        else pP3[k]=cp3*rP3[k];
                    if (pP3[k]<0)pP3[k]=0;
                    normalisationP3+=pP3[k];
                }
            }
            for (int k=0;k<m.getActionsP3();k++){
                pP3[k]/=normalisationP3;
                avgPP3[k]+=pP3[k];
            }
            int nextActionP3=sample(pP3,e);


            p1payoff+=m.getP1()[nextActionP1][nextActionP2][nextActionP3];
            //System.out.println(m.getP1()[nextActionP1][nextActionP2]);
            p2payoff+=m.getP2()[nextActionP1][nextActionP2][nextActionP3];
            p3payoff+=m.getP3()[nextActionP1][nextActionP2][nextActionP3];
            steps.add(new RegretStep(nextActionP1,nextActionP2,nextActionP3,pP1,pP2,pP3));
            System.out.print("    P1Avg (");
            for (int k=0;k<avgPP1.length;k++){
                System.out.print(avgPP1[k]/(steps.size()));
                if (k<avgPP1.length-1)System.out.print(",");
            }
            System.out.print(")   -   ");
            System.out.print("    P2Avg (");
            for (int k=0;k<avgPP2.length;k++){
                System.out.print(avgPP2[k]/(steps.size()));
                if (k<avgPP2.length-1)System.out.print(",");
            }
            System.out.print(")   -   ");
            System.out.print("P3Avg (");
            for (int k=0;k<avgPP3.length;k++){
                System.out.print(avgPP3[k]/(steps.size()));
                if (k<avgPP3.length-1)System.out.print(",");
            }
            System.out.println(")");
            p1f.addData(avgPP1,steps.size());
            p2f.addData(avgPP2,steps.size());
            p3f.addData(avgPP3,steps.size());
            if (x%20==0){
                p1f.repaint();
                p2f.repaint();
                p3f.repaint();
            }
            System.out.print("    P1Payoff sum: "+p1payoff+"   -    ");
            System.out.print("P2Payoff sum: "+p2payoff+"   -    ");
            System.out.println("P3Payoff sum: "+p3payoff);
        }
        fl.actionPerformed(null);

    }

    private int sample(double[] p, double e){
        double[] cum=new double[p.length];
        double sum=0;
        double v=rnd.nextDouble();
        if (v<e)return rnd.nextInt(p.length);
        v=rnd.nextDouble();
        for (int x=0;x<p.length;x++){
            sum+=p[x];
            cum[x]=sum;
            double ll=-1;
            if (x>0)ll=cum[x-1];
            if (v>ll&&v<=sum)return x;
        }
        return -1;
    }

    class RegretStep{
        private int p1,p2,p3;
        private double[] mp1,mp2,mp3;

        RegretStep(int p1, int p2,int p3, double[] mp1, double[] mp2, double[] mp3) {
            this.p1 = p1;
            this.p2 = p2;
            this.p3 = p3;
            this.mp1 = mp1;
            this.mp2 = mp2;
            this.mp3 = mp3;

            System.out.print("    P1Action: "+p1+" P1Strategy (");
            for (int x=0;x<mp1.length;x++){
                System.out.print(mp1[x]);
                if (x<mp1.length-1)System.out.print(", ");
            }
            System.out.print(") "+"   -    ");
            System.out.print("    P2Action: "+p2+" P2Strategy (");
            for (int x=0;x<mp2.length;x++){
                System.out.print(mp2[x]);
                if (x<mp2.length-1)System.out.print(", ");
            }
            System.out.print(") "+"   -    ");
            System.out.print("    P3Action: "+p3+" P3Strategy (");
            for (int x=0;x<mp3.length;x++){
                System.out.print(mp3[x]);
                if (x<mp3.length-1)System.out.print(", ");
            }
            System.out.println(") ");

        }

        public int getP1() {
            return p1;
        }

        public int getP2() {
            return p2;
        }

        public int getP3() {
            return p3;
        }

        public double[] getMp1() {
            return mp1;
        }

        public double[] getMp2() {
            return mp2;
        }

        public double[] getMp3() {
            return mp3;
        }

    }

}
