/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "License"); you may not use this file except in
 * compliance with the License.  You may obtain a copy of the License at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the License is distributed on an "AS IS"
 * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.  See
 * the License for the specific language governing rights and limitations
 * under the License.
 *
 * The Original Code is Mozilla Communicator client code.
 *
 * The Initial Developer of the Original Code is Netscape Communications
 * Corporation.  Portions created by Netscape are Copyright (C) 1998
 * Netscape Communications Corporation.  All Rights Reserved.
 */

#include "nsXMLElement.h"
#include "nsIDocument.h"
#include "nsIAtom.h"
#include "nsIEventListenerManager.h"
#include "nsIDOMScriptObjectFactory.h"

#include "nsIEventStateManager.h"
#include "nsDOMEvent.h"
#include "nsINameSpace.h"
#include "nsINameSpaceManager.h"
#include "nsIURL.h"

//static NS_DEFINE_IID(kIDOMElementIID, NS_IDOMELEMENT_IID);
static NS_DEFINE_IID(kIXMLContentIID, NS_IXMLCONTENT_IID);

nsresult
NS_NewXMLElement(nsIXMLContent** aInstancePtrResult, nsIAtom* aTag)
{
  NS_PRECONDITION(nsnull != aInstancePtrResult, "null ptr");
  if (nsnull == aInstancePtrResult) {
    return NS_ERROR_NULL_POINTER;
  }
  nsIXMLContent* it = new nsXMLElement(aTag);
  if (nsnull == it) {
    return NS_ERROR_OUT_OF_MEMORY;
  }
  return it->QueryInterface(kIXMLContentIID, (void**) aInstancePtrResult);
}

static nsIAtom* kLinkAtom;  // XXX these should get moved to nsXMLAtoms
static nsIAtom* kHrefAtom;
static nsIAtom* kShowAtom;

nsXMLElement::nsXMLElement(nsIAtom *aTag)
{
  NS_INIT_REFCNT();
  mInner.Init((nsIContent *)(nsIXMLContent *)this, aTag);
  mIsLink = PR_FALSE;

  if (nsnull == kLinkAtom) {
    kLinkAtom = NS_NewAtom("link");
    kHrefAtom = NS_NewAtom("href");
    kShowAtom = NS_NewAtom("show");
  }
  else {
    NS_ADDREF(kLinkAtom);
    NS_ADDREF(kHrefAtom);
    NS_ADDREF(kShowAtom);
  }
}
 
nsXMLElement::~nsXMLElement()
{
  nsrefcnt  refcnt;
  NS_RELEASE2(kLinkAtom, refcnt);
  NS_RELEASE2(kHrefAtom, refcnt);
  NS_RELEASE2(kShowAtom, refcnt);
}

NS_IMETHODIMP 
nsXMLElement::QueryInterface(REFNSIID aIID,
			     void** aInstancePtr)
{
  NS_IMPL_CONTENT_QUERY_INTERFACE(aIID, aInstancePtr, this, nsIXMLContent)
  if (aIID.Equals(kIXMLContentIID)) {
    nsIXMLContent* tmp = this;
    *aInstancePtr = (void*) tmp;
    NS_ADDREF_THIS();
    return NS_OK;
  }
  
  return NS_NOINTERFACE;
}

NS_IMPL_ADDREF(nsXMLElement)
NS_IMPL_RELEASE(nsXMLElement)


NS_IMETHODIMP 
nsXMLElement::SetAttribute(PRInt32 aNameSpaceID, nsIAtom* aName, 
			                     const nsString& aValue,
			                     PRBool aNotify)
{
  // XXX It sucks that we have to do a strcmp for
  // every attribute set. It might be a bit more expensive
  // to create an atom.
  if ((kNameSpaceID_XML == aNameSpaceID) && 
      (aName == kLinkAtom) && (aValue.Equals("simple"))) {
    mIsLink = PR_TRUE;
  }

  return mInner.SetAttribute(aNameSpaceID, aName, aValue, aNotify);
}

