/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */

/*
 *  Copyright (C) 2002 Hiroyuki Ikezoe
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2, 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 General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include "config.h"
#include "mozilla.h"

#include <gtkmozembed.h>
#include <gtkmozembed_internal.h>

#include <mozilla-config.h>
#include <nsIDOMDocument.h>
#include <nsIDocumentViewer.h>
#include <nsIWebBrowser.h>
#include <nsIDocument.h>
#include <nsIDOMMouseEvent.h>
#include <nsIDOMKeyEvent.h>
#include <nsIDOMEventTarget.h>
#include <nsIDOMHTMLElement.h>
#include <nsIDOMNamedNodeMap.h>
#include <nsIDOMNSHTMLElement.h>  
#include <nsIWebBrowserPersist.h>
#include <nsNetUtil.h>
#include <nsIWebBrowserFind.h>
#include <nsIDOMNSDocument.h>
#include <nsIDOMNSEvent.h>
#include <nsIPrefService.h>

static gchar *mozilla_get_attribute (nsIDOMNode *node, gchar *attribute);

extern "C" glong
mozilla_get_mouse_event_info(GtkMozEmbed *b, gpointer event,  MouseEventInfo *info)
{
	nsresult result;

	nsIDOMMouseEvent *aMouseEvent =  (nsIDOMMouseEvent*)event;
             
     	nsCOMPtr<nsIDOMEventTarget> OriginalTarget;

     	nsCOMPtr<nsIDOMNSEvent> aEvent = do_QueryInterface(aMouseEvent);
     	if (!aEvent) return (CONTEXT_NONE);
	
	PRBool mod_key;
	info->modifier = 0;
	aMouseEvent->GetAltKey(&mod_key);
        if (mod_key) info->modifier |= ALT_KEY;
	
	aMouseEvent->GetShiftKey(&mod_key);
        if (mod_key) info->modifier |= SHIFT_KEY;
	
	aMouseEvent->GetMetaKey(&mod_key);
        if (mod_key) info->modifier |= META_KEY;
	
	aMouseEvent->GetCtrlKey(&mod_key);
        if (mod_key) info->modifier |= CTRL_KEY;
	
     	result = aEvent->GetOriginalTarget(getter_AddRefs(OriginalTarget));
	
     	if (NS_FAILED(result) || !OriginalTarget) return (  CONTEXT_NONE );
             
     	nsCOMPtr<nsIDOMNode> OriginalNode = do_QueryInterface(OriginalTarget);
     	if (!OriginalNode) return ( CONTEXT_NONE );
             
     	nsString nodename;
     	OriginalNode->GetNodeName(nodename);
     
     	if (nodename.EqualsWithConversion ("xul:thumb") ||
	    nodename.EqualsWithConversion ("xul:slider"))
	{
       		return (  CONTEXT_NONE );
	}

     	nsCOMPtr<nsIDOMEventTarget> EventTarget;
     	result = aMouseEvent->GetTarget(getter_AddRefs(EventTarget));
     	if (NS_FAILED(result) || !EventTarget) return (  CONTEXT_NONE );
             
	nsCOMPtr<nsIDOMNode> node = do_QueryInterface(EventTarget);
	if (!node)    return (  CONTEXT_NONE );
     
     	nsCOMPtr<nsIDOMDocument> domDoc;
     	result = node->GetOwnerDocument(getter_AddRefs(domDoc));
     	if (!NS_SUCCEEDED (result) || !domDoc) return (  CONTEXT_NONE );
     
     	nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
     	if(!doc) return (  CONTEXT_NONE );
	
     	nsCOMPtr<nsIDOMNSDocument> nsDoc = do_QueryInterface(domDoc);
     	if (!nsDoc) return (  CONTEXT_NONE );
     
	nsIURI *baseURI;
#ifdef MOZILLA_SNAPSHOT
     	result = doc->GetBaseURL(&baseURI);
#else
	result = doc->GetBaseURL(baseURI);
#endif
     	if (NS_FAILED(result) || !baseURI) return (  CONTEXT_NONE );

     	nsString mime;
     	nsDoc->GetContentType(mime);
     	if (mime.EqualsWithConversion ("text/xul"))  return (  CONTEXT_NONE );

	PRUint32 flags = CONTEXT_NONE;
	nsCOMPtr<nsIDOMHTMLElement> element;
	
     	do {
		PRUint16 type;
		node->GetNodeType(&type);

		element = do_QueryInterface(node);
		if (element)
		{
			nsAutoString tag;
			element->GetTagName(tag);

			if (tag.EqualsWithConversion("input", PR_TRUE))
			{
				flags |= CONTEXT_INPUT;
			}
			else if (tag.EqualsWithConversion("textarea", PR_TRUE))
			{
				flags |= CONTEXT_INPUT;
			}
			else if (tag.EqualsWithConversion("img", PR_TRUE))
			{
				flags |= CONTEXT_IMAGE;

				char *src = mozilla_get_attribute (node, "src");
				if (!src)  return (  CONTEXT_NONE );

			     	nsAutoString srca;
			     	srca.AssignWithConversion(src);
			     
			     	nsCString srcc,imgc;
			     	srcc.AssignWithConversion(srca);
			   
			     	result = baseURI->Resolve(srcc, imgc);
			     	g_free(src);
			   

			     	info->cinfo.img = ToNewCString(imgc);
			   
			     	if (!info->cinfo.img) return (CONTEXT_NONE);
			
		   
			}
		   
			else
			     {
				
				flags |= CONTEXT_OTHER;
			}

			nsCOMPtr<nsIDOMNamedNodeMap> attributes;
			node->GetAttributes(getter_AddRefs(attributes));
			if (attributes)
			{
				nsCOMPtr<nsIDOMNode> hrefNode;
				nsAutoString href; 
			     	href.AssignWithConversion("href");
				attributes->GetNamedItem(href, getter_AddRefs(hrefNode));
				if (hrefNode)
				{
					flags |= CONTEXT_LINK;
				     	nsAutoString linkhtml;
				     	nsCOMPtr<nsIDOMNSHTMLElement> nsElement;
				     	
				     	nsElement = do_QueryInterface (element);				      
				     	
				     	if (nsElement)
				       	{
						result = nsElement->GetInnerHTML(linkhtml);
					     	if (NS_SUCCEEDED(result) && ToNewCString(linkhtml)) 
					       		info->cinfo.linktext = g_strdup(ToNewCString(linkhtml));
					}

					char *hhref =  mozilla_get_attribute (node, "href");
					if (!hhref)   return (  CONTEXT_NONE );

				     	nsAutoString hrefa;
				     	hrefa.AssignWithConversion(hhref);
				     
				     	nsCString hrefc,linkc;
				     	hrefc.AssignWithConversion(hrefa);

				     	result = baseURI->Resolve(hrefc,linkc);
				     	g_free(hhref);

					info->cinfo.link = ToNewCString(linkc);
				     
					if (!info->cinfo.link)  return (  CONTEXT_NONE );

					break;
				}
			}
		}
		nsCOMPtr<nsIDOMNode> parentNode;
		node->GetParentNode(getter_AddRefs(parentNode));
		
		if (!parentNode)
		{
			node = nsnull;
			flags |= CONTEXT_DOCUMENT;
			break;
		}
		node = parentNode;
	} while (node);
	return (flags);
}

/* Nautilus CREDITS here */
static gchar *
mozilla_get_attribute (nsIDOMNode *node, gchar *attribute)
{
     	nsresult result;
	
	nsCOMPtr<nsIDOMNamedNodeMap> attributes;
	result = node->GetAttributes(getter_AddRefs (attributes));
	if (!NS_SUCCEEDED (result) || !attributes) return ( NULL );

	nsAutoString attr; 

	attr.AssignWithConversion (attribute);
                                        
	nsCOMPtr<nsIDOMNode> attrNode;
	result = attributes->GetNamedItem (attr, getter_AddRefs (attrNode));
	if (!NS_SUCCEEDED (result) || !attrNode)  return ( NULL );

	nsAutoString nodeValue;
			
	result = attrNode->GetNodeValue (nodeValue);
	if (!NS_SUCCEEDED (result))  return ( NULL );
                                                
	char *cstr = ToNewCString(nodeValue);
	char *lifeSucks = g_strdup (cstr);
			
	nsMemory::Free (cstr);
	
	return ( lifeSucks );
}

