// Copyright 2006 Ben Hutchings <ben@decadent.org.uk>.
// See the file "COPYING" for licence details.

#include <new>
#include <string>

#include <langinfo.h>

#include <nsCOMPtr.h>
#include <nsICharsetConverterManager.h>
#include <nsIComponentManager.h>
#include <nsIFactory.h>
#if MOZ_VERSION_MAJOR > 1 || (MOZ_VERSION_MAJOR == 1 && MOZ_VERSION_MINOR >= 8)
#   include <nsIComponentRegistrar.h>
#   include <nsServiceManagerUtils.h>
#else
#   include <nsIServiceManagerUtils.h>
#endif
#include <nsIUnicodeEncoder.h>

#include "null_prompt_service.hpp"
#include "videolink.hpp"
#include "xpcom_support.hpp"

using xpcom_support::check;

namespace
{
    NS_DEFINE_IID(prompt_service_iid, NS_IPROMPTSERVICE_IID);

    class null_prompt_service_factory : public nsIFactory
    {
	NS_DECL_ISUPPORTS
	NS_DECL_NSIFACTORY
    };

    NS_IMPL_ISUPPORTS1(null_prompt_service_factory, nsIFactory)

    NS_IMETHODIMP null_prompt_service_factory::CreateInstance(
	nsISupports *, const nsIID & iid, void ** result)
    {
	if (!iid.Equals(prompt_service_iid))
	    return NS_ERROR_NO_INTERFACE;

	if (null_prompt_service * service =
	        new (std::nothrow) null_prompt_service)
	{
	    service->AddRef();
	    *result = service;
	    return NS_OK;
	}
	else
	{
	    return NS_ERROR_OUT_OF_MEMORY;
	}
    }

    NS_IMETHODIMP null_prompt_service_factory::LockFactory(PRBool /*lock*/)
    {
	return NS_ERROR_NOT_IMPLEMENTED;
    }

    std::string native_error_string(const PRUnichar * text)
    {
	std::string result;
	PRInt32 text_len = 0;
	while (text[text_len])
	    ++text_len;

	nsCOMPtr<nsICharsetConverterManager> conv_manager;
	nsCOMPtr<nsIUnicodeEncoder> encoder;
	static const nsCID charset_converter_manager_cid =
	    NS_ICHARSETCONVERTERMANAGER_CID;
	if (NS_SUCCEEDED(CallGetService<nsICharsetConverterManager>(
			     charset_converter_manager_cid,
			     getter_AddRefs(conv_manager)))
	    && NS_SUCCEEDED(conv_manager->GetUnicodeEncoder(
				nl_langinfo(CODESET),
				getter_AddRefs(encoder))))
	{
	    encoder->SetOutputErrorBehavior(
		nsIUnicodeEncoder::kOnError_Replace, NULL, PRUnichar('?'));

	    char buf[1000];  // Hopefully long enough for an error message

	    char * out = buf;
	    PRInt32 out_len = sizeof(buf);
	    encoder->Convert(text, &text_len, out, &out_len);
	    out += out_len;

	    out_len = sizeof(buf) - out_len;
	    encoder->Finish(out, &out_len);
	    out += out_len;

	    result.assign(buf, out);
	}
	else
	{
	    // Convert to ASCII
	    result.resize(text_len);
	    for (PRInt32 i = 0; i != text_len; ++i)
		result[i] = (text[i] < 0x80) ? text[i] : '?';
	}

	return result;
    }
}

NS_IMPL_ISUPPORTS1(null_prompt_service, nsIPromptService)

NS_IMETHODIMP null_prompt_service::Alert(
    nsIDOMWindow *, const PRUnichar *, const PRUnichar * text)
{
    fatal_error(native_error_string(text));
    return NS_OK;
}

NS_IMETHODIMP null_prompt_service::AlertCheck(
    nsIDOMWindow *, const PRUnichar *, const PRUnichar * text,
    const PRUnichar *, PRBool *)
{
    fatal_error(native_error_string(text));
    return NS_OK;
}

NS_IMETHODIMP null_prompt_service::Confirm(
    nsIDOMWindow *, const PRUnichar *, const PRUnichar *, PRBool * result)
{
    // Cancel
    *result = false;
    return NS_OK;
}

NS_IMETHODIMP null_prompt_service::ConfirmCheck(
    nsIDOMWindow *, const PRUnichar *, const PRUnichar *,
    const PRUnichar *, PRBool *, PRBool * result)
{
    // Cancel
    *result = false;
    return NS_OK;
}

NS_IMETHODIMP null_prompt_service::ConfirmEx(
    nsIDOMWindow *, const PRUnichar *, const PRUnichar *,
    PRUint32 flags, const PRUnichar *, const PRUnichar *, const PRUnichar *,
    const PRUnichar *, PRBool *, PRInt32 * result)
{
    // Accept the default
    if (flags & BUTTON_POS_1_DEFAULT)
	*result = 1;
    else if (flags & BUTTON_POS_2_DEFAULT)
	*result = 2;
    else
	*result = 0;
    return NS_OK;
}

NS_IMETHODIMP null_prompt_service::Prompt(
    nsIDOMWindow *, const PRUnichar *, const PRUnichar *, PRUnichar **,
    const PRUnichar *, PRBool *, PRBool * result)
{
    // Cancel
    *result = false;
    return NS_OK;
}

NS_IMETHODIMP null_prompt_service::PromptUsernameAndPassword(
    nsIDOMWindow *, const PRUnichar *, const PRUnichar *,
    PRUnichar **, PRUnichar **, const PRUnichar *, PRBool *, PRBool * result)
{
    // Cancel
    *result = false;
    return NS_OK;
}

NS_IMETHODIMP null_prompt_service::PromptPassword(
    nsIDOMWindow *, const PRUnichar *, const PRUnichar *, PRUnichar **,
    const PRUnichar *, PRBool *, PRBool * result)
{
    // Cancel
    *result = false;
    return NS_OK;
}

NS_IMETHODIMP null_prompt_service::Select(
    nsIDOMWindow *, const PRUnichar *, const PRUnichar *, PRUint32,
    const PRUnichar **, PRInt32 *, PRBool * result)
{
    // Cancel
    *result = false;
    return NS_OK;
}

void null_prompt_service::install()
{
    static const nsCID prompt_service_cid = {
	0xa2112d6a, 0x0e28, 0x421f,
	{0xb4, 0x6a, 0x25, 0xc0, 0xb3, 0x8, 0xcb, 0xd0}
    };
    nsCOMPtr<nsIFactory> prompt_factory(new null_prompt_service_factory);
#   if MOZ_VERSION_MAJOR > 1                              \
    || (MOZ_VERSION_MAJOR == 1 && MOZ_VERSION_MINOR >= 8)
        nsCOMPtr<nsIComponentRegistrar> comp_registrar;
        check(NS_GetComponentRegistrar(getter_AddRefs(comp_registrar)));
	check(comp_registrar->RegisterFactory(
		  prompt_service_cid,
		  "Prompt Service",
		  "@mozilla.org/embedcomp/prompt-service;1",
		  prompt_factory));	  
#   else
	check(nsComponentManager::RegisterFactory(
		  prompt_service_cid,
		  "Prompt Service",
		  "@mozilla.org/embedcomp/prompt-service;1",
		  prompt_factory,
		  PR_TRUE)); // replace existing
#   endif
}
