import java.applet.*;
import java.awt.*;
import java.awt.font.*;
import java.awt.geom.*;
import javax.swing.*;
import javax.swing.border.*;
import java.awt.event.*;
//-----------------------------------------------------------------------------
//C a r S i m u l a t o r F r a m e
//Klassen indeholder selve applet vinduet, som den sætter op, samt søger for
//at starte og stoppe simulationen.
public class CarSimulatorFrame extends JApplet
{
  //Indlejret klasse til at håndtere simulations knappen.
  class SimulationButton extends JButton implements ActionListener
  {
    SimulationButton(String str) { super(str); addActionListener(this); }
    public void actionPerformed(ActionEvent evt)
    {
      if(this.getText().equals("Start Simulation"))
      {
        int nrOfQueues = simulationControlePanel.getField(SimulationControlePanel.NR_OF_QUEUES);
        int payTime = simulationControlePanel.getField(SimulationControlePanel.PAY_TIME);
        int meanArrival = simulationControlePanel.getField(SimulationControlePanel.MEAN_ARRIVAL);
        int diviasionArrival = simulationControlePanel.getField(SimulationControlePanel.DIVIASION_ARRIVAL );

        for(int i = 0; i < MAX_CARQUEUES; i++)
          carQueuePanels[i].reset();
        if(carSimulator.newSimulationSession(meanArrival, diviasionArrival, payTime, nrOfQueues))
        {
          simulationResultPanel.startClock();
          simulationResultPanel.reset();
          this.setText("Stop Simulation");
        }
      }
      else
      {
        carSimulator.stopSimulation();
        simulationResultPanel.stopClock();
        this.setText("Start Simulation");
      }
    }
  }
  final int MAX_CARQUEUES = 5;
  CarSimulator carSimulator = null;
  CarQueuePanel[] carQueuePanels = new CarQueuePanel[MAX_CARQUEUES];
  SimulationResultPanel simulationResultPanel = new SimulationResultPanel();
  SimulationControlePanel simulationControlePanel = new SimulationControlePanel();
  SimulationButton simulationButton = new SimulationButton("Start Simulation");

  JPanel[] panels = new JPanel[3];
  Border etched = BorderFactory.createEtchedBorder();
  Container pane = getContentPane();
  GridLayout layout = new GridLayout(3, 1);
  public void init()
  {
    //setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    //setTitle("Øresunds-bridge car simulation system v1.0");
    //setSize(600, 420);
    pane.setLayout(layout);

    panels[0] = new TitelPanel();;
    panels[0].setBorder(etched);

    panels[1] = new JPanel();
    panels[1].setBorder(etched);
    panels[1].setLayout(new BorderLayout());
    JPanel skod = new JPanel();
    skod.setLayout(new GridLayout(1, 2));
    skod.add(simulationResultPanel);
    skod.add(simulationControlePanel);
    panels[1].add(skod, BorderLayout.CENTER);
    panels[1].add(simulationButton, BorderLayout.SOUTH);

    panels[2] = new JPanel();
    panels[2].setBorder(BorderFactory.createTitledBorder(etched, "Car queue information:"));
    panels[2].setLayout(new GridLayout(1, 5));

    for(int i = 0; i < MAX_CARQUEUES; i++)
    {
      carQueuePanels[i] = new CarQueuePanel(i+1);
      carQueuePanels[i].setBorder(etched);
      panels[2].add(carQueuePanels[i]);
    }
    for(int i = 0; i < 3; i++)
      pane.add(panels[i]);

    carSimulator = new CarSimulator(carQueuePanels, simulationResultPanel);
  }
  public void start() { ((TitelPanel)panels[0]).start(); }
  public void stop()
  { carSimulator.stopSimulation();
    simulationResultPanel.stopClock();
    simulationButton.setText("Start Simulation");
  }
}
//-----------------------------------------------------------------------------
//T i t e l P a n e l
//Denne klasse laver et panel hvorpå program overskriften bliver skrevet.
//Herudover scrolles navnet samt programmørens navn ind fra hver sin side, når
//man trykker på musen over panelet.
class TitelPanel extends JPanel implements Runnable
{
  //Indlejret klase til at tage sig af museklikket (starter en scroller tråd)
  class MouseStuff extends MouseAdapter
  {
    public void mousePressed(MouseEvent evt) { start(); }
  }

