/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.apache.log4j.chainsaw;

import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.text.NumberFormat;

import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.border.Border;

import org.apache.log4j.chainsaw.icons.ChainsawIcons;
import org.apache.log4j.chainsaw.messages.MessageCenter;


/**
 * A general purpose status bar for all Frame windows
 *
 * @author Paul Smith &lt;psmith@apache.org&gt;
 *
 */
public class ChainsawStatusBar extends JPanel {
  private static final int DELAY_PERIOD = 5000;
  private static final String DEFAULT_MSG = "Welcome to Chainsaw v2!";
  private final JLabel statusMsg = new JLabel(DEFAULT_MSG);
  private final JLabel searchMatchLabel = new JLabel("", SwingConstants.CENTER);
  private final JLabel pausedLabel = new JLabel("", SwingConstants.CENTER);
  private final JLabel lineSelectionLabel = new JLabel("", SwingConstants.CENTER);
  private final JLabel eventCountLabel = new JLabel("", SwingConstants.CENTER);
  private final JLabel receivedEventLabel = new JLabel("", SwingConstants.CENTER);
  private final JLabel receivedConnectionlabel = new JLabel("", SwingConstants.CENTER);
  private volatile long lastReceivedConnection = System.currentTimeMillis();
  private final Thread connectionThread;
  private final Icon pausedIcon = new ImageIcon(ChainsawIcons.PAUSE);
  private final Icon netConnectIcon =
    new ImageIcon(ChainsawIcons.ANIM_NET_CONNECT);
  private final NumberFormat nf = NumberFormat.getNumberInstance();
  private final Border statusBarComponentBorder =
    BorderFactory.createLineBorder(statusMsg.getBackground().darker());
  private final LogUI logUI;

    public ChainsawStatusBar(LogUI logUI) {
    setLayout(new GridBagLayout());
    this.logUI = logUI;
    nf.setMaximumFractionDigits(0);
    nf.setMinimumFractionDigits(0);
    nf.setGroupingUsed(false);

    JPanel statusMsgPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 2, 2));
    
    statusMsgPanel.add(statusMsg);
    statusMsgPanel.setBorder(statusBarComponentBorder);
    
    pausedLabel.setBorder(statusBarComponentBorder);
    pausedLabel.setMinimumSize(
      new Dimension(pausedIcon.getIconWidth(), pausedIcon.getIconHeight()));

    pausedLabel.setToolTipText(
      "Shows whether the current Log panel is paused or not");

    receivedEventLabel.setBorder(statusBarComponentBorder);
    receivedEventLabel.setToolTipText(
      "Indicates whether Chainsaw is receiving events, and how fast it is processing them");
    receivedEventLabel.setMinimumSize(
      new Dimension(
        receivedEventLabel.getFontMetrics(receivedEventLabel.getFont())
                          .stringWidth("99999999999.9/s") + 5,
        (int) receivedEventLabel.getPreferredSize().getHeight()));

	eventCountLabel.setBorder(statusBarComponentBorder);
	eventCountLabel.setToolTipText("<# viewable events>:<# total events>");
	eventCountLabel.setMinimumSize(
	new Dimension(
	eventCountLabel.getFontMetrics(eventCountLabel.getFont())
						.stringWidth("Filtered/Total: 999999999999:999999999999") + 5,
	  (int) eventCountLabel.getPreferredSize().getHeight()));
	  
    searchMatchLabel.setBorder(statusBarComponentBorder);
    searchMatchLabel.setToolTipText("<# viewable events>:<# total events>");
    searchMatchLabel.setMinimumSize(
    new Dimension(
    searchMatchLabel.getFontMetrics(eventCountLabel.getFont()).stringWidth("Find matches: 999999999999") + 5,
            (int) searchMatchLabel.getPreferredSize().getHeight()));

    receivedConnectionlabel.setBorder(statusBarComponentBorder);
    receivedConnectionlabel.setToolTipText(
      "Indicates whether Chainsaw has received a remote connection");
    receivedConnectionlabel.setMinimumSize(
      new Dimension(
        netConnectIcon.getIconWidth() + 4,
        (int) receivedConnectionlabel.getPreferredSize().getHeight()));

    lineSelectionLabel.setBorder(statusBarComponentBorder);
    lineSelectionLabel.setMinimumSize(
      new Dimension(
        lineSelectionLabel.getFontMetrics(lineSelectionLabel.getFont())
                          .stringWidth("999999999"),
        (int) lineSelectionLabel.getPreferredSize().getHeight()));
    lineSelectionLabel.setToolTipText(
      "The current line # selected");

    JComponent[] toFix =
      new JComponent[] {
        searchMatchLabel, eventCountLabel,
        receivedConnectionlabel, lineSelectionLabel, receivedEventLabel,
        pausedLabel
      };

    for (int i = 0; i < toFix.length; i++) {
      toFix[i].setPreferredSize(toFix[i].getMinimumSize());
      toFix[i].setMaximumSize(toFix[i].getMinimumSize());
    }

    statusMsg.setMinimumSize(pausedLabel.getPreferredSize());
    statusMsg.setToolTipText("Shows messages from Chainsaw");

    GridBagConstraints c = new GridBagConstraints();
    c.insets = new Insets(2, 2, 2, 2);
    c.weightx = 1.0;
    c.weighty = 1.0;
    c.ipadx = 2;
    c.ipady = 2;
    c.gridx = 0;
    c.gridy = 0;
    c.fill = GridBagConstraints.BOTH;
    c.anchor = GridBagConstraints.WEST;

    add(statusMsgPanel, c);

    c.weightx = 0.0;
    c.weighty = 0.0;
    c.gridx = 1;
    add(receivedConnectionlabel, c);

	c.weightx = 0.0;
	c.weighty = 0.0;
	c.gridx = 2;
	add(lineSelectionLabel, c);
	
    c.weightx = 0.0;
    c.weighty = 0.0;
    c.gridx = 3;
    add(searchMatchLabel, c);

	c.weightx = 0.0;
	c.weighty = 0.0;
	c.gridx = 4;
	add(eventCountLabel, c);

    c.weightx = 0.0;
    c.weighty = 0.0;
    c.gridx = 5;
    add(receivedEventLabel, c);

    c.weightx = 0.0;
    c.weighty = 0.0;
    c.gridx = 6;

    add(pausedLabel, c);

    connectionThread =
      new Thread(
        new Runnable() {
          public void run() {
            while (true) {
              try {
                Thread.sleep(DELAY_PERIOD);
              } catch (InterruptedException e) {
              }

              Icon icon = null;

              if (
                (System.currentTimeMillis() - lastReceivedConnection) < DELAY_PERIOD) {
                icon = netConnectIcon;
              }

              final Icon theIcon = icon;
              SwingUtilities.invokeLater(
                new Runnable() {
                  public void run() {
                    receivedConnectionlabel.setIcon(theIcon);
                  }
                });
            }
          }
        });
    connectionThread.start();
  }

  void setDataRate(final double dataRate) {
    SwingUtilities.invokeLater(
      new Runnable() {
        public void run() {
          receivedEventLabel.setText(nf.format(dataRate) + "/s");
        }
      });
  }

  /**
   * Indicates a new connection has been established between
   * Chainsaw and some remote host
   * @param source
   */
  void remoteConnectionReceived(String source) {
    lastReceivedConnection = System.currentTimeMillis();
    MessageCenter.getInstance().getLogger().info("Connection received from " + source);
    connectionThread.interrupt();

    //    TODO and maybe play a sound?
  }

  /**
   * Called when the paused state of the LogPanel has been updated
   * @param isPaused
   * @param tabName
   */
  void setPaused(final boolean isPaused, String tabName) {
    if (tabName.equals(logUI.getActiveTabName())) {
      Runnable runnable =
        new Runnable() {
          public void run() {
              pausedLabel.setIcon(isPaused ? pausedIcon : null);
              pausedLabel.setToolTipText(
                isPaused ? "This Log panel is currently paused"
                         : "This Log panel is not paused");
          }
      };
      SwingUtilities.invokeLater(runnable);
    }
  }

  void setSelectedLine(
          final int selectedLine, final int lineCount, final int total, String tabName) {
    if (tabName.equals(logUI.getActiveTabName())) {
        SwingUtilities.invokeLater(
          new Runnable() {
            public void run() {
              lineSelectionLabel.setText(selectedLine+"");
              eventCountLabel.setText("Filtered/Total: " + lineCount + ":" + total);
            }
          });
    }
  }

  void setSearchMatchCount(int searchMatchCount, String tabName) {
      if (tabName.equals(logUI.getActiveTabName())) {
          if (searchMatchCount == 0) {
            searchMatchLabel.setText("");
          } else {
            searchMatchLabel.setText("Find matches: " + searchMatchCount);
          }
      }
  }

  void setNothingSelected() {
    SwingUtilities.invokeLater(
      new Runnable() {
        public void run() {
          lineSelectionLabel.setText("");
        }
      });
  }

  void clear() {
    setMessage(DEFAULT_MSG);
    setNothingSelected();
    SwingUtilities.invokeLater(
      new Runnable() {
        public void run() {
          receivedEventLabel.setText("");
        }
      });
  }

  public void setMessage(final String msg) {
    SwingUtilities.invokeLater(
      new Runnable() {
        public void run() {
          statusMsg.setText(" " + msg);
        }
      });
  }
}