extern "C" gchar * 
mozilla_get_title (GtkMozEmbed *embed)
{
	gchar *title;
	nsXPIDLString  uTitle;

	*getter_Copies(uTitle) = gtk_moz_embed_get_title_unichar(embed);

	title = g_strdup (NS_ConvertUCS2toUTF8(uTitle).get());
	return title;
}

extern "C" gchar * 
mozilla_get_link_message (GtkMozEmbed *embed)
{
	gchar *message;
	nsXPIDLString  uMessage;

	*getter_Copies(uMessage) = gtk_moz_embed_get_link_message_unichar(embed);

	message = g_strdup (NS_ConvertUCS2toUTF8(uMessage).get());
	return message;
}

extern "C" gint
mozilla_get_button (gpointer event)
{
	PRUint16 button;
	((nsIDOMMouseEvent *)event)->GetButton(&button);
	return button;
}

extern "C" gint 
mozilla_get_mouse_position (gpointer event, glong *x, glong *y)
{
	((nsIDOMMouseEvent *)event)->GetClientX((PRInt32 *)x);
	((nsIDOMMouseEvent *)event)->GetClientY((PRInt32 *)y);
	return 0;
}

extern "C" gint 
mozilla_get_key_code (gpointer event, gulong *key_code)
{
	((nsIDOMKeyEvent *)event)->GetKeyCode((PRUint32 *)key_code);
	return 0;
}

