package org.jgroups.tests;


import org.jgroups.Address;
import org.jgroups.JChannel;
import org.jgroups.Message;
import org.jgroups.View;
import org.jgroups.debug.Debugger;

import javax.swing.*;
import javax.swing.border.BevelBorder;
import javax.swing.border.Border;
import javax.swing.border.EtchedBorder;
import javax.swing.border.TitledBorder;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.WindowEvent;
import java.util.Hashtable;
import java.util.Map;


/**
 * Title:        Demos of PARTITIONER protocol. Simulates network partitions.
 * Description:  The usage of PartitionerTestFrame is quite simple:
 * As the program finds new members in the view it adds them to the Hashtable and
 * to Processes list.
 * You can chose a Process from the processes list and digit a number over the set button.
 * Once you push the Set button the number is stored in the local Hastable.
 * Once you push the Execute button the local Hashtable is sent with a SET_PARTITIONS Event.
 * <p/>
 * The TextArea under the output area is to send messages over the channel and you view
 * the results on the output area.
 * <p/>
 * In the future it may be a good thing let the PARTITIONER layer spawn a control Frame
 * so that the use of this layer is transparent to the application, but it is also possible
 * to use a partitionerTestFrame (a little modified) in a Group of other Processes just
 * to control their behaviour against partitions.
 * Copyright:    Copyright (c) 2000
 * Company:      Computer Network Laboratory
 * 
 * @author Gianluca Collot
 * @version 1.1
 */


public class PartitionerTest extends JFrame implements Runnable {
    static String channel_properties="UDP:PARTITIONER:SHUFFLE:MERGE" +
            ":PING:FD(shun=false):" +
            "STABLE:" +
            "NAKACK:FRAG:FLUSH:GMS:" +
            "VIEW_ENFORCER:" +
            "QUEUE";
    JChannel channel;
    Debugger debugger;

    boolean connected=false;
    Hashtable ht=new Hashtable();

    JPanel jPanel1=new JPanel();
    BorderLayout borderLayout1=new BorderLayout();
    JPanel statusbar=new JPanel();
    JLabel status=new JLabel();
    BorderLayout borderLayout2=new BorderLayout();
    JPanel jPanel3=new JPanel();
    JScrollPane jScrollPane2=new JScrollPane();
    JList partitionsList=new JList();
    BorderLayout borderLayout3=new BorderLayout();
    JLabel partitionLabel=new JLabel();
    JTextField partitionField=new JTextField();
    JButton execute=new JButton();
    JButton set=new JButton();
    JPanel jPanel2=new JPanel();
    GridLayout gridLayout1=new GridLayout();
    TitledBorder titledBorder1;
    Border border1;
    Border border2;
    TitledBorder titledBorder2;
    Border border3;
    TitledBorder titledBorder3;
    BorderLayout borderLayout5=new BorderLayout();
    JButton connect=new JButton();
    JPanel applicationPanel=new JPanel();
    JTextArea output=new JTextArea();
    JScrollPane jScrollPane1=new JScrollPane();
    JTextField commandTextField=new JTextField();
    BorderLayout borderLayout4=new BorderLayout();


    public PartitionerTest(JChannel channel) {
        this.channel=channel;
        init();
    }

    public PartitionerTest() {
        try {
            channel=new JChannel(channel_properties);
        }
        catch(Exception ex) {
            ex.printStackTrace();
            System.exit(4);
        }
        init();
    }

    /**
     * Initializes the channel and application threads
     */

    void init() {
        try {
            channel.connect("prova");
            connected=true;
//      if (channel.getView().getMembers().elementAt(0).equals(channel.getLocalAddress())) {
//	Debugger debugger = new Debugger(channel);
//	debugger.start();
//      }
        }
        catch(Exception ex) {
            System.exit(4);
        }
        try {
            jbInit();
        }
        catch(Exception e) {
            e.printStackTrace();
        }
        Thread receiver=new Thread(this, "Receiver");
        receiver.start();
    }

