package senseiTests.domainsTest;

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

//import sensei.util.Debug;
import sensei.util.Error;
import sensei.util.ErrorHandler;
import sensei.util.IntWrapper;
import sensei.middleware.domains.BehaviourOnViewChanges;
import sensei.middleware.domains.Property;
import senseiTests.middleware.domainsTest.StateTransferType;

public class UIFrame extends JFrame implements ActionListener, ItemListener, MouseListener, ErrorHandler,
  DynamicInfoPrintable
{

  //*************************************************************************************
  //**************************** CONSTRUCTOR ********************************************
  //*************************************************************************************

  public UIFrame(UIUser user, Answerer answerer)
  {
    super(TITLE);
    this.user = user;
    this.answerer = answerer;
    minimumSubgroupId = new IntWrapper(sensei.gms.Consts.EVERY_MEMBER_ID);
    wrapper = new IntWrapper();

    init();
    initMenu();
    initPopupMenus();

    answerer.setListener(eventsPanel);

    subgroups = new TreeMap();
    joined=transfered=false;
    lastGroupCreated=0;

    addWindowListener(new WindowAdapter(){public void windowClosing(WindowEvent evt){
        exit();
      }});

    pack();
    int x = (int) getGraphicsConfiguration().getBounds().getWidth();
    int y = (int) getGraphicsConfiguration().getBounds().getHeight();
    setLocation(x/10+new java.util.Random().nextInt(2*(x/5)),
                y/10+new java.util.Random().nextInt(2*(y/5)));
    show();

    Error.setErrorHandler(this);
  }

  public void clear(final UIPropertiesUser propertiesUser, final BehaviourOnViewChanges behaviour,
    final CoordinatorElectionMode ceMode, final boolean automatic, final boolean showEvents,
    final boolean enableProperties, final boolean enableDynamicSubgroups)
  {
    this.propertiesUser = propertiesUser;
    SwingUtilities.invokeLater(new Runnable(){
      public void run()
      {
        subgroups = new TreeMap();
        joined=transfered=false;
        lastGroupCreated=0;

        eventsPanel.clear();

        menuModeME.setEnabled(true);
        menuModeNME.setEnabled(true);
        menuModeNMB.setEnabled(true);
        if (behaviour==BehaviourOnViewChanges.MembersOnTransferExcludedFromGroup)
          menuModeME.setSelected(true);
        else if (behaviour==BehaviourOnViewChanges.StatelessMembersDoNotBelongToGroup)
          menuModeNME.setSelected(true);
        else
          menuModeNMB.setSelected(true);

        if (ceMode==CoordinatorElectionMode.InfrastructureControlled)
          menuCoordinatorInfrastructure.setSelected(true);
        else if (ceMode==CoordinatorElectionMode.SelectedByWeights)
          menuCoordinatorWeights.setSelected(true);
        else
          menuCoordinatorSelection.setSelected(true);

        menuSubgroupsAutomatic.setSelected(automatic);
        menuSubgroupsEvents.setSelected(showEvents);
        menuEnableDynamic.setSelected(enableDynamicSubgroups);

        menuGroupJoin.setEnabled(true);
        menuGroupLeave.setEnabled(false);
        menuPropertiesUpdate.setEnabled(false);

        menuCoordinatorWeights.setEnabled(false);
        menuProperties.setEnabled(false);
        menuEnableProperties.setEnabled(true);
        menuEnableProperties.setSelected(enableProperties);
        popupPropertiesAutomaticUpdate.setSelected(enableProperties);

        menuSubgroups.setEnabled(true);
        joined=transfered=false;

        subgroupsPanel.removeAll();
        subgroups.clear();

        menuSubgroups.setEnabled(true);
        menuCoordinatorSelection.setEnabled(true);
        menuCoordinatorInfrastructure.setEnabled(true);
        menuEnableDynamic.setEnabled(true);

        pack();
      }
    });
  }

  //*************************************************************************************
  //**************************** DYNAMIC INFO PRINTABLE *********************************
  //*************************************************************************************

  public void showCreatedDynamicGroup(int subgroupId, StateTransferType type, String info)
  {
    eventsPanel.showCreatedDynamicGroup(subgroupId, type, info);
  }

  public void showRemovedDynamicGroup(int subgroupId, String info)
  {
    eventsPanel.showRemovedDynamicGroup(subgroupId, info);
  }

  //*************************************************************************************
  //**************************** MESSAGE ************************************************
  //*************************************************************************************

  public void showMessage(String s, String title)
  {
    JOptionPane.showMessageDialog(this, s, title,JOptionPane.INFORMATION_MESSAGE);
  }

  public void showMessage(String s)
  {
    showMessage(s, TITLE);
  }

  public void showError(String s)
  {
    showMessage(s, "Fatal error");
  }

  public void showError(Exception ex)
  {
    ex.printStackTrace();
    showMessage(ex.toString(), "Fatal error");
  }

  public void showError(String area, Exception ex)
  {
    ex.printStackTrace();
    showMessage(ex.toString(), "Fatal error on " + area);
  }

  //*************************************************************************************
  //**************************** REQUEST INT ********************************************
  //*************************************************************************************

  /**
   * Request an integer for the user, returning -1 if cancel is pressed
   */
  int requestInt(String message, int shownValue)
  {
    Object answer =
      JOptionPane.showInputDialog(this, message, TITLE,JOptionPane.QUESTION_MESSAGE, null, null, new Integer(shownValue));
    return answer==null? -1 : Integer.valueOf((String)answer).intValue();
  }

  //*************************************************************************************
  //**************************** COLOUR PANEL METHODS ***********************************
  //*************************************************************************************

  public void createColourPanel(IntWrapper id, StateTransferType type, StateChanger stateChanger)
  {
    //Debug.assert(!subgroups.containsKey(id),Consts.AREA,"UIFrame::createColourPanel::1");
    ColoursInfoPanel panel = new ColoursInfoPanel(this,stateChanger,id.i,StateTransferTypeConverter.asString(type));
    subgroups.put(id, panel);
    int pos = subgroups.subMap(minimumSubgroupId, id).size();
    subgroupsPanel.add(panel, pos);
    lastGroupCreated=id.i;
    pack();
  }

  public void removeColourPanel(int id)
  {
    ColoursInfoPanel panel = (ColoursInfoPanel) subgroups.remove(new IntWrapper(id));
    //Debug.assert(panel!=null,Consts.AREA,"UIFrame::removeColourPanel::1");
    subgroupsPanel.remove(panel);
    pack();
  }

  public void setStateOnColourPanel(int id, String state)
  {
    wrapper.i=id;
    ColoursInfoPanel panel = (ColoursInfoPanel)(subgroups.get(wrapper));
    //Debug.assert(panel!=null,Consts.AREA,"UIFrame::setStateOnColourPanel::"+id);
    panel.setState(state);
  }

  public void enableUserInteractionOnColourPanel(IntWrapper where)
  {
    if (transfered)
    {
      ColoursInfoPanel panel = (ColoursInfoPanel) subgroups.get(where);
      //Debug.assert(panel!=null,Consts.AREA,"UIFrame::enableUserInteractionOnColourPanel::1");
      panel.enableUserInteraction();
    }
  }

  void enableUserInteractionOnEveryColourPanel()
  {
    Iterator it = subgroups.values().iterator();
    while(it.hasNext())
      ((ColoursInfoPanel)(it.next())).enableUserInteraction();
  }

  //*************************************************************************************
  //**************************** STATE TRANSFERED ***************************************
  //*************************************************************************************

  public void stateTransfered()
  {
    if (joined && !transfered)
    {
      menuSubgroups.setEnabled(true);
      enableUserInteractionOnEveryColourPanel();
      transfered=true;
    }
  }

  //*************************************************************************************
  //**************************** PRIVATE ************************************************
  //*************************************************************************************

  void init()
  {
    JPanel superSubgroupsPanel = new JPanel(new BorderLayout());
    subgroupsPanel = new JPanel(new GridLayout(0,1));
    superSubgroupsPanel.add(subgroupsPanel, BorderLayout.NORTH);
    info=new JPanel(new BorderLayout());
    infoDim = new JPanel(new GridLayout(1,0));
    info.add(infoDim, BorderLayout.CENTER);
    JPanel panel = new JPanel(new BorderLayout());
    panel.add(superSubgroupsPanel, BorderLayout.NORTH);
    panel.add(info, BorderLayout.CENTER);
//    if (sensei.gms.Trace.TRACE)
//    {
//      sensei.gms.TraceGraphical tg = new sensei.gms.TraceGraphical(false, false);
//      sensei.gms.Trace.addTracer(tg);
//      panel.add(tg,BorderLayout.SOUTH);
//    }
    getContentPane().add(panel);

    stateTransferPanel = new StateTransferInfoPanel(answerer);
    eventsPanel = new StateTransferInfoListingPanel();
    propertiesPanel = new PropertiesPanel();
  };

  void initMenu()
  {
    JMenuBar menuBar = new JMenuBar();
    setJMenuBar(menuBar);
    {
      JMenu menuGroup = new JMenu("Group");
      menuGroup.setMnemonic(KeyEvent.VK_G);
      menuBar.add(menuGroup);
      {
        menuGroupJoin = new JMenuItem("Join", KeyEvent.VK_J);
        menuGroup.add(menuGroupJoin);
        menuGroupJoin.addActionListener(this);
        menuGroupLeave = new JMenuItem("Leave", KeyEvent.VK_L);
        menuGroup.add(menuGroupLeave);
        menuGroupLeave.addActionListener(this);
        menuGroup.addSeparator();
        menuGroupExit = new JMenuItem("Exit", KeyEvent.VK_X);
        menuGroup.add(menuGroupExit);
        menuGroupExit.addActionListener(this);
      }
      JMenu menuFeatures = new JMenu("Features");
      menuFeatures.setMnemonic(KeyEvent.VK_F);
      menuBar.add(menuFeatures);
      {
        JMenu menuMode = new JMenu("Behaviour");
        menuMode.setMnemonic(KeyEvent.VK_B);
        menuFeatures.add(menuMode);
        {
          ButtonGroup group = new ButtonGroup();

          menuModeME = new JRadioButtonMenuItem("Members on transfer excluded",true);
          menuModeME.setMnemonic(KeyEvent.VK_M);
          group.add(menuModeME);
          menuMode.add(menuModeME);

          menuModeNME = new JRadioButtonMenuItem("New members excluded",false);
          menuModeNME.setMnemonic(KeyEvent.VK_N);
          group.add(menuModeNME);
          menuMode.add(menuModeNME);

          menuModeNMB = new JRadioButtonMenuItem("New members belong to the group",false);
          menuModeNMB.setMnemonic(KeyEvent.VK_B);
          group.add(menuModeNMB);
          menuMode.add(menuModeNMB);
        }
        JMenu menuCoordinator = new JMenu("Coordinator election");
        menuCoordinator.setMnemonic(KeyEvent.VK_C);
        menuFeatures.add(menuCoordinator);
        {
          ButtonGroup group = new ButtonGroup();

          menuCoordinatorInfrastructure = new JRadioButtonMenuItem("Infrastructure controlled",true);
          menuCoordinatorInfrastructure.setMnemonic(KeyEvent.VK_I);
          group.add(menuCoordinatorInfrastructure);
          menuCoordinator.add(menuCoordinatorInfrastructure);

          menuCoordinatorWeights = new JRadioButtonMenuItem("Selected by \"weight\" property",false);
          menuCoordinatorWeights.setMnemonic(KeyEvent.VK_W);
          group.add(menuCoordinatorWeights);
          menuCoordinator.add(menuCoordinatorWeights);

          menuCoordinatorSelection = new JRadioButtonMenuItem("Selected by the user",false);
          menuCoordinatorSelection.setMnemonic(KeyEvent.VK_U);
          group.add(menuCoordinatorSelection);
          menuCoordinator.add(menuCoordinatorSelection);
        }
        menuEnableProperties = new JCheckBoxMenuItem("Enable properties");
        menuEnableProperties.setMnemonic(KeyEvent.VK_P);
        menuFeatures.add(menuEnableProperties);
        menuEnableProperties.addItemListener(this);

        menuEnableDynamic = new JCheckBoxMenuItem("Enable dynamic subgroups");
        menuEnableDynamic.setMnemonic(KeyEvent.VK_D);
        menuFeatures.add(menuEnableDynamic);
      }
      menuSubgroups = new JMenu("Subgroups");
      menuSubgroups.setMnemonic(KeyEvent.VK_S);
      menuBar.add(menuSubgroups);
      {
        menuSubgroupsCreate = new JMenuItem("Create ...", KeyEvent.VK_C);
        menuSubgroupsCreate.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_1, ActionEvent.ALT_MASK));
        menuSubgroups.add(menuSubgroupsCreate);
        menuSubgroupsCreate.addActionListener(this);

        menuSubgroupsRemove = new JMenuItem("Remove ...", KeyEvent.VK_R);
        menuSubgroupsRemove.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_2, ActionEvent.ALT_MASK));
        menuSubgroups.add(menuSubgroupsRemove);
        menuSubgroupsRemove.addActionListener(this);
        menuSubgroups.addSeparator();

        menuSubgroupsAutomatic = new JCheckBoxMenuItem("Automatic transfer");
        menuSubgroupsAutomatic.setMnemonic(KeyEvent.VK_A);
        menuSubgroups.add(menuSubgroupsAutomatic);
        menuSubgroupsAutomatic.addItemListener(this);

        menuSubgroupsAutomaticDelay = new JMenuItem("Automatic delay ...", KeyEvent.VK_D);
        menuSubgroups.add(menuSubgroupsAutomaticDelay);
        menuSubgroupsAutomaticDelay.addActionListener(this);
        menuSubgroups.addSeparator();

        menuSubgroupsEvents = new JCheckBoxMenuItem("Show events");
        menuSubgroupsEvents.setMnemonic(KeyEvent.VK_E);
        menuSubgroupsEvents.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_9, ActionEvent.ALT_MASK));
        menuSubgroups.add(menuSubgroupsEvents);
        menuSubgroupsEvents.addItemListener(this);

        menuSubgroupsClearEvents = new JMenuItem("Clear events", KeyEvent.VK_C);
        menuSubgroups.add(menuSubgroupsClearEvents);
        menuSubgroupsClearEvents.addActionListener(this);
      }
      menuProperties = new JMenu("Properties");
      menuProperties.setMnemonic(KeyEvent.VK_P);
      menuBar.add(menuProperties);
      {
        menuPropertiesAdd = new JMenuItem("Add ...", KeyEvent.VK_A);
        menuPropertiesAdd.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_4, ActionEvent.ALT_MASK));
        menuProperties.add(menuPropertiesAdd);
        menuPropertiesAdd.addActionListener(this);

        menuPropertiesRem = new JMenuItem("Remove ...", KeyEvent.VK_R);
        menuPropertiesRem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_5, ActionEvent.ALT_MASK));
        menuProperties.add(menuPropertiesRem);
        menuPropertiesRem.addActionListener(this);
        menuProperties.addSeparator();

        menuPropertiesUpdate = new JMenuItem("Update properties", KeyEvent.VK_U);
        menuProperties.add(menuPropertiesUpdate);
        menuPropertiesUpdate.addActionListener(this);

        menuPropertiesAutomaticUpdate = new JCheckBoxMenuItem("Automatic update");
        menuPropertiesAutomaticUpdate.setMnemonic(KeyEvent.VK_T);
        menuProperties.add(menuPropertiesAutomaticUpdate);
        menuPropertiesAutomaticUpdate.addItemListener(this);
        menuProperties.addSeparator();

        menuPropertiesWindow = new JCheckBoxMenuItem("Show properties");
        menuPropertiesWindow.setMnemonic(KeyEvent.VK_S);
        menuPropertiesWindow.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_8, ActionEvent.ALT_MASK));
        menuProperties.add(menuPropertiesWindow);
        menuPropertiesWindow.addItemListener(this);
      }
      JMenu menuHelp = new JMenu("Help");
      menuHelp.setMnemonic(KeyEvent.VK_H);
      menuBar.add(Box.createHorizontalGlue());
      menuBar.add(menuHelp);
      {
        menuHelpAbout = new JMenuItem("About", KeyEvent.VK_A);
        menuHelp.add(menuHelpAbout);
        menuHelpAbout.addActionListener(this);
      }
    }
    menuGroupJoin.setEnabled(true);
    menuGroupLeave.setEnabled(false);
    menuSubgroups.setEnabled(true);

    menuProperties.setEnabled(false);
    menuPropertiesUpdate.setEnabled(false);
}

  void initPopupMenus()
  {
    popupStateTransfer = new JPopupMenu();
    popupSubgroupsTransferHide = new JMenuItem("Hide", KeyEvent.VK_H);
    popupStateTransfer.add(popupSubgroupsTransferHide);
    popupSubgroupsTransferHide.addActionListener(this);

    popupEvents = new JPopupMenu();
    popupEventsClear = new JMenuItem("Clear", KeyEvent.VK_C);
    popupEvents.add(popupEventsClear);
    popupEventsClear.addActionListener(this);
    popupEventsHide = new JMenuItem("Hide", KeyEvent.VK_H);
    popupEvents.add(popupEventsHide);
    popupEventsHide.addActionListener(this);

    popupProperties = new JPopupMenu();
    popupPropertiesAdd = new JMenuItem("Add property...", KeyEvent.VK_A);
    popupProperties.add(popupPropertiesAdd);
    popupPropertiesAdd.addActionListener(this);
    popupPropertiesRemove = new JMenuItem("Remove property...", KeyEvent.VK_R);
    popupProperties.add(popupPropertiesRemove);
    popupPropertiesRemove.addActionListener(this);
    popupProperties.addSeparator();
    popupPropertiesUpdate = new JMenuItem("Update", KeyEvent.VK_U);
    popupProperties.add(popupPropertiesUpdate);
    popupPropertiesUpdate.addActionListener(this);
    popupPropertiesAutomaticUpdate = new JCheckBoxMenuItem("Automatic update");
    popupPropertiesAutomaticUpdate.setMnemonic(KeyEvent.VK_T);
    popupProperties.add(popupPropertiesAutomaticUpdate);
    popupPropertiesAutomaticUpdate.addItemListener(this);
    popupProperties.addSeparator();
    popupPropertiesHide = new JMenuItem("Hide", KeyEvent.VK_H);
    popupProperties.add(popupPropertiesHide);
    popupPropertiesHide.addActionListener(this);

    stateTransferPanel.addMouseListener(this);
    eventsPanel.addMouseListener(this);
    propertiesPanel.addMouseListener(this);
  }

  void enableProperties()
  {
    properties=true;
    propertiesUser.enableProperties();
    menuEnableProperties.setEnabled(false);
    menuCoordinatorWeights.setEnabled(true);
    menuProperties.setEnabled(true);
    menuPropertiesWindow.setSelected(true);
  }

  //*************************************************************************************
  //**************************** SET PROPERTIES *****************************************
  //*************************************************************************************

  public void setProperties(int member, Property[] properties)
  {
    propertiesPanel.setProperties(member, properties);
  }

  public void clearProperties()
  {
    propertiesPanel.clearProperties();
  }

  //*************************************************************************************
  //**************************** ADD PROPERTY *******************************************
  //*************************************************************************************

  public void addProperty(Property[] properties)
  {
    Property toAdd = new PropertySelector(properties, this, "Add property", "Name", "Value", true, true).getInput();
    if (toAdd!=null)
      propertiesUser.addProperty(toAdd);
  }

  //*************************************************************************************
  //**************************** REMOVE PROPERTY ****************************************
  //*************************************************************************************

  public void removeProperty(Property[] properties)
  {
    Property toRem = new PropertySelector(properties, this, "Remove property", "Name", "Value", false, true).getInput();
    if (toRem!=null)
      propertiesUser.removeProperty(toRem);
  }

  //*************************************************************************************
  //**************************** ACTION PERFORMED ***************************************
  //*************************************************************************************

  public void actionPerformed(ActionEvent e)
  {
    Object obj = e.getSource();
    if (obj==menuGroupJoin)
      doJoin();
    else if (obj==menuGroupLeave)
      user.doLeave();
    else if (obj==menuGroupExit)
      exit();
    else if (((obj==menuPropertiesAdd) || (obj==popupPropertiesAdd)) && properties)
      addProperty(propertiesUser.getProperties());
    else if (((obj==menuPropertiesRem) || (obj==popupPropertiesRemove))  && properties)
      removeProperty(propertiesUser.getProperties());
    else if ((obj==menuSubgroupsClearEvents) || (obj==popupEventsClear))
      eventsPanel.clear();
    else if (((obj==menuPropertiesUpdate) || (obj==popupPropertiesUpdate)) && joined && properties)
      propertiesUser.updateProperties();
    else if (obj==popupSubgroupsTransferHide)
      menuSubgroupsAutomatic.setSelected(true);
    else if (obj==popupEventsHide)
      menuSubgroupsEvents.setSelected(false);
    else if (obj==popupPropertiesHide)
      menuPropertiesWindow.setSelected(false);
    else if ((obj==menuCoordinatorWeights) || (obj==menuCoordinatorSelection))
      user.changedCoordinatorElection(menuCoordinatorWeights.isSelected());
    else if (obj==menuSubgroupsCreate)
    {
      if (joined)
      {
        StateKeeper generator = new StateKeeper();
        generator.generateRandomState();
        String[] state = generator.getSubStates();
        String displayState = generator.toString();
        generator=null;
        SubgroupRequester subgroupRequester = new SubgroupRequester(this, displayState);
        if (subgroupRequester.getInput())
          user.doCreateDynamicSubgroup(state,subgroupRequester.getType(),subgroupRequester.getWait());
      }
      else
      {
        SubgroupRequester subgroupRequester = new SubgroupRequester(this,lastGroupCreated+1);
        if (subgroupRequester.getInput())
          user.doCreateSubgroup(subgroupRequester.getId(), subgroupRequester.getType());
      }
    }
    else if (obj==menuSubgroupsRemove)
    {
      int n=requestInt("Subgroup id:", lastGroupCreated);
      if (n!=-1)
        user.doRemoveSubgroup(n);
    }
    else if (obj==menuSubgroupsAutomaticDelay)
    {
      int n=requestInt("Delay (ms) on automatic mode: ", answerer.getAutomaticDelay());
      if (n>=0)
        answerer.setAutomaticDelay(n);
      else
        showMessage("Delay cannot be negative");
    }
    else if (obj==menuHelpAbout)
      showMessage("State Transfer Tester, (c)LuisM Pena, August 2001: testing the state transfer capabilities on Sensei",
          "About");
  }

  //*************************************************************************************
  //**************************** JOIN METHODS *******************************************
  //*************************************************************************************

  void joined(int id)
  {
    if (!joined)
    {
      menuGroupJoin.setEnabled(false);
      menuGroupLeave.setEnabled(true);
      menuSubgroups.setEnabled(false);
      menuModeME.setEnabled(false);
      menuModeNME.setEnabled(false);
      menuModeNMB.setEnabled(false);
      menuPropertiesUpdate.setEnabled(true);
      if (menuCoordinatorInfrastructure.isSelected())
      {
        menuCoordinatorWeights.setEnabled(false);
        menuCoordinatorSelection.setEnabled(false);
      }
      else
      {
        menuCoordinatorWeights.addActionListener(this);
        menuCoordinatorSelection.addActionListener(this);
      }
      menuCoordinatorInfrastructure.setEnabled(false);
      menuEnableProperties.setEnabled(false);
      menuEnableDynamic.setEnabled(false);
      joined=true;
      setTitle(TITLE + " - member " + id);
    }
  }

  public void doJoin()
  {
    if (!joined)
    {
      final BehaviourOnViewChanges behaviour =
        menuModeME.isSelected()? BehaviourOnViewChanges.MembersOnTransferExcludedFromGroup :
        menuModeNME.isSelected()? BehaviourOnViewChanges.StatelessMembersDoNotBelongToGroup :
          BehaviourOnViewChanges.StatelessMembersBelongToGroup;

      final CoordinatorElectionMode ceMode =
        menuCoordinatorInfrastructure.isSelected()? CoordinatorElectionMode.InfrastructureControlled :
        menuCoordinatorWeights.isSelected()? CoordinatorElectionMode.SelectedByWeights :
          CoordinatorElectionMode.SelectedByUser;

      menuGroupJoin.setEnabled(false);
      new Thread(){
        public void run()
        {
          if (!user.doJoin(behaviour, ceMode, menuEnableDynamic.isSelected()))
            menuGroupJoin.setEnabled(true);
        }
      }.start();
      Thread.yield();
    }
  }

  //*************************************************************************************
  //**************************** ITEM STATE CHANGED *************************************
  //*************************************************************************************

  public void itemStateChanged(ItemEvent e)
  {
    Object source = e.getItem();
    boolean selected=e.getStateChange()==ItemEvent.SELECTED;
    if (source==menuSubgroupsAutomatic)
    {
      if (selected)
      {
        answerer.setAutomatic();
        info.remove(stateTransferPanel);
      }
      else
      {
        answerer.setManual(stateTransferPanel);
        info.add(stateTransferPanel,BorderLayout.SOUTH);
      }
      pack();
    }
    else if (source==menuSubgroupsEvents)
    {
      if (selected)
        infoDim.add(eventsPanel);
      else
        infoDim.remove(eventsPanel);
      pack();
    }
    else if ((source==menuEnableProperties) && selected)
      enableProperties();
    else if ((source==menuPropertiesWindow) && properties)
    {
      if (selected)
        infoDim.add(propertiesPanel);
      else
        infoDim.remove(propertiesPanel);
      pack();
    }
    else if (source==menuPropertiesAutomaticUpdate)
    {
      if (selected!=popupPropertiesAutomaticUpdate.isSelected()) //keeping synchronize popup and menu
      {
        popupPropertiesAutomaticUpdate.setSelected(selected);
        propertiesUser.updatePropertiesAutomatically(selected, joined);
      }
    }
    else if (source==popupPropertiesAutomaticUpdate)
    {
      if (selected!=menuPropertiesAutomaticUpdate.isSelected()) //keeping synchronize popup and menu
      {
        menuPropertiesAutomaticUpdate.setSelected(selected);
        propertiesUser.updatePropertiesAutomatically(selected, joined);
      }
    }
  }

  //*************************************************************************************
  //**************************** MOUSE LISTENER *****************************************
  //*************************************************************************************

  public void mousePressed(MouseEvent e)
  {
    maybeShowPopup(e);
  }

  public void mouseReleased(MouseEvent e)
  {
    maybeShowPopup(e);
  }

  public void mouseClicked(MouseEvent e){}
  public void mouseEntered(MouseEvent e){}
  public void mouseExited(MouseEvent e){}

  private void maybeShowPopup(MouseEvent e)
  {
    if (e.isPopupTrigger())
    {
      Component o = e.getComponent();
      if (o==stateTransferPanel)
        popupStateTransfer.show(o, e.getX(), e.getY());
      else if (o==eventsPanel)
        popupEvents.show(o, e.getX(), e.getY());
      else if (o==propertiesPanel)
        popupProperties.show(o, e.getX(), e.getY());
    }
  }

  //*************************************************************************************
  //**************************** DEBUG OUTPUT OPERATIONS ********************************
  //*************************************************************************************

  /**
    * Output the debug message
    **/
  public boolean handleError(String area, String debugMessage)
  {
    System.out.println(debugMessage);
    showMessage(debugMessage, area);
    return false;
  }

  /**
    * Output the debug message
    **/
  public boolean handleException(String area, Exception ex)
  {
    ex.printStackTrace();
    showMessage(ex.toString(), area);
    return false;
  }

  public void showCoordinatorElector(int target, int coordinator)
  {
    eventsPanel.showCoordinatorElector(target, coordinator);
  }

  //*************************************************************************************
  //**************************** OPERATIONS TO ANSWER TO USER EVENTS ********************
  //*************************************************************************************

  void exit()
  {
    user.onExit();
  }

  //*************************************************************************************
  //**************************** DATA ***************************************************
  //*************************************************************************************

  JMenuItem menuGroupJoin, menuGroupLeave, menuGroupExit, menuSubgroupsCreate, menuSubgroupsRemove, menuHelpAbout;
  JMenuItem menuSubgroupsAutomaticDelay, menuPropertiesAdd, menuPropertiesRem;
  JMenuItem menuSubgroupsClearEvents, menuPropertiesUpdate;
  JCheckBoxMenuItem menuEnableProperties, menuEnableDynamic;
  JMenuItem popupSubgroupsTransferHide, popupEventsClear, popupEventsHide, popupPropertiesAdd;
  JMenuItem popupPropertiesRemove, popupPropertiesUpdate, popupPropertiesHide;
  JPopupMenu popupStateTransfer, popupEvents, popupProperties;
  JCheckBoxMenuItem menuSubgroupsAutomatic, menuSubgroupsEvents, menuPropertiesWindow, menuPropertiesAutomaticUpdate;
  JCheckBoxMenuItem popupPropertiesAutomaticUpdate;
  JRadioButtonMenuItem menuModeME, menuModeNME, menuModeNMB;
  JRadioButtonMenuItem menuCoordinatorInfrastructure, menuCoordinatorWeights, menuCoordinatorSelection;
  JMenu menuSubgroups, menuProperties;
  JPanel info, infoDim;
  JPanel subgroupsPanel;
  PropertiesPanel propertiesPanel;
  StateTransferInfoPanel stateTransferPanel;
  StateTransferInfoListingPanel eventsPanel;
  SortedMap subgroups; //IntWrapper, ColoursInfoPanel
  Answerer answerer;
  UIUser user;
  UIPropertiesUser propertiesUser;
  IntWrapper minimumSubgroupId, wrapper;

  int lastGroupCreated;
  boolean joined, transfered, properties;
  final static String TITLE = "State Transfer tester";
}