package profiles.uitest;

import profiles.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.Vector;
import profiles.Device;
import profiles.Message;


/**
 * E' il componente principale della simulazione. E' un pannello grafico,
 * che racchiude in se tutta l'interfaccia grafica. Implementa però anche le
 * funzionalità di BasicService.
 *
 * @author Lorenzo Moretti
 * @version 1.0
 */

public class SimulationPanel extends JPanel
    implements BasicService {
  static BasicService bs = null;

  // Vettore dei dispositivi utilizzati.
  Vector devices = new Vector();

  // Componenti grafici
  JPopupMenu menu = new JPopupMenu();
  JCheckBoxMenuItem autoUpdateItem;

  // Var. utilizzate per la selezione/rimozione/spostamento dei dispositivi
  UIDevice selectedDevice = null;
  int lastX;
  int lastY;


  public SimulationPanel() {
    bs = this;

    // Auto-aggiornamento attivato.
    new RepainterThread(this).start();

    setDoubleBuffered(true);
    setPreferredSize(new Dimension(640, 480));

    // Aggiungi i menu al popup
    JMenuItem item = new JMenuItem("Add Device");
    item.addActionListener(new AddDeviceAdapter(this));
    menu.add(item);

    item = new JMenuItem("Remove Device");
    item.addActionListener(new RemoveDeviceAdapter(this));
    menu.add(item);

    item = new JMenuItem("Properties");
    item.addActionListener(new PropertiesDeviceAdapter(this));
    menu.add(item);

    menu.add(new JSeparator());

    item = new JMenuItem("Update Routing Tables");
    item.addActionListener(new UpdateRoutingTablesAdapter(this));
    menu.add(item);

    item = new JMenuItem("Get Route To Device");
    item.addActionListener(new GetRouteAdapter(this));
    menu.add(item);

    // Gestione del mouse
    addMouseListener(new MouseAdapter() {
      public void mousePressed(MouseEvent evt) {
        selectedDevice = null;
        lastX = evt.getX();
        lastY = evt.getY();
        if (evt.isPopupTrigger()) {
          menu.show(evt.getComponent(), evt.getX(), evt.getY());
        } else {
          selectedDevice = getSelectedDevice(lastX,lastY);
        }
      }
      public void mouseReleased(MouseEvent evt) {
        if (evt.isPopupTrigger()) {
          menu.show(evt.getComponent(), evt.getX(), evt.getY());
        } else {
          if (selectedDevice != null) {
            selectedDevice.setPosX(evt.getX());
            selectedDevice.setPosY(evt.getY());
            repaint();
          }
        }
      }
    });

  }


  /**
   * Ridisegna il pannello
   * @param g
   */
  public void paint(Graphics g) {
    g.setColor(Color.WHITE);
    g.fillRect(0,0,this.getWidth(),this.getHeight());
    for (int i = 0; i < devices.size(); i++) {
      ((UIDevice)devices.get(i)).paint(g);
    }
  }

  /**
   * Callback per il menù pop-up. Aggiunge un nuovo dispositivo.
   */
  public void addDevice() {
    devices.add(new UIDevice(this, lastX, lastY, 100));
    repaint();
  }

  /**
   * Callback per il menù pop-up. Rimuove un dispositivo.
   */
  public void removeDevice() {
    UIDevice dev = getSelectedDevice(lastX, lastY);
    if (dev != null) {
      dev.destroy();
      devices.remove(dev);
      repaint();
    }
  }

  /**
   * Callback per il menù pop-up. Mostra le proprietà del dispositivo.
   */
  public void showDeviceProperties() {
    UIDevice dev = getSelectedDevice(lastX, lastY);
    if (dev != null) {

      UIDevicePropertiesPanel p = new UIDevicePropertiesPanel(dev);
      JOptionPane.showMessageDialog(this, p, "Device properties",
                                    JOptionPane.CLOSED_OPTION);
      repaint();
    }
  }

  /**
   * Utilità interna. Seleziona un dispositivo cliccato.
   */
  UIDevice getSelectedDevice(int x, int y) {
    for (int i = 0; i < devices.size(); i++) {
      UIDevice dev = (UIDevice) devices.get(i);
      if (Math.abs(dev.getPosX() - x) < UIDevice.INTERNAL_RADIUS &&
          Math.abs(dev.getPosY() - y) < UIDevice.INTERNAL_RADIUS) {
        return dev;
      }
    }

    return null;
  }

  /**
   * Restituisce i dispositivi visibili per un dispositivo d.
   * @param d
   * @return
   */
  public Vector getDevicesInRange(Device d) {
    UIDevice curDev = (UIDevice) d;
    Vector res = new Vector();

    for (int i = 0; i < devices.size(); i++) {
      UIDevice dev = (UIDevice) devices.get(i);
      if (dev.getId() != curDev.getId() &&
          Math.sqrt(Math.pow(curDev.getPosX() - dev.getPosX(), 2)+
                    Math.pow(curDev.getPosY() - dev.getPosY(), 2)) <=
          (dev.getRadius() + curDev.getRadius())/2) {
        res.add(UIDevice.fromString(dev.toString()));
      }
    }


    return res;
  }

  /**
   * Callback per il menù pop-up. Aggiorna le tabelle di routing dei dispositivi.
   */
  public void updateRoutingTables() {
    for (int i = 0; i < devices.size(); i++) {
      ((Device)devices.get(i)).refresh(this);
    }
  }

  /**
   * Callback per il menù pop-up. Mostra un percoso da un dispositivo selezionato
   * ad un dispositivo richiesto via dialogo.
   */
  public void getRoute() {
    UIDevice from = getSelectedDevice(lastX, lastY);
    if (from != null) {
      String destId = JOptionPane.showInputDialog("Type destination-device id:");
      Vector route = from.getRoutingTable().getRouteTo(destId);
      StringBuffer sbuff = new StringBuffer();
      for (int i = 0; i < route.size(); i++) {
        sbuff.append("->");
        sbuff.append(route.get(i));
      }
      JOptionPane.showMessageDialog(this, sbuff.toString(),
                                    "Route from " + from.getId() + " to " +
                                    destId, JOptionPane.DEFAULT_OPTION);
    }
  }

  /**
   * Restituisce una periferica se chiamata per nome.
   * @param id
   * @return
   */
  public Device getDevice(String id) {
    // Per semplicità ingoro la distanza del chiamante
    for (int i = 0; i < devices.size(); i++)
      if (((Device)devices.get(i)).getId().equals(id)) {
        return ((UIDevice)devices.get(i));
      }

    return null;
  }

  public void registerListnerDevice(Device d) {
    // Assumo che tutti si siano già registrati
  }

  /**
   * Inoltra un messaggio al destinatario.
   * @param msg
   */
  public void deliverMessage(Message msg) {
    TestFrame.appendMessage("deliver message: " + msg);
    // Passaggio del messaggio.
    Device dev = getDevice((String)msg.getRoute().get(msg.getRoute().size()-1));
    if (dev != null)
      dev.receive(TextMessage.fromString(msg.toString()));
  }

  /**
   * Utilità interna.
   * @return
   */
  static public BasicService getBasicService() {
    return bs;
  }

  public Device getRegisteredListenerDevice() {
    return null;
  }

  /**
   * Effettua un broadcast a tutti i dispositivi nei
   * raggiungibili.
   * @param d l'esecutore del bcast
   * @param msg il messaggio
   */
  public void bcast(Device d, Message msg) {
    TestFrame.appendMessage("BCAST: " + msg);
    for (int i = 0; i < devices.size(); i++) {
      if (d.getRoutingTable().
          getRouteTo((((Device)devices.elementAt(i)).getId())).size() >0)
      getDevice(((Device)devices.elementAt(i)).getId()).receive(msg);
    }
  }
}


// Adapter per l'interfaccia grafica

class AddDeviceAdapter implements ActionListener {
  SimulationPanel caller;

  AddDeviceAdapter(SimulationPanel c) {
    caller = c;
  }

  public void actionPerformed(ActionEvent actionEvent) {
    caller.addDevice();
  }
}

class RemoveDeviceAdapter implements ActionListener {
  SimulationPanel caller;

  RemoveDeviceAdapter(SimulationPanel c) {
    caller = c;
  }

  public void actionPerformed(ActionEvent actionEvent) {
    caller.removeDevice();
  }
}


class PropertiesDeviceAdapter implements ActionListener {
  SimulationPanel caller;

  PropertiesDeviceAdapter(SimulationPanel c) {
    caller = c;
  }

  public void actionPerformed(ActionEvent actionEvent) {
    caller.showDeviceProperties();
  }
}

class UpdateRoutingTablesAdapter implements ActionListener {
  SimulationPanel caller;

  UpdateRoutingTablesAdapter(SimulationPanel c) {
    caller = c;
  }

  public void actionPerformed(ActionEvent actionEvent) {
    caller.updateRoutingTables();
  }
}

class GetRouteAdapter implements ActionListener {
  SimulationPanel caller;

  GetRouteAdapter(SimulationPanel c) {
    caller = c;
  }

  public void actionPerformed(ActionEvent actionEvent) {
    caller.getRoute();
  }
}