  Thread runner = null;
  float x1 = 0;
  float y1 = 30;
  float x2 = 600;
  float y2 = 60;
  String message1 = "Realtime Bridge Simulation";
  String message2 = "by Mads Lenskjær";
  int strWidth1, strWidth2, strHeight1, strHeight2;
  Font font1, font2;
  boolean finishedScrolling = false;

  TitelPanel()
  {
    super();
    addMouseListener(new MouseStuff());
  }

  //Funktionen sætter grafikken op for overskriften, dvs måler størrelsen og bredten
  //således at teksterne kan blive scrollet korrekt ind på midten. Herefter sparker
  //den en tråd igang som gør flytte arbejdet.
  public void start()
  {
    if(runner != null)
      return;
    runner = new Thread(this);
    finishedScrolling = false;
    Graphics2D g2D = null;
    while((g2D = (Graphics2D)getGraphics()) == null) ;

    font1 = new Font("arial", Font.PLAIN, 30);
    g2D.setFont(font1);
    FontRenderContext context = g2D.getFontRenderContext();
    Rectangle2D bounds = font1.getStringBounds(message1, context);
    strWidth1 = (int)bounds.getWidth();
    strHeight1 = (int)bounds.getHeight();
    y1 = strHeight1 + 20;
    x1 = -strWidth1;
    font2 = new Font("arial", Font.PLAIN+Font.ITALIC, 22);
    g2D.setFont(font2);
    context = g2D.getFontRenderContext();
    bounds = font2.getStringBounds(message2, context);
    strWidth2 = (int)bounds.getWidth();
    strHeight2 = (int)bounds.getHeight();
    y2 = y1+ strHeight2+20;
    x2 = getWidth();
    runner.start();
  }
  public void paintComponent(Graphics g)
  {
    super.paintComponent(g);
    Graphics2D g2D = (Graphics2D)g;
    g2D.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
    g2D.setFont(font1);
    if(finishedScrolling)
    { x1 = (int)((getWidth()/2)-(strWidth1/2));
      x2 = (int)((getWidth()/2));
    }
    g2D.drawString(message1,x1,y1);
    g2D.setFont(font2);
    g2D.drawString(message2, x2,y2);
  }
  public void run()
  {
    boolean finished1 = false;
    boolean finished2 = false;
    int n = 0;
    int midtPoint;
    try
    { while(!finished1 && !runner.interrupted() || !finished2 && !runner.interrupted())
      {
        repaint();
        midtPoint = (int)((getWidth()/2)-(strWidth1/2));

        if(x1 < (int)(midtPoint))
          x1 += (0.0006*(n*n));
        else
          finished1 = true;

        midtPoint = (int)((getWidth()/2));
        if(x2 > midtPoint)
          x2 -= (0.0006*(n*n));
        else
          finished2 = true;
        n++;
        runner.sleep(1);
      }
    }catch(InterruptedException e) {}
    finishedScrolling = true;
    runner = null;
  }
}
//-----------------------------------------------------------------------------
//S i m u l a t i o n C o n t r o l e P a n e l
//Klassen indeholder de kontrol parametre som simulationen udføres på baggrund af
class SimulationControlePanel extends JPanel
{
  ValueLabel nrOfQueuesPanel = new ValueLabel("Open queues:", 5);
  ValueLabel payTimePanel = new ValueLabel("Pay time (sec): ", 5);
  ValueLabel meanArrivalTimePanel = new ValueLabel("Mean Arrival Time(sec): ", 5);
  ValueLabel diviasionArrivalTimePanel = new ValueLabel("Diviation arrival Time(sec): ", 5);