extern "C" gint 
mozilla_get_char_code (gpointer event, gulong *char_code)
{
	((nsIDOMKeyEvent *)event)->GetCharCode((PRUint32 *)char_code);
	return 0;
}

extern "C" gboolean 
mozilla_find (GtkMozEmbed *b, const char *keyword, gboolean *didFind)
{
	g_return_val_if_fail(b != NULL, FALSE);

	NS_ConvertUTF8toUCS2 ucs2text(keyword);
	PRUnichar *search_string = ToNewUnicode(ucs2text);

	nsIWebBrowser *wb = nsnull;
	gtk_moz_embed_get_nsIWebBrowser(b, &wb);
	nsCOMPtr<nsIWebBrowserFind> finder(do_GetInterface(wb));
	finder->SetSearchString(search_string);
	finder->SetFindBackwards(FALSE);
	finder->SetWrapFind(TRUE);
	finder->SetEntireWord(TRUE);
	finder->SetSearchFrames(TRUE);
	finder->SetMatchCase(FALSE);
	finder->FindNext(didFind);
	nsMemory::Free (search_string);

	return ( *didFind );
}

extern "C" glong
mozilla_get_key_event_info(GtkMozEmbed *b, gpointer event, KeyEventInfo *info)
{
	nsresult result;

	nsIDOMKeyEvent *aKeyEvent =  (nsIDOMKeyEvent*)event;
             
     	nsCOMPtr<nsIDOMEventTarget> OriginalTarget;

     	nsCOMPtr<nsIDOMNSEvent> aEvent = do_QueryInterface(aKeyEvent);
     	if (!aEvent) return (CONTEXT_NONE);
	
     	result = aEvent->GetOriginalTarget(getter_AddRefs(OriginalTarget));
	
     	if (NS_FAILED(result) || !OriginalTarget) return (  CONTEXT_NONE );
                          
     	nsCOMPtr<nsIDOMNode> OriginalNode = do_QueryInterface(OriginalTarget);
     	if (!OriginalNode) return ( CONTEXT_NONE );
             
     	nsString nodename;
     	OriginalNode->GetNodeName(nodename);
     
     	if (nodename.EqualsWithConversion ("xul:thumb") ||
	    nodename.EqualsWithConversion ("xul:slider"))
	{
       		return (  CONTEXT_NONE );
	}

     	nsCOMPtr<nsIDOMEventTarget> EventTarget;
     	result = aKeyEvent->GetTarget(getter_AddRefs(EventTarget));
     	if (NS_FAILED(result) || !EventTarget) return (  CONTEXT_NONE );
             
	nsCOMPtr<nsIDOMNode> node = do_QueryInterface(EventTarget);
	if (!node)    return (  CONTEXT_NONE );
     
     	nsCOMPtr<nsIDOMDocument> domDoc;
     	result = node->GetOwnerDocument(getter_AddRefs(domDoc));
     	if (!NS_SUCCEEDED (result) || !domDoc) return (  CONTEXT_NONE );
     
     	nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
     	if(!doc) return (  CONTEXT_NONE );
	
     	nsCOMPtr<nsIDOMNSDocument> nsDoc = do_QueryInterface(domDoc);
     	if (!nsDoc) return (  CONTEXT_NONE );
     
     	nsIURI *baseURI;
#ifdef MOZILLA_SNAPSHOT
	result = doc->GetBaseURL(&baseURI);
#else
	result = doc->GetBaseURL(baseURI);
#endif
     	if (NS_FAILED(result) || !baseURI) return (  CONTEXT_NONE );

     	nsString mime;
     	nsDoc->GetContentType(mime);
     	if (mime.EqualsWithConversion ("text/xul"))  return (  CONTEXT_NONE );

	PRUint32 flags = CONTEXT_NONE;
	nsCOMPtr<nsIDOMHTMLElement> element;
	
     	do {
		PRUint16 type;
		node->GetNodeType(&type);

		element = do_QueryInterface(node);
		if (element)
		{
			nsAutoString tag;
			element->GetTagName(tag);

			if (tag.EqualsWithConversion("input", PR_TRUE))
			{
				flags |= CONTEXT_INPUT;
			}
			else if (tag.EqualsWithConversion("textarea", PR_TRUE))
			{
				flags |= CONTEXT_INPUT;
			}
			else if (tag.EqualsWithConversion("img", PR_TRUE))
			{
				flags |= CONTEXT_IMAGE;

				char *src = mozilla_get_attribute (node, "src");
				if (!src)  return (  CONTEXT_NONE );

			     	nsAutoString srca;
			     	srca.AssignWithConversion(src);
			     
			     	nsCString srcc,imgc;
			     	srcc.AssignWithConversion(srca);
			   
			     	result = baseURI->Resolve(srcc, imgc);
			     	g_free(src);
			   

			     	info->cinfo.img = ToNewCString(imgc);
			   
			     	if (!info->cinfo.img) return (CONTEXT_NONE);
			
		   
			}
			else
			     {
				
				flags |= CONTEXT_OTHER;
			}

			nsCOMPtr<nsIDOMNamedNodeMap> attributes;
			node->GetAttributes(getter_AddRefs(attributes));
			if (attributes)
			{
				nsCOMPtr<nsIDOMNode> hrefNode;
				nsAutoString href; 
			     	href.AssignWithConversion("href");
				attributes->GetNamedItem(href, getter_AddRefs(hrefNode));
				if (hrefNode)
				{
					flags |= CONTEXT_LINK;
				     	nsAutoString linkhtml;
				     	nsCOMPtr<nsIDOMNSHTMLElement> nsElement;
				     	
				     	nsElement = do_QueryInterface (element);				      
				     	
				     	if (nsElement)
				       	{
						result = nsElement->GetInnerHTML(linkhtml);
					     	if (NS_SUCCEEDED(result) && ToNewCString(linkhtml)) 
					       		info->cinfo.linktext = g_strdup(ToNewCString(linkhtml));
					}

					char *href =  mozilla_get_attribute (node, "href");
					if (!href)   return (  CONTEXT_NONE );

				     	nsAutoString hrefa;
				     	hrefa.AssignWithConversion(href);
				     
				     	nsCString hrefc,linkc;
				     	hrefc.AssignWithConversion(hrefa);

				     	result = baseURI->Resolve(hrefc,linkc);
				     	g_free(href);

					info->cinfo.link = ToNewCString(linkc);
				     
					if (info->cinfo.link)  return (  CONTEXT_NONE );

					break;
				}
			}
		}
		nsCOMPtr<nsIDOMNode> parentNode;
		node->GetParentNode(getter_AddRefs(parentNode));
		
		if (!parentNode)
		{
			node = nsnull;
			flags |= CONTEXT_DOCUMENT;
			break;
		}
		node = parentNode;
	} while (node);
	return (flags);
}

