/* 
 *  Copyright © 2006, 2008 Christian Persch
 *
 *  This program 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, or (at your option)
 *  any later version.
 *
 *  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.  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 program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 *  Some code taken from mozilla/embedding/components/windowwatcher/src/nsPromptService.cpp
 *  which was under MPL/LGPL/GPL tri-licence and is here being used under the licence above.
 *  Original notice:
 *
 *   The Original Code is mozilla.org code.
 *
 *   The Initial Developer of the Original Code is
 *   Netscape Communications Corporation.
 *   Portions created by the Initial Developer are Copyright (C) 2001
 *   the Initial Developer. All Rights Reserved.
 */

#include <xpcom-config.h>
#include <config.h>

#ifdef HAVE_GECKO_1_9
#include <nsIDOMDocumentEvent.h>
#include <nsIDOMDocument.h>
#include <nsIDOMEvent.h>
#include <nsIDOMEventTarget.h>
#include <nsIPrivateDOMEvent.h>
#include <nsPIDOMWindow.h>
#include <nsServiceManagerUtils.h>
#include <nsThreadUtils.h>
#endif /* HAVE_GECKO_1_9 */

#include <gtk/gtkdialog.h>

#include "EphyUtils.h"

#include "AutoModalDialog.h"

AutoModalDialog::AutoModalDialog (nsIDOMWindow *aWindow,
                                  PRBool aNotifyDOM)
  : mWindow (aWindow),
    mStack (),
    mModalState (aWindow),
#ifdef HAVE_GECKO_1_9
    mDefaultEnabled (DispatchEvent ("DOMWillOpenModalDialog", aNotifyDOM)),
#else
    mDefaultEnabled (PR_TRUE),
#endif
    mContextPushed (NS_SUCCEEDED (mStack.Init ()))
{
}

AutoModalDialog::~AutoModalDialog ()
{
#ifdef HAVE_GECKO_1_9
  if (mDefaultEnabled) {
    DispatchEvent ("DOMModalDialogClosed", PR_TRUE);
  }
#endif /* HAVE_GECKO_1_9 */
}

GtkWindow *
AutoModalDialog::GetParent ()
{
  return GTK_WINDOW (EphyUtils::FindGtkParent (mWindow));
}

int
AutoModalDialog::Run (GtkDialog *aDialog)
{
  NS_ASSERTION (ShouldShow(), "Calling ::Run on a prevented dialogue!");

  nsCOMPtr<nsPIDOMWindow> pWindow (do_QueryInterface (mWindow));

  // Reset popup state while opening a modal dialog, and firing
  // events about the dialog, to prevent the current state from
  // being active the whole time a modal dialog is open.
  nsAutoPopupStatePusher popupStatePusher (pWindow, openAbused);
  
#if 1
  return gtk_dialog_run (aDialog);
#else
  /* Do NOT use gtk_dialog_run here, since it blocks the network thread!
   * See https://bugzilla.mozilla.org/show_bug.cgi?id=338225
   */
  
  g_object_ref_sink (aDialog);
  mResponse = GTK_RESPONSE_DELETE_EVENT;

  gulong responseHandler = g_signal_connect (aDialog, "response",
                                             G_CALLBACK (ResponseCallback),
                                             reinterpret_cast<void*>(this));
  gulong deleteHandler = g_signal_connect (aDialog, "delete-event",
                                           G_CALLBACK (DeleteCallback), NULL);

  gtk_window_present (GTK_WINDOW (aDialog));

  nsCOMPtr<nsIThread> thread (do_GetCurrentThread ());
  NS_ASSERTION (thread, "No UI thread?");
  
  mContinueModalLoop = PR_TRUE;
  while (mContinueModalLoop) {
    if (!NS_ProcessNextEvent (thread))
      break;
  }

  g_signal_handler_disconnect (aDialog, responseHandler);
  g_signal_handler_disconnect (aDialog, deleteHandler);
  g_object_unref (aDialog);

  return mResponse;
#endif
}

#ifdef HAVE_GECKO_1_9

PRBool
AutoModalDialog::DispatchEvent (const char *aEventName,
                                PRBool aDoNotify)
{
  if (!mWindow || !aDoNotify) {
    return PR_TRUE;
  }

  nsCOMPtr<nsIDOMDocument> domdoc;
  mWindow->GetDocument (getter_AddRefs (domdoc));

  nsCOMPtr<nsIDOMDocumentEvent> docevent (do_QueryInterface (domdoc));
  nsCOMPtr<nsIDOMEvent> event;

  PRBool defaultActionEnabled = PR_TRUE;

  if (docevent) {
    docevent->CreateEvent (NS_LITERAL_STRING ("Events"), getter_AddRefs (event));

    nsCOMPtr<nsIPrivateDOMEvent> privateEvent (do_QueryInterface (event));
    if (privateEvent) {
      event->InitEvent (NS_ConvertASCIItoUTF16 (aEventName), PR_TRUE, PR_TRUE);

      privateEvent->SetTrusted(PR_TRUE);

      nsCOMPtr<nsIDOMEventTarget> target (do_QueryInterface (mWindow));

      target->DispatchEvent (event, &defaultActionEnabled);
    }
  }

  return defaultActionEnabled;
}

/* static */ void PR_CALLBACK
AutoModalDialog::ResponseCallback (GtkWidget *aDialog,
                                   int aResponse,
                                   void *aData)
{
  AutoModalDialog *obj = reinterpret_cast<AutoModalDialog*>(aData);

  gtk_widget_hide (aDialog);
  obj->mResponse = aResponse;
  obj->mContinueModalLoop = PR_FALSE;
}

/* static */ gboolean PR_CALLBACK
AutoModalDialog::DeleteCallback (GtkWidget *aDialog,
                                 void *aEvent,
                                 void *aData)
{
  gtk_dialog_response (GTK_DIALOG (aDialog), GTK_RESPONSE_DELETE_EVENT);
  return TRUE;
}

#endif /* HAVE_GECKO_1_9 */