  static final int NR_OF_QUEUES = 0;
  static final int PAY_TIME = 1;
  static final int MEAN_ARRIVAL = 2;
  static final int DIVIASION_ARRIVAL = 3;
  SimulationControlePanel()
  {
    setLayout(new GridLayout(4, 1));
    setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), "Simulation settings:"));
    add(nrOfQueuesPanel);
    add(payTimePanel);
    add(meanArrivalTimePanel);
    add(diviasionArrivalTimePanel);
    nrOfQueuesPanel.setTextField("5");
    payTimePanel.setTextField("20");
    meanArrivalTimePanel.setTextField("4");
    diviasionArrivalTimePanel.setTextField("2");
  }
  public int getField(int type)
  {
    String str = "";
    int Value = -1;
    try
    {
      switch(type)
      { case NR_OF_QUEUES: str = nrOfQueuesPanel.getTextField(); break;
        case PAY_TIME: str = payTimePanel.getTextField(); break;
        case MEAN_ARRIVAL: str = meanArrivalTimePanel.getTextField(); break;
        case DIVIASION_ARRIVAL: str = diviasionArrivalTimePanel.getTextField(); break;
      }
      Value = Integer.parseInt(str);
    }catch(Exception e) { return -1; }
    return Value;
  }
}
//-----------------------------------------------------------------------------
//S i m u l a t i o n R e s u l t P a n e l
//Klassen indeholder de vigtigste simulations resultater som løbende bliver opdateret
class SimulationResultPanel extends JPanel
{
  CombiLabel processedCarsLabel = new CombiLabel("Processed Cars:");
  CombiLabel meanWaitingTimeLabel = new CombiLabel("Mean waitingtime:");
  CombiLabel maximalWaitingTimeLabel = new CombiLabel("Maximal waitingtime:");
  ClockPanel clockPanel = new ClockPanel("Simulation Time:");

  SimulationResultPanel()
  {
    setLayout(new GridLayout(4, 1));
    setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), "Simulation result:"));
    add(processedCarsLabel);
    add(meanWaitingTimeLabel);
    add(maximalWaitingTimeLabel);
    add(clockPanel);
  }
  public void setProcessedCars(int nr) { processedCarsLabel.setDataLabel(nr); }
  public void setMeanWaitingTime(int time)
  { meanWaitingTimeLabel.setDataLabel(ClockPanel.sec2Time(time));
  }
  public void setMaximalWaitingTime(int time)
  { maximalWaitingTimeLabel.setDataLabel(ClockPanel.sec2Time(time));
  }
  public void startClock() { clockPanel.startClock(); }
  public void stopClock() { clockPanel.stopClock(); }
  public void reset()
  { processedCarsLabel.setDataLabel(0);
    meanWaitingTimeLabel.setDataLabel(0);
    maximalWaitingTimeLabel.setDataLabel(0);
  }
}
//-----------------------------------------------------------------------------
//C a r Q u e u e P a n e l
//Klassen sætter grafikken op tilhørende en bil kø. Der indeholder den felter
//angivende antallet af ventende biler, antallet af færdige biler, samt
//bodens resttide
class CarQueuePanel extends JPanel
{
  JLabel queueLabel = new JLabel("", JLabel.CENTER);
  CombiLabel nrOfCarsLabel = new CombiLabel("Waiting:");
  CombiLabel nrOfMyProcessedCarsLabel = new CombiLabel("Finished:");
  CombiLabel freeTimeLabel = new CombiLabel("Spare time:");

