/* LanguageTool, a natural language style checker
 * Copyright (C) 2011 Daniel Naber (http://www.danielnaber.de)
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
 * USA
 */
package org.languagetool.openoffice;

import java.awt.event.WindowEvent;
import java.awt.event.WindowFocusListener;
import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.util.Date;

import javax.swing.JDialog;
import javax.swing.JOptionPane;
import javax.swing.UIManager;

import org.languagetool.JLanguageTool;
import org.languagetool.openoffice.OfficeTools.OfficeProductInfo;
import org.languagetool.tools.Tools;

import com.sun.star.uno.XComponentContext;

/**
 * Writes Messages to screen or log-file
 * @since 4.3
 * @author Fred Kruse, Marcin Miłkowski
 */
public class MessageHandler {
  
  private static final String logLineBreak = System.lineSeparator();  //  LineBreak in Log-File (MS-Windows compatible)
  
  private static boolean isOpen = false;
  
  private static boolean testMode;
  
  MessageHandler(XComponentContext xContext) {
    initLogFile(xContext);
  }

  /**
   * Initialize log-file
   */
  private static void initLogFile(XComponentContext xContext) {
    try (OutputStream stream = new FileOutputStream(OfficeTools.getLogFilePath(xContext));
        OutputStreamWriter writer = new OutputStreamWriter(stream, StandardCharsets.UTF_8);
        BufferedWriter br = new BufferedWriter(writer)
        ) {
      Date date = new Date();
      OfficeProductInfo officeInfo = OfficeTools.getOfficeProductInfo(xContext);
      writer.write("LT office integration log from " + date + logLineBreak + logLineBreak);
      writer.write("LanguageTool " + JLanguageTool.VERSION + " (" + JLanguageTool.BUILD_DATE + ", " 
          + JLanguageTool.GIT_SHORT_ID + ")" + logLineBreak);
      writer.write("OS: " + System.getProperty("os.name") + " " 
          + System.getProperty("os.version") + " on " + System.getProperty("os.arch") + logLineBreak);
      if (officeInfo != null) { 
        writer.write(officeInfo.ooName + " " + officeInfo.ooVersion + officeInfo.ooExtension
            + " (" + officeInfo.ooVendor +"), " + officeInfo.ooLocale + logLineBreak);
      }
      writer.write(OfficeTools.getJavaInformation() + logLineBreak + logLineBreak);
    } catch (Throwable t) {
      showError(t);
    }
  }
  
  /**
   * Initialize MessageHandler
   */
  static void init(XComponentContext xContext) {
    initLogFile(xContext);
  }

  /**
   * Show an error in a dialog
   */
  public static void showError(Throwable e) {
    printException(e);
    if (testMode) {
      throw new RuntimeException(e);
    }
    String msg = "An error has occurred in LanguageTool "
        + JLanguageTool.VERSION + " (" + JLanguageTool.BUILD_DATE + "):\n" + e + "\nStacktrace:\n";
    msg += Tools.getFullStackTrace(e);
    String metaInfo = "OS: " + System.getProperty("os.name") + " on "
        + System.getProperty("os.arch") + ", Java version "
        + System.getProperty("java.version") + " from "
        + System.getProperty("java.vm.vendor");
    msg += metaInfo;
    DialogThread dt = new DialogThread(msg, true);
    e.printStackTrace();
    dt.start();
  }

  /**
   * Write to log-file
   */
  public static void printToLogFile(String str) {
    try (OutputStream stream = new FileOutputStream(OfficeTools.getLogFilePath(), true);
        OutputStreamWriter writer = new OutputStreamWriter(stream, StandardCharsets.UTF_8);
        BufferedWriter br = new BufferedWriter(writer)
        ) {
      writer.write(str + logLineBreak);
    } catch (Throwable t) {
      showError(t);
    }
  }

  /** 
   * Prints Exception to log-file  
   */
  static void printException(Throwable t) {
   printToLogFile(Tools.getFullStackTrace(t));
  }

  /**
   * Will throw exception instead of showing errors as dialogs - use only for test cases.
   */
  static void setTestMode(boolean mode) {
    testMode = mode;
  }

  /**
   * Shows a message in a dialog box
   * @param txt message to be shown
   */
  public static void showMessage(String txt) {
    showMessage(txt, true);
  }

  static void showMessage(String txt, boolean toLogFile) {
    if (toLogFile) {
      printToLogFile(txt);
    }
    DialogThread dt = new DialogThread(txt, false);
    dt.run();
  }

  /**
   * run an information message in a separate thread
   * closing if lost focus
   */
  static void showClosingInformationDialog(String text) {
    ClosingInformationThread informationDialog = new ClosingInformationThread(text);
    informationDialog.start();
  }
  
  /**
   * class to run a dialog in a separate thread
   */
  private static class DialogThread extends Thread {
    private final String text;
    private boolean isException;

    DialogThread(String text, boolean isException) {
      this.text = text;
      this.isException = isException;
    }

    @Override
    public void run() {
      if (isException) {
        if (!isOpen) {
          isOpen = true;
          JOptionPane.showMessageDialog(null, text);
          isOpen = false;
        }
      } else {
        JOptionPane.showMessageDialog(null, text);
      }
    }
  }
  
  /**
   * class to run a dialog in a separate thread
   * closing if lost focus
   */
  private static class ClosingInformationThread extends Thread {
    private final String text;
    JDialog dialog;

    ClosingInformationThread(String text) {
      this.text = text;
    }

    @Override
    public void run() {
      JOptionPane pane = new JOptionPane(text, JOptionPane.INFORMATION_MESSAGE);
      dialog = pane.createDialog(null, UIManager.getString("OptionPane.messageDialogTitle", null));
      dialog.setModal(false);
      dialog.setAutoRequestFocus(true);
      dialog.setAlwaysOnTop(true);
      dialog.addWindowFocusListener(new WindowFocusListener() {
        @Override
        public void windowGainedFocus(WindowEvent e) {
        }
        @Override
        public void windowLostFocus(WindowEvent e) {
          dialog.setVisible(false);
        }
      });
      dialog.setVisible(true);
    }
  }
  
}