NS_IMETHODIMP 
nsXMLElement::HandleDOMEvent(nsIPresContext& aPresContext,
			     nsEvent* aEvent,
			     nsIDOMEvent** aDOMEvent,
			     PRUint32 aFlags,
			     nsEventStatus& aEventStatus)
{
  // Try script event handlers first
  nsresult ret = mInner.HandleDOMEvent(aPresContext, aEvent, aDOMEvent,
                                       aFlags, aEventStatus);

  if (mIsLink && (NS_OK == ret) && (nsEventStatus_eIgnore == aEventStatus)) {
    switch (aEvent->message) {
    case NS_MOUSE_LEFT_BUTTON_DOWN:
      {
        nsIEventStateManager *stateManager;
        if (NS_OK == aPresContext.GetEventStateManager(&stateManager)) {
          stateManager->SetActiveLink(this);
          NS_RELEASE(stateManager);
        }
        aEventStatus = nsEventStatus_eConsumeNoDefault; 
      }
      break;

    case NS_MOUSE_LEFT_BUTTON_UP:
      {
        nsIEventStateManager *stateManager;
        nsIContent *activeLink;
        if (NS_OK == aPresContext.GetEventStateManager(&stateManager)) {
          stateManager->GetActiveLink(&activeLink);
          NS_RELEASE(stateManager);
        }

        if (activeLink == this) {
          if (nsEventStatus_eConsumeNoDefault != aEventStatus) {
            nsAutoString show, href, target;
            nsIURL* baseURL = nsnull;
	          nsLinkVerb verb = eLinkVerb_Replace;
	          target.Truncate();
            GetAttribute(kNameSpaceID_XML, kHrefAtom, href);
            GetAttribute(kNameSpaceID_XML, kShowAtom, show);
	          // XXX Should probably do this using atoms 
	          if (show.Equals("new")) {
	            verb = eLinkVerb_New;
	          }
	          else if (show.Equals("embed")) {
	            verb = eLinkVerb_Embed;
	          }
            mInner.TriggerLink(aPresContext, verb, baseURL, href, target, PR_TRUE);
              // XXX shouldn't base URL come from document???
            NS_IF_RELEASE(baseURL);
            aEventStatus = nsEventStatus_eConsumeNoDefault; 
          }
        }
      }
      break;

    case NS_MOUSE_RIGHT_BUTTON_DOWN:
      // XXX Bring up a contextual menu provided by the application
      break;

    case NS_MOUSE_ENTER:
      //mouse enter doesn't work yet.  Use move until then.
      {
        nsAutoString href, target;
        nsIURL* baseURL = nsnull;
        GetAttribute(kNameSpaceID_XML, kHrefAtom, href);
        mInner.TriggerLink(aPresContext, eLinkVerb_Replace, baseURL, href, target, PR_FALSE);
              // XXX shouldn't base URL come from document???
        aEventStatus = nsEventStatus_eConsumeDoDefault; 
      }
      break;

      // XXX this doesn't seem to do anything yet
    case NS_MOUSE_EXIT:
      {
        nsAutoString empty;
        mInner.TriggerLink(aPresContext, eLinkVerb_Replace, nsnull, empty, empty, PR_FALSE);
        aEventStatus = nsEventStatus_eConsumeDoDefault; 
      }
      break;

    default:
      break;
    }
  }
  return ret;
}

NS_IMETHODIMP
nsXMLElement::CloneNode(PRBool aDeep, nsIDOMNode** aReturn)
{
  nsXMLElement* it = new nsXMLElement(mInner.mTag);
  if (nsnull == it) {
    return NS_ERROR_OUT_OF_MEMORY;
  }
  mInner.CopyInnerTo((nsIContent *)(nsIXMLContent *)this, &it->mInner, aDeep);
  return it->QueryInterface(kIDOMNodeIID, (void**) aReturn);
}