    /**
     * jbInit()
     * This function provides initialization for the gui objects
     */

    private void jbInit() throws Exception {
        titledBorder1=new TitledBorder("");
        border1=BorderFactory.createCompoundBorder(BorderFactory.createEtchedBorder(Color.white, new Color(148, 145, 140)), BorderFactory.createEmptyBorder(4, 4, 4, 4));
        border2=BorderFactory.createBevelBorder(BevelBorder.LOWERED, Color.white, Color.white, new Color(148, 145, 140), new Color(103, 101, 98));
        titledBorder2=new TitledBorder(new EtchedBorder(EtchedBorder.RAISED, Color.white, new Color(148, 145, 140)), "Processes");
        border3=BorderFactory.createLineBorder(Color.black, 2);
        titledBorder3=new TitledBorder(new EtchedBorder(EtchedBorder.RAISED, Color.white, new Color(148, 145, 140)), "Channel Output");
        this.setSize(400, 400);
        this.setTitle("Partitioner Test :" + channel.getLocalAddress());
        this.addWindowListener(new java.awt.event.WindowAdapter() {
            public void windowClosing(WindowEvent e) {
                this_windowClosing(e);
            }
        });
        this.getContentPane().setLayout(borderLayout1);
        jPanel1.setLayout(borderLayout3);
        statusbar.setLayout(borderLayout2);
        status.setMinimumSize(new Dimension(100, 20));
        status.setText("ciao");
        statusbar.setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED, Color.white, Color.white, new Color(148, 145, 140), new Color(103, 101, 98)));
        jPanel3.setLayout(borderLayout5);
        partitionLabel.setHorizontalAlignment(SwingConstants.CENTER);
        partitionLabel.setText("Partition");
        execute.setText("Execute");
        execute.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(ActionEvent e) {
                execute_actionPerformed(e);
            }
        });
        set.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(ActionEvent e) {
                set_actionPerformed(e);
            }
        });
        set.setText("Set");
        set.setActionCommand("Set");
        jPanel2.setLayout(gridLayout1);
        partitionField.setMinimumSize(new Dimension(20, 20));
        gridLayout1.setRows(8);
        gridLayout1.setColumns(1);
        gridLayout1.setVgap(4);
        jPanel3.setBorder(border1);
        jPanel3.setMinimumSize(new Dimension(50, 50));
        jPanel3.setPreferredSize(new Dimension(200, 200));
        jScrollPane2.setBorder(titledBorder2);
        jScrollPane2.setMinimumSize(new Dimension(100, 80));
        jPanel2.setMinimumSize(new Dimension(77, 100));
        partitionsList.setBorder(BorderFactory.createLoweredBevelBorder());
        partitionsList.setMinimumSize(new Dimension(100, 80));
        connect.setActionCommand("Connect");
        connect.setText("Disconnect");
        connect.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(ActionEvent e) {
                connect_actionPerformed(e);
            }
        });
        jScrollPane1.setAutoscrolls(true);
        jScrollPane1.setBorder(titledBorder3);
        jScrollPane1.setMinimumSize(new Dimension(100, 100));
        jScrollPane1.setPreferredSize(new Dimension(300, 100));
        applicationPanel.setLayout(borderLayout4);
        commandTextField.addKeyListener(new java.awt.event.KeyAdapter() {
            public void keyTyped(KeyEvent e) {
                commandTextField_keyTyped(e);
            }
        });
        commandTextField.setMaximumSize(new Dimension(300, 21));
        this.getContentPane().add(statusbar, BorderLayout.SOUTH);
        statusbar.add(status, BorderLayout.CENTER);
        this.getContentPane().add(jPanel1, BorderLayout.NORTH);
        jPanel1.add(jPanel3, BorderLayout.CENTER);
        jPanel3.add(jScrollPane2, BorderLayout.NORTH);
        jScrollPane2.getViewport().add(partitionsList, null);
        jPanel1.add(jPanel2, BorderLayout.WEST);
        jPanel2.add(partitionLabel, null);
        jPanel2.add(partitionField, null);
        jPanel2.add(set, null);
        jPanel2.add(execute, null);
        jPanel2.add(connect, null);
        this.getContentPane().add(applicationPanel, BorderLayout.CENTER);
        applicationPanel.add(jScrollPane1, BorderLayout.CENTER);
        applicationPanel.add(commandTextField, BorderLayout.SOUTH);
        jScrollPane1.getViewport().add(output, null);
    }

    public void run() {
        boolean running=true;
        Object received=null;
        View view;
        Message msg;
        String payload;
        while(running) {
            try {
                received=channel.receive(0);
            }
            catch(Exception ex) {
                System.out.println("PartitionerTest.run() :" + ex);
                System.exit(-1);
            }
            if(received instanceof View) {
                view=(View)received;
                status.setText(view.toString());
                for(int i=0; i < view.size(); i++) {
                    Address member=(Address)view.getMembers().elementAt(i);
                    if(ht.get(member) == null) {
                        ht.put(member, new Integer(1));
                    }
                }
                partitionsList.setListData(ht.entrySet().toArray());
                output.append(view + "\n");
                output.setCaretPosition(output.getText().length());
            }

            if(received instanceof Message) {
                msg=(Message)received;
                Object tmp=null;
                tmp=msg.getObject();
                if(tmp instanceof String) {
                    payload=(String)tmp;
                    if("stop".equals(payload)) {
                        running=false;
                    }
                    output.append(':' + payload + '\n');
                }
                else
                    output.append("Received not a String\n");
            }
            output.setCaretPosition(output.getText().length());
        }
        channel.close();
        output.append("Channel Closed\n");
    } //run()

    /**
     * Sets the partition for the selected component on the hashtable
     */

    void set_actionPerformed(ActionEvent e) {
        if(partitionsList.getSelectedValue() != null) {
            Map.Entry entry=(Map.Entry)partitionsList.getSelectedValue();
            entry.setValue(new Integer(partitionField.getText()));
            partitionsList.setListData(ht.entrySet().toArray());
        }
    }

    /**
     * Sends a message over the channel
     */

    void sendButton_actionPerformed(ActionEvent e) {
        try {
            channel.send(new Message(null, null, commandTextField.getText()));
            commandTextField.setText("");
        }
        catch(Exception ex) {
            ex.printStackTrace();
        }

    }

    void this_windowClosing(WindowEvent e) {
        channel.close();
        System.out.println("Exiting");
        System.exit(0);
    }

    void execute_actionPerformed(ActionEvent e) {
        channel.down(new org.jgroups.Event(org.jgroups.Event.SET_PARTITIONS, ht));
    }

    static void main(String[] args) {

        PartitionerTest frame=new PartitionerTest();
        frame.validate();
        System.out.println("Frame validated");
        // Center the frame
        Dimension screenSize=Toolkit.getDefaultToolkit().getScreenSize();
        Dimension frameSize=frame.getSize();
        if(frameSize.height > screenSize.height)
            frameSize.height=screenSize.height;
        if(frameSize.width > screenSize.width)
            frameSize.width=screenSize.width;
        frame.setLocation((screenSize.width - frameSize.width) / 2, (screenSize.height - frameSize.height) / 2);
        frame.setVisible(true);
        System.out.println("Frame Visible");
    }

    void connect_actionPerformed(ActionEvent e) {
        try {
            if(!connected) {
                channel.connect("prova");
                connected=true;
                connect.setText("Disconnect");
            }
            else {
                channel.disconnect();
                connected=false;
                connect.setText("Connect");
                status.setText("Disconnected");
            }
        }
        catch(Exception ex) {
            ex.printStackTrace();
        }
    }

    void commandTextField_keyTyped(KeyEvent e) {
        if(e.getKeyChar() == '\n') sendButton_actionPerformed(null);
    }
}
