/*
 * 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.BorderLayout;
import java.awt.Dimension;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.IOException;
import java.util.Properties;
import javax.swing.BorderFactory;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;

/**
 * The main application.
 *
 * @author <a href="mailto:oliver@puppycrawl.com">Oliver Burn</a>
 */
public class Main extends JFrame {
    /** the default port number to listen on **/
    private static final int DEFAULT_PORT = 4445;

    /** name of property for port name **/
    public static final String PORT_PROP_NAME = "chainsaw.port";

    /** use to log messages **/
    private static final Logger LOG = Logger.getLogger(Main.class);

    /**
     * Creates a new <code>Main</code> instance.
     */
    private Main() {
	super("CHAINSAW - Log4J Log Viewer");
	// create the all important model
	final MyTableModel model = new MyTableModel();

	// Create the menu bar.
	final JMenuBar menuBar = new JMenuBar();
	setJMenuBar(menuBar);
	final JMenu menu = new JMenu("File");
	menuBar.add(menu);

	try {
	    final LoadXMLAction lxa = new LoadXMLAction(this, model);
	    final JMenuItem loadMenuItem = new JMenuItem("Load file...");
	    menu.add(loadMenuItem);
	    loadMenuItem.addActionListener(lxa);
	} catch (NoClassDefFoundError e) {
	    LOG.info("Missing classes for XML parser", e);
	    JOptionPane.showMessageDialog(this, "XML parser not in classpath - unable to load XML events.", "CHAINSAW",
		    JOptionPane.ERROR_MESSAGE);
	} catch (Exception e) {
	    LOG.info("Unable to create the action to load XML files", e);
	    JOptionPane.showMessageDialog(this, "Unable to create a XML parser - unable to load XML events.",
		    "CHAINSAW", JOptionPane.ERROR_MESSAGE);
	}

	final JMenuItem exitMenuItem = new JMenuItem("Exit");
	menu.add(exitMenuItem);
	exitMenuItem.addActionListener(ExitAction.INSTANCE);

	// Add control panel
	final ControlPanel cp = new ControlPanel(model);
	getContentPane().add(cp, BorderLayout.NORTH);

	// Create the table
	final JTable table = new JTable(model);
	table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
	final JScrollPane scrollPane = new JScrollPane(table);
	scrollPane.setBorder(BorderFactory.createTitledBorder("Events: "));
	scrollPane.setPreferredSize(new Dimension(900, 300));

	// Create the details
	final JPanel details = new DetailPanel(table, model);
	details.setPreferredSize(new Dimension(900, 300));

	// Add the table and stack trace into a splitter
	final JSplitPane jsp = new JSplitPane(JSplitPane.VERTICAL_SPLIT, scrollPane, details);
	getContentPane().add(jsp, BorderLayout.CENTER);

	addWindowListener(new WindowAdapter() {
	    public void windowClosing(WindowEvent aEvent) {
		ExitAction.INSTANCE.actionPerformed(null);
	    }
	});

	pack();
	setVisible(true);

	setupReceiver(model);
    }

    /**
     * Setup recieving messages.
     *
     * @param aModel a <code>MyTableModel</code> value
     */
    private void setupReceiver(MyTableModel aModel) {
	int port = DEFAULT_PORT;
	final String strRep = System.getProperty(PORT_PROP_NAME);
	if (strRep != null) {
	    try {
		port = Integer.parseInt(strRep);
	    } catch (NumberFormatException nfe) {
		LOG.fatal("Unable to parse " + PORT_PROP_NAME + " property with value " + strRep + ".");
		JOptionPane.showMessageDialog(this, "Unable to parse port number from '" + strRep + "', quitting.",
			"CHAINSAW", JOptionPane.ERROR_MESSAGE);
		System.exit(1);
	    }
	}

	try {
	    final LoggingReceiver lr = new LoggingReceiver(aModel, port);
	    lr.start();
	} catch (IOException e) {
	    LOG.fatal("Unable to connect to socket server, quiting", e);
	    JOptionPane.showMessageDialog(this, "Unable to create socket on port " + port + ", quitting.", "CHAINSAW",
		    JOptionPane.ERROR_MESSAGE);
	    System.exit(1);
	}
    }

    ////////////////////////////////////////////////////////////////////////////
    // static methods
    ////////////////////////////////////////////////////////////////////////////

    /** initialise log4j **/
    private static void initLog4J() {
	final Properties props = new Properties();
	props.setProperty("log4j.rootLogger", "DEBUG, A1");
	props.setProperty("log4j.appender.A1", "org.apache.log4j.ConsoleAppender");
	props.setProperty("log4j.appender.A1.layout", "org.apache.log4j.TTCCLayout");
	PropertyConfigurator.configure(props);
    }

    /**
     * The main method.
     *
     * @param aArgs ignored
     */
    public static void main(String[] aArgs) {
	initLog4J();
	new Main();
    }
}
