/*
 * Copyright (C) 2013 4th Line GmbH, Switzerland
 *
 * The contents of this file are subject to the terms of either the GNU
 * Lesser General Public License Version 2 or later ("LGPL") or the
 * Common Development and Distribution License Version 1 or later
 * ("CDDL") (collectively, the "License"). You may not use this file
 * except in compliance with the License. See LICENSE.txt for more
 * information.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 */

package org.fourthline.cling.support.shared;

import org.fourthline.cling.support.shared.log.LogView;
import org.seamless.util.logging.LoggingUtil;
import org.seamless.swing.Application;
import org.seamless.swing.logging.LogMessage;
import org.seamless.swing.logging.LoggingHandler;
import org.seamless.util.OS;

import javax.inject.Inject;
import javax.inject.Singleton;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.util.logging.Handler;
import java.util.logging.LogManager;

/**
 * @author Christian Bauer
 */
public abstract class Main implements ShutdownHandler, Thread.UncaughtExceptionHandler {

    @Inject
    LogView.Presenter logPresenter;

    final protected JFrame errorWindow = new JFrame();

    // In addition to the JUL-configured handler, show log messages in the UI
    final protected LoggingHandler loggingHandler =
        new LoggingHandler() {
            @Override
            protected void log(LogMessage msg) {
                logPresenter.pushMessage(msg);
            }
        };

    protected boolean isRegularShutdown;

    public void init() {

        try {
            // Platform specific setup
            if (OS.checkForMac())
                NewPlatformApple.setup(this, getAppName());

            // Some UI stuff (of course, why would the OS L&F be the default -- too easy?!)
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());

        } catch (Exception ex) {
            // Ignore...
        }

        // Exception handler
        errorWindow.setPreferredSize(new Dimension(900, 400));
        errorWindow.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent windowEvent) {
                errorWindow.dispose();
            }
        });
        Thread.setDefaultUncaughtExceptionHandler(this);

        // Shutdown behavior
        Runtime.getRuntime().addShutdownHook(new Thread() {
            @Override
            public void run() {
                if (!isRegularShutdown) { // Don't run the hook if everything is already stopped
                    shutdown();
                }
            }
        });

        // Wire logging UI into JUL, don't reset JUL root logger but
        // add our handler if there is a JUL config file
        if (System.getProperty("java.util.logging.config.file") == null) {
            LoggingUtil.resetRootHandler(loggingHandler);
        } else {
            LogManager.getLogManager().getLogger("").addHandler(loggingHandler);
        }
    }

    @Override
    public void shutdown() {
        isRegularShutdown = true;
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                errorWindow.dispose();
            }
        });
    }

    @Override
    public void uncaughtException(Thread thread, final Throwable throwable) {

        System.err.println("In thread '" + thread + "' uncaught exception: " + throwable);
        throwable.printStackTrace(System.err);

        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                errorWindow.getContentPane().removeAll();

                JTextArea textArea = new JTextArea();
                textArea.setEditable(false);
                StringBuilder text = new StringBuilder();

                text.append("An exceptional error occurred!\nYou can try to continue or exit the application.\n\n");
                text.append("Please tell us about this here:\nhttp://www.4thline.org/projects/mailinglists-cling.html\n\n");
                text.append("-------------------------------------------------------------------------------------------------------------\n\n");
                Writer stackTrace = new StringWriter();
                throwable.printStackTrace(new PrintWriter(stackTrace));
                text.append(stackTrace.toString());

                textArea.setText(text.toString());
                JScrollPane pane = new JScrollPane(textArea);
                errorWindow.getContentPane().add(pane, BorderLayout.CENTER);

                JButton exitButton = new JButton("Exit Application");
                exitButton.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        System.exit(1);
                    }
                });

                errorWindow.getContentPane().add(exitButton, BorderLayout.SOUTH);

                errorWindow.pack();
                Application.center(errorWindow);
                textArea.setCaretPosition(0);

                errorWindow.setVisible(true);
            }
        });
    }

    protected void removeLoggingHandler() {
        LogManager.getLogManager().getLogger("").removeHandler(loggingHandler);
    }

    abstract protected String getAppName();

}