/**
 * this function is picked from galeon-1.2.7
 * mozilla_preference_set: set a string mozilla preference
 */
extern "C" gboolean
mozilla_preference_set(const char *preference_name, const char *new_value)
{
	g_return_val_if_fail (preference_name != NULL, FALSE);

	/*It is legitimate to pass in a NULL value sometimes. So let's not
	 *assert and just check and return.
	 */
	if (!new_value) return FALSE;

	nsCOMPtr<nsIPrefService> prefService = 
				do_GetService (NS_PREFSERVICE_CONTRACTID);
	nsCOMPtr<nsIPrefBranch> pref;
	prefService->GetBranch ("", getter_AddRefs(pref));

	if (pref)
	{
		nsresult rv = pref->SetCharPref (preference_name, new_value);            
		return NS_SUCCEEDED (rv) ? TRUE : FALSE;
	}

	return FALSE;
}

/**
 * this function is picked from galeon-1.2.7
 * mozilla_preference_set_boolean: set a boolean mozilla preference
 */
extern "C" gboolean
mozilla_preference_set_boolean (const char *preference_name,
				gboolean new_boolean_value)
{
	g_return_val_if_fail (preference_name != NULL, FALSE);
  
	nsCOMPtr<nsIPrefService> prefService = 
				do_GetService (NS_PREFSERVICE_CONTRACTID);
	nsCOMPtr<nsIPrefBranch> pref;
	prefService->GetBranch ("", getter_AddRefs(pref));
  
	if (pref)
	{
		nsresult rv = pref->SetBoolPref (preference_name,
				new_boolean_value ? PR_TRUE : PR_FALSE);
		return NS_SUCCEEDED (rv) ? TRUE : FALSE;
	}

	return FALSE;
}

/**
 * this function is picked from galeon-1.2.7
 * mozilla_preference_set_int: set an integer mozilla preference
 */
extern "C" gboolean
mozilla_preference_set_int (const char *preference_name, int new_int_value)
{
	g_return_val_if_fail (preference_name != NULL, FALSE);

	nsCOMPtr<nsIPrefService> prefService = 
				do_GetService (NS_PREFSERVICE_CONTRACTID);
	nsCOMPtr<nsIPrefBranch> pref;
	prefService->GetBranch ("", getter_AddRefs(pref));

	if (pref)
	{
		nsresult rv = pref->SetIntPref (preference_name, new_int_value);
		return NS_SUCCEEDED (rv) ? TRUE : FALSE;
	}

	return FALSE;
}