  CarQueuePanel(int n)
  {
    Font font = new Font("arial", Font.PLAIN+Font.BOLD,12);
    queueLabel.setFont(font);
    queueLabel.setText("Queue nr " + n);
    setLayout(new GridLayout(4, 1, 0,3));
    add(queueLabel);
    add(nrOfCarsLabel);
    add(nrOfMyProcessedCarsLabel);
    add(freeTimeLabel);
  }
  public void setWaitingCars(int nr) { nrOfCarsLabel.setDataLabel(nr); }
  public void setFinishedCars(int nr) { nrOfMyProcessedCarsLabel.setDataLabel(nr); }
  public void setFreeTime(int time) { freeTimeLabel.setDataLabel(ClockPanel.sec2Time(time)); }
  public void reset()
  { nrOfCarsLabel.setDataLabel(0);
    nrOfMyProcessedCarsLabel.setDataLabel(0);
    freeTimeLabel.setDataLabel(0);
  }
}
//-----------------------------------------------------------------------------
//C l o c k P a n e l
//Klassen indeholder 2 labels hvorpå det sidste kan angive klokken, som et
//digital ur (timer:min:sec). Herudover implementere klassen også en tråd
//der tæller uret op sekundtvis!
class ClockPanel extends JPanel implements Runnable
{
  Thread runner = null;
  long startTime = 0;
  JLabel clockNameLabel = new JLabel("", SwingConstants.LEFT);
  JLabel clockLabel = new JLabel("0", SwingConstants.RIGHT);
  Font font = new Font("arial", Font.PLAIN,12);

  ClockPanel(String str)
  {
    setLayout(new BorderLayout());
    clockNameLabel.setFont(font);
    clockLabel.setFont(font);
    clockNameLabel.setText(str);
    setBorder(BorderFactory.createEtchedBorder());

    add(clockNameLabel,BorderLayout.WEST);
    add(clockLabel,BorderLayout.EAST);
  }
  public static String sec2Time(int sec)
  {
    String time = (""+ (int)(sec/60));
    time += ":";
    if(sec%60 < 10)
      time += "0";
    time += ("" + (int)(sec%60));
    return time;
  }
  public void startClock()
  { runner = new Thread(this);
    startTime = System.currentTimeMillis();
    runner.start();
  }
  public void stopClock()
  { runner.interrupt();
    runner = null;
  }
  public void run()
  { try
    { while(!runner.interrupted())
      {
        Thread.sleep(1000);
        clockLabel.setText(sec2Time((int)((System.currentTimeMillis() - startTime)/1000)));
      }
    }catch(InterruptedException e) {}
  }
}
//-----------------------------------------------------------------------------
//V a l u e L a b e l
//Klassen laver et panel hvorpå der er et label(data overskrift) og et text felt hvori
//brugeren kan skrive indputs til programmet.!
class ValueLabel extends JPanel
{
  JLabel textLabel = new JLabel("", SwingConstants.LEFT);
  JTextField textField = null;
  Font font = new Font("arial", Font.PLAIN,12);

  ValueLabel(String text, int characters)
  {
    textField = new JTextField(characters);
    setLayout(new BorderLayout());
    textLabel.setFont(font);
    textField.setFont(font);
    textLabel.setText(text);
    textField = new JTextField(characters);
    add(textLabel, BorderLayout.WEST);
    add(textField, BorderLayout.EAST);
    setBorder(BorderFactory.createEtchedBorder());
  }
  public void setTextField(String value) { textField.setText(value); }
  public String getTextField() { return textField.getText(); }
}
//-----------------------------------------------------------------------------
//C o m b i L a b e l
//Klassen laver et panel hvorpå der er 2 labels, hvor den første benyttes som overskrift
//og den anden som en slags data værdi felt til display for en variabel..
class CombiLabel extends JPanel
{
  JLabel strLabel = new JLabel("", SwingConstants.LEFT);
  JLabel dataLabel = new JLabel("0", SwingConstants.RIGHT);
  Font font = new Font("arial", Font.PLAIN, 12);
  CombiLabel(String str)
  {
    setLayout(new BorderLayout());
    strLabel.setFont(font);
    dataLabel.setFont(font);
    strLabel.setText(str);
    setBorder(BorderFactory.createEtchedBorder());
    add(strLabel, BorderLayout.WEST);
    add(dataLabel, BorderLayout.EAST);
  }
  public void setDataLabel(int data) {  dataLabel.setText(""+data); }
  public void setDataLabel(double data) { dataLabel.setText(""+data); }
  public void setDataLabel(String data) { dataLabel.setText(data); }
}
//-----------------------------------------------------------------------------