/* -*- 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 "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

#include "nsIHTMLAttributes.h"
#include "nsIHTMLStyleSheet.h"
#include "nsIStyleRule.h"
#include "nsString.h"
#include "nsISupportsArray.h"
#include "nsCRT.h"
#include "nsIArena.h"
#include "nsIStyleContext.h"
#include "nsHTMLAtoms.h"
#include "nsIHTMLContent.h"
#include "nsVoidArray.h"

static NS_DEFINE_IID(kIHTMLAttributesIID, NS_IHTML_ATTRIBUTES_IID);
static NS_DEFINE_IID(kIStyleRuleIID, NS_ISTYLE_RULE_IID);

//#define DEBUG_REFS

struct HTMLAttribute {
  HTMLAttribute(void)
    : mAttribute(nsnull),
      mValue(),
      mNext(nsnull)
  {
  }

  HTMLAttribute(nsIAtom* aAttribute, const nsString& aValue)
    : mAttribute(aAttribute),
      mValue(aValue),
      mNext(nsnull)
  {
    NS_IF_ADDREF(mAttribute);
  }

  HTMLAttribute(nsIAtom* aAttribute, const nsHTMLValue& aValue)
    : mAttribute(aAttribute),
      mValue(aValue),
      mNext(nsnull)
  {
    NS_IF_ADDREF(mAttribute);
  }

  HTMLAttribute(const HTMLAttribute& aCopy)
    : mAttribute(aCopy.mAttribute),
      mValue(aCopy.mValue),
      mNext(nsnull)
  {
    NS_IF_ADDREF(mAttribute);
  }

  ~HTMLAttribute(void)
  {
    NS_IF_RELEASE(mAttribute);
  }

  HTMLAttribute& operator=(const HTMLAttribute& aCopy)
  {
    NS_IF_RELEASE(mAttribute);
    mAttribute = aCopy.mAttribute;
    NS_IF_ADDREF(mAttribute);
    mValue = aCopy.mValue;
    return *this;
  }

  PRBool operator==(const HTMLAttribute& aOther) const
  {
    return PRBool((mAttribute == aOther.mAttribute) &&
                  (mValue == aOther.mValue));
  }

  PRUint32 HashValue(void) const
  {
    return PRUint32(mAttribute) ^ mValue.HashValue();
  }

  void Reset(void)
  {
    NS_IF_RELEASE(mAttribute);
    mValue.Reset();
  }

  void Set(nsIAtom* aAttribute, const nsHTMLValue& aValue)
  {
    NS_IF_RELEASE(mAttribute);
    mAttribute = aAttribute;
    NS_IF_ADDREF(mAttribute);
    mValue = aValue;
  }

  void Set(nsIAtom* aAttribute, const nsString& aValue)
  {
    NS_IF_RELEASE(mAttribute);
    mAttribute = aAttribute;
    NS_IF_ADDREF(mAttribute);
    mValue.SetStringValue(aValue);
  }

  void AppendToString(nsString& aBuffer) const
  {
    if (nsnull != mAttribute) {
      nsAutoString temp;
      mAttribute->ToString(temp);
      aBuffer.Append(temp);
      if (eHTMLUnit_Null != mValue.GetUnit()) {
        aBuffer.Append(" = ");
        mValue.AppendToString(aBuffer);
      }
    }
    else {
      aBuffer.Append("null");
    }
  }

  void ToString(nsString& aBuffer) const
  {
    if (nsnull != mAttribute) {
      mAttribute->ToString(aBuffer);
      if (eHTMLUnit_Null != mValue.GetUnit()) {
        aBuffer.Append(" = ");
        mValue.AppendToString(aBuffer);
      }
    }
    else {
      aBuffer.Truncate();
      aBuffer.Append("null");
    }
  }

  nsIAtom*        mAttribute;
  nsHTMLValue     mValue;
  HTMLAttribute*  mNext;
};

// ----------------

struct nsClassList {
  nsClassList(nsIAtom* aAtom)
    : mAtom(aAtom),
      mNext(nsnull)
  {
  }
  nsClassList(const nsClassList& aCopy)
    : mAtom(aCopy.mAtom),
      mNext(nsnull)
  {
    NS_ADDREF(mAtom);
    if (nsnull != aCopy.mNext) {
      mNext = new nsClassList(*(aCopy.mNext));
    }
  }
  ~nsClassList(void)
  {
    NS_RELEASE(mAtom);
    if (nsnull != mNext) {
      delete mNext;
    }
  }

  nsIAtom*      mAtom;
  nsClassList*  mNext;
};

class HTMLAttributesImpl: public nsIHTMLAttributes, public nsIStyleRule {
public:
  void* operator new(size_t size);
  void* operator new(size_t size, nsIArena* aArena);
  void operator delete(void* ptr);

  HTMLAttributesImpl(nsIHTMLStyleSheet* aSheet, 
                     nsMapAttributesFunc aFontMapFunc,
                     nsMapAttributesFunc aMapFunc);
  HTMLAttributesImpl(const HTMLAttributesImpl& aCopy);
  virtual ~HTMLAttributesImpl(void);

  NS_IMETHOD QueryInterface(const nsIID& aIID, void** aInstancePtr);
  NS_IMETHOD_(nsrefcnt) AddRef();
  NS_IMETHOD_(nsrefcnt) Release();

  // nsIHTMLAttributes
  NS_IMETHOD SetAttribute(nsIAtom* aAttribute, const nsString& aValue,
                          PRInt32& aAttrCount);
  NS_IMETHOD SetAttribute(nsIAtom* aAttribute, const nsHTMLValue& aValue,
                          PRInt32& aAttrCount);
  NS_IMETHOD UnsetAttribute(nsIAtom* aAttribute, PRInt32& aAttrCount);

  NS_IMETHOD GetAttribute(nsIAtom* aAttribute,
                          nsHTMLValue& aValue) const;

  NS_IMETHOD GetAttributeNameAt(PRInt32 aIndex,
                                nsIAtom*& aName) const;

  NS_IMETHOD GetAttributeCount(PRInt32& aCount) const;
  NS_IMETHOD Equals(const nsIHTMLAttributes* aAttributes, PRBool& aResult) const;
  NS_IMETHOD HashValue(PRUint32& aValue) const;

  NS_IMETHOD GetID(nsIAtom*& aResult) const;
  NS_IMETHOD GetClasses(nsVoidArray& aArray) const;
  NS_IMETHOD HasClass(nsIAtom* aClass) const;

  NS_IMETHOD AddContentRef(void);
  NS_IMETHOD ReleaseContentRef(void);
  NS_IMETHOD GetContentRefCount(PRInt32& aRefCount) const;

  NS_IMETHOD Clone(nsIHTMLAttributes** aInstancePtrResult) const;
  NS_IMETHOD Reset(void);
  NS_IMETHOD SetMappingFunctions(nsMapAttributesFunc aFontMapFunc, nsMapAttributesFunc aMapFunc);

  // nsIStyleRule 
  NS_IMETHOD Equals(const nsIStyleRule* aRule, PRBool& aResult) const;
  NS_IMETHOD GetStyleSheet(nsIStyleSheet*& aSheet) const;
  NS_IMETHOD SetStyleSheet(nsIHTMLStyleSheet* aSheet);
  // Strength is an out-of-band weighting, always 0 here
  NS_IMETHOD GetStrength(PRInt32& aStrength) const;
  NS_IMETHOD MapFontStyleInto(nsIStyleContext* aContext, nsIPresContext* aPresContext);
  NS_IMETHOD MapStyleInto(nsIStyleContext* aContext, nsIPresContext* aPresContext);

  NS_IMETHOD List(FILE* out, PRInt32 aIndent) const;

private:
  HTMLAttributesImpl& operator=(const HTMLAttributesImpl& aCopy);
  PRBool operator==(const HTMLAttributesImpl& aCopy) const;

protected:
  PRUint32 mInHeap : 1;
  PRUint32 mRefCnt : 31;

  nsIHTMLStyleSheet*  mSheet;
  PRInt32             mContentRefCount;
  PRInt32             mCount;
  HTMLAttribute       mFirst;
  nsIAtom*            mID;
  nsClassList*        mClassList;
  nsMapAttributesFunc mFontMapper;
  nsMapAttributesFunc mMapper;

#ifdef DEBUG_REFS
  PRInt32 mInstance;
#endif
};


#ifdef DEBUG_REFS
static PRInt32  gInstanceCount = 0;
static PRInt32  gInstrument = 4;
#endif


void* HTMLAttributesImpl::operator new(size_t size)
{
  HTMLAttributesImpl* rv = (HTMLAttributesImpl*) ::operator new(size);
#ifdef NS_DEBUG
  if (nsnull != rv) {
    nsCRT::memset(rv, 0xEE, size);
  }
#endif
  rv->mInHeap = 1;
  return (void*) rv;
}

void* HTMLAttributesImpl::operator new(size_t size, nsIArena* aArena)
{
  HTMLAttributesImpl* rv = (HTMLAttributesImpl*) aArena->Alloc(PRInt32(size));
#ifdef NS_DEBUG
  if (nsnull != rv) {
    nsCRT::memset(rv, 0xEE, size);
  }
#endif
  rv->mInHeap = 0;
  return (void*) rv;
}

void HTMLAttributesImpl::operator delete(void* ptr)
{
  HTMLAttributesImpl* attr = (HTMLAttributesImpl*) ptr;
  if (nsnull != attr) {
    if (attr->mInHeap) {
      ::operator delete(ptr);
    }
  }
}


HTMLAttributesImpl::HTMLAttributesImpl(nsIHTMLStyleSheet* aSheet, 
                                       nsMapAttributesFunc aFontMapFunc,
                                       nsMapAttributesFunc aMapFunc)
  : mSheet(aSheet),
    mFirst(),
    mCount(0),
    mID(nsnull),
    mClassList(nsnull),
    mContentRefCount(0),
    mFontMapper(aFontMapFunc),
    mMapper(aMapFunc)
{
  NS_INIT_REFCNT();

#ifdef DEBUG_REFS
  mInstance = ++gInstanceCount;
  fprintf(stdout, "%d of %d + HTMLAttributes\n", mInstance, gInstanceCount);
#endif
}

HTMLAttributesImpl::HTMLAttributesImpl(const HTMLAttributesImpl& aCopy)
  : mSheet(aCopy.mSheet),
    mFirst(aCopy.mFirst),
    mCount(aCopy.mCount),
    mID(aCopy.mID),
    mClassList(nsnull),
    mContentRefCount(0),
    mFontMapper(aCopy.mFontMapper),
    mMapper(aCopy.mMapper)
{
  NS_INIT_REFCNT();

  HTMLAttribute* next = aCopy.mFirst.mNext;
  HTMLAttribute* last = &mFirst;
  while (nsnull != next) {
    HTMLAttribute* attr = new HTMLAttribute(*next);
    last->mNext = attr;
    last = attr;
    next = next->mNext;
  }

  NS_IF_ADDREF(mID);
  if (nsnull != aCopy.mClassList) {
    mClassList = new nsClassList(*(aCopy.mClassList));
  }

#ifdef DEBUG_REFS
  mInstance = ++gInstanceCount;
  fprintf(stdout, "%d of %d + HTMLAttributes\n", mInstance, gInstanceCount);
#endif
}

HTMLAttributesImpl::~HTMLAttributesImpl(void)
{
  Reset();

#ifdef DEBUG_REFS
  fprintf(stdout, "%d of %d - HTMLAttribtues\n", mInstance, gInstanceCount);
  --gInstanceCount;
#endif
}

#ifdef DEBUG_REFS
nsrefcnt HTMLAttributesImpl::AddRef(void)                                
{                                    
  if ((gInstrument == -1) || (mInstance == gInstrument)) {
    fprintf(stdout, "%d AddRef HTMLAttributes %d\n", mRefCnt + 1, mInstance);
  }
  return ++mRefCnt;                                          
}

nsrefcnt HTMLAttributesImpl::Release(void)                         
{                                                      
  if ((gInstrument == -1) || (mInstance == gInstrument)) {
    fprintf(stdout, "%d Release HTMLAttributes %d\n", mRefCnt - 1, mInstance);
  }
  if (--mRefCnt == 0) {                                
    delete this;                                       
    return 0;                                          
  }                                                    
  return mRefCnt;                                      
}
#else
NS_IMPL_ADDREF(HTMLAttributesImpl)
NS_IMPL_RELEASE(HTMLAttributesImpl)
#endif


nsresult HTMLAttributesImpl::QueryInterface(const nsIID& aIID,
                                            void** aInstancePtrResult)
{
  NS_PRECONDITION(nsnull != aInstancePtrResult, "null pointer");
  if (nsnull == aInstancePtrResult) {
    return NS_ERROR_NULL_POINTER;
  }
  static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID);
  if (aIID.Equals(kIHTMLAttributesIID)) {
    *aInstancePtrResult = (void*) ((nsIHTMLAttributes*)this);
    NS_ADDREF_THIS();
    return NS_OK;
  }
  if (aIID.Equals(kIStyleRuleIID)) {
    *aInstancePtrResult = (void*) ((nsIStyleRule*)this);
    NS_ADDREF_THIS();
    return NS_OK;
  }
  if (aIID.Equals(kISupportsIID)) {
    *aInstancePtrResult = (void*) ((nsISupports*)(nsIHTMLAttributes*)this);
    NS_ADDREF_THIS();
    return NS_OK;
  }
  return NS_NOINTERFACE;
}


NS_IMETHODIMP
HTMLAttributesImpl::Equals(const nsIHTMLAttributes* aAttributes, PRBool& aResult) const
{
  if (this == aAttributes) {
    aResult = PR_TRUE;
  }
  else {
    const HTMLAttributesImpl* other = (HTMLAttributesImpl*)aAttributes;
    aResult = PR_FALSE;

    if (mCount == other->mCount) {
      if (mID == other->mID) {
        const HTMLAttribute* attr = &mFirst;
        const HTMLAttribute* otherAttr = (&other->mFirst);

        aResult = PR_TRUE;
        while (nsnull != attr) {
          if (! ((*attr) == (*otherAttr))) {
            aResult = PR_FALSE;
            break;
          }
          attr = attr->mNext;
          otherAttr = otherAttr->mNext;
        }
      }
    }
  }
  return NS_OK;
}

NS_IMETHODIMP
HTMLAttributesImpl::Equals(const nsIStyleRule* aRule, PRBool& aResult) const
{
  nsIHTMLAttributes* iHTMLAttr;

  if (this == aRule) {
    aResult = PR_TRUE;
  }
  else {
    aResult = PR_FALSE;
    if ((nsnull != aRule) && 
        (NS_OK == ((nsIStyleRule*)aRule)->QueryInterface(kIHTMLAttributesIID, (void**) &iHTMLAttr))) {

      Equals(iHTMLAttr, aResult);

      NS_RELEASE(iHTMLAttr);
    }
  }
  return NS_OK;
}

NS_IMETHODIMP
HTMLAttributesImpl::HashValue(PRUint32& aValue) const
{
  aValue = 0;

  const HTMLAttribute* attr = &mFirst;
  while (nsnull != attr) {
    if (nsnull != attr->mAttribute) {
      aValue = aValue ^ attr->HashValue();
    }
    attr = attr->mNext;
  }
  return NS_OK;
}

const PRUnichar kNullCh = PRUnichar('\0');

static void ParseClasses(const nsString& aClassString, nsClassList** aClassList)
{
  NS_ASSERTION(nsnull == *aClassList, "non null start list");

  nsAutoString  classStr(aClassString);  // copy to work buffer
  classStr.Append(kNullCh);  // put an extra null at the end

  PRUnichar* start = (PRUnichar*)(const PRUnichar*)classStr.GetUnicode();
  PRUnichar* end   = start;

  while (kNullCh != *start) {
    while ((kNullCh != *start) && nsString::IsSpace(*start)) {  // skip leading space
      start++;
    }
    end = start;

    while ((kNullCh != *end) && (PR_FALSE == nsString::IsSpace(*end))) { // look for space or end
      end++;
    }
    *end = kNullCh; // end string here

    if (start < end) {
      *aClassList = new nsClassList(NS_NewAtom(start));
      aClassList = &((*aClassList)->mNext);
    }

    start = ++end;
  }
}


NS_IMETHODIMP
HTMLAttributesImpl::SetAttribute(nsIAtom* aAttribute, const nsString& aValue,
                                 PRInt32& aCount)
{
  if (nsHTMLAtoms::id == aAttribute) {
    NS_IF_RELEASE(mID);
    mID = NS_NewAtom(aValue);
  }
  else if (nsHTMLAtoms::kClass == aAttribute) {
    if (nsnull != mClassList) {
      delete mClassList;
      mClassList = nsnull;
    }
    ParseClasses(aValue, &mClassList);
  }

  HTMLAttribute*  last = nsnull;
  HTMLAttribute*  attr = &mFirst;

  if (nsnull != mFirst.mAttribute) {
    while (nsnull != attr) {
      if (attr->mAttribute == aAttribute) {
        attr->mValue.SetStringValue(aValue);
        aCount = mCount;
        return NS_OK;
      }
      last = attr;
      attr = attr->mNext;
    }
  }

  if (nsnull == last) {
    mFirst.Set(aAttribute, aValue);
  }
  else {
    attr = new HTMLAttribute(aAttribute, aValue);
    if (nsnull == attr) {
      return NS_ERROR_OUT_OF_MEMORY;
    }
    last->mNext = attr;
  }
  aCount = ++mCount;
  return NS_OK;
}

NS_IMETHODIMP
HTMLAttributesImpl::SetAttribute(nsIAtom* aAttribute,
                                 const nsHTMLValue& aValue,
                                 PRInt32& aCount)
{
  if (eHTMLUnit_Null == aValue.GetUnit()) {
    return UnsetAttribute(aAttribute, aCount);
  }
  
  if (nsHTMLAtoms::id == aAttribute) {
    NS_IF_RELEASE(mID);
    if (eHTMLUnit_String == aValue.GetUnit()) {
      nsAutoString  buffer;
      mID = NS_NewAtom(aValue.GetStringValue(buffer));
    }
  }
  else if (nsHTMLAtoms::kClass == aAttribute) {
    if (nsnull != mClassList) {
      delete mClassList;
      mClassList = nsnull;
    }
    if (eHTMLUnit_String == aValue.GetUnit()) {
      nsAutoString  buffer;
      aValue.GetStringValue(buffer);
      ParseClasses(buffer, &mClassList);
    }
  }

  HTMLAttribute*  last = nsnull;
  HTMLAttribute*  attr = &mFirst;

  if (nsnull != mFirst.mAttribute) {
    while (nsnull != attr) {
      if (attr->mAttribute == aAttribute) {
        attr->mValue = aValue;
        aCount = mCount;
        return NS_OK;
      }
      last = attr;
      attr = attr->mNext;
    }
  }

  if (nsnull == last) {
    mFirst.Set(aAttribute, aValue);
  }
  else {
    attr = new HTMLAttribute(aAttribute, aValue);
    if (nsnull == attr) {
      return NS_ERROR_OUT_OF_MEMORY;
    }
    last->mNext = attr;
  }
  aCount = ++mCount;
  return NS_OK;
}

NS_IMETHODIMP
HTMLAttributesImpl::UnsetAttribute(nsIAtom* aAttribute,
                                   PRInt32& aCount)
{
  if (nsHTMLAtoms::id == aAttribute) {
    NS_IF_RELEASE(mID);
  }
  else if (nsHTMLAtoms::kClass == aAttribute) {
    if (nsnull != mClassList) {
      delete mClassList;
      mClassList = nsnull;
    }
  }

  HTMLAttribute*  prev = nsnull;
  HTMLAttribute*  attr = &mFirst;

  while (nsnull != attr) {
    if (attr->mAttribute == aAttribute) {
      if (nsnull == prev) {
        if (nsnull != mFirst.mNext) {
          attr = mFirst.mNext;
          mFirst = *(mFirst.mNext);
          mFirst.mNext = mFirst.mNext->mNext;
          delete attr;
        }
        else {
          mFirst.Reset();
        }
      }
      else {
        prev->mNext = attr->mNext;
        delete attr;
      }
      mCount--;
      break;
    }
    prev = attr;
    attr = attr->mNext;
  }
  aCount = mCount;
  return NS_OK;
}

NS_IMETHODIMP
HTMLAttributesImpl::GetAttribute(nsIAtom* aAttribute,
                                 nsHTMLValue& aValue) const
{
  aValue.Reset();

  const HTMLAttribute*  attr = &mFirst;

  while (nsnull != attr) {
    if (attr->mAttribute == aAttribute) {
      aValue = attr->mValue;
      return (attr->mValue.GetUnit() == eHTMLUnit_Null)
        ? NS_CONTENT_ATTR_NO_VALUE
        : NS_CONTENT_ATTR_HAS_VALUE;
    }
    attr = attr->mNext;
  }
  return NS_CONTENT_ATTR_NOT_THERE;
}

NS_IMETHODIMP
HTMLAttributesImpl::GetAttributeNameAt(PRInt32 aIndex,
                                       nsIAtom*& aName) const
{
  nsresult result = NS_ERROR_ILLEGAL_VALUE;

  if ((0 <= aIndex) && (aIndex < mCount)) {
    PRInt32 index = 0;
    const HTMLAttribute*  attr = &mFirst;

    while (nsnull != attr) {
      if (nsnull != attr->mAttribute) {
        if (aIndex == index++) {
          aName = attr->mAttribute;
          NS_ADDREF(aName);
          result = NS_OK;
          break;
        }
      }
      attr = attr->mNext;
    }
  }
  return result;
}

NS_IMETHODIMP
HTMLAttributesImpl::GetAttributeCount(PRInt32& aCount) const
{
  aCount = mCount;
  return NS_OK;
}

NS_IMETHODIMP
HTMLAttributesImpl::GetID(nsIAtom*& aResult) const
{
  NS_IF_ADDREF(mID);
  aResult = mID;
  return NS_OK;
}

NS_IMETHODIMP
HTMLAttributesImpl::GetClasses(nsVoidArray& aArray) const
{
  aArray.Clear();
  const nsClassList* classList = mClassList;
  while (nsnull != classList) {
    aArray.AppendElement(classList->mAtom); // NOTE atom is not addrefed
    classList = classList->mNext;
  }
  return NS_OK;
}

NS_IMETHODIMP
HTMLAttributesImpl::HasClass(nsIAtom* aClass) const
{
  const nsClassList* classList = mClassList;
  while (nsnull != classList) {
    if (classList->mAtom == aClass) {
      return NS_OK;
    }
    classList = classList->mNext;
  }
  return NS_COMFALSE;
}

NS_IMETHODIMP
HTMLAttributesImpl::AddContentRef(void)
{
  ++mContentRefCount;
  return NS_OK;
}

NS_IMETHODIMP
HTMLAttributesImpl::ReleaseContentRef(void)
{
  --mContentRefCount;
  return NS_OK;
}

NS_IMETHODIMP
HTMLAttributesImpl::GetContentRefCount(PRInt32& aCount) const
{
  aCount = mContentRefCount;
  return NS_OK;
}

NS_IMETHODIMP
HTMLAttributesImpl::Clone(nsIHTMLAttributes** aInstancePtrResult) const
{
  if (aInstancePtrResult == nsnull) {
    return NS_ERROR_NULL_POINTER;
  }

  HTMLAttributesImpl* clown = new HTMLAttributesImpl(*this);

  if (nsnull == clown) {
    return NS_ERROR_OUT_OF_MEMORY;
  }

  return clown->QueryInterface(kIHTMLAttributesIID, (void **) aInstancePtrResult);
}

NS_IMETHODIMP
HTMLAttributesImpl::Reset(void)
{
  mCount = 0;
  mFirst.Reset();
  HTMLAttribute*  next = mFirst.mNext;
  while (nsnull != next) {
    HTMLAttribute*  attr = next;
    next = next->mNext;
    delete attr;
  }
  mFirst.mNext = nsnull;
  NS_IF_RELEASE(mID);
  if (nsnull != mClassList) {
    delete mClassList;
    mClassList = nsnull;
  }
  mFontMapper = nsnull;
  mMapper = nsnull;
  return NS_OK;
}

NS_IMETHODIMP
HTMLAttributesImpl::SetMappingFunctions(nsMapAttributesFunc aFontMapFunc, nsMapAttributesFunc aMapFunc)
{
  mFontMapper = aFontMapFunc;
  mMapper = aMapFunc;
  return NS_OK;
}

NS_IMETHODIMP
HTMLAttributesImpl::MapFontStyleInto(nsIStyleContext* aContext, nsIPresContext* aPresContext)
{
  if (0 < mCount) {
    if (nsnull != mFontMapper) {
      (*mFontMapper)(this, aContext, aPresContext);
    }
  }
  return NS_OK;
}

NS_IMETHODIMP
HTMLAttributesImpl::MapStyleInto(nsIStyleContext* aContext, nsIPresContext* aPresContext)
{
  if (0 < mCount) {
    NS_ASSERTION(mMapper || mFontMapper, "no mapping function");
    if (nsnull != mMapper) {
      (*mMapper)(this, aContext, aPresContext);
    }
  }
  return NS_OK;
}

NS_IMETHODIMP
HTMLAttributesImpl::GetStyleSheet(nsIStyleSheet*& aSheet) const
{
  NS_IF_ADDREF(mSheet);
  aSheet = mSheet;
  return NS_OK;
}

NS_IMETHODIMP
HTMLAttributesImpl::SetStyleSheet(nsIHTMLStyleSheet* aSheet)
{ // this is not ref-counted, sheet sets to null when it goes away
  mSheet = aSheet;
  return NS_OK;
}

// Strength is an out-of-band weighting, always 0 here
NS_IMETHODIMP
HTMLAttributesImpl::GetStrength(PRInt32& aStrength) const
{
  aStrength = 0;
  return NS_OK;
}

NS_IMETHODIMP
HTMLAttributesImpl::List(FILE* out, PRInt32 aIndent) const
{
  const HTMLAttribute*  attr = &mFirst;
  nsAutoString buffer;

  while (nsnull != attr) {
    for (PRInt32 index = aIndent; --index >= 0; ) fputs("  ", out);
    attr->ToString(buffer);
    fputs(buffer, out);
    fputs("\n", out);
    attr = attr->mNext;
  }
  return NS_OK;
}

extern NS_HTML nsresult
  NS_NewHTMLAttributes(nsIHTMLAttributes** aInstancePtrResult, nsIHTMLStyleSheet* aSheet,
                       nsMapAttributesFunc aFontMapFunc, nsMapAttributesFunc aMapFunc)
{
  if (aInstancePtrResult == nsnull) {
    return NS_ERROR_NULL_POINTER;
  }

  HTMLAttributesImpl  *it = new HTMLAttributesImpl(aSheet, aFontMapFunc, aMapFunc);

  if (nsnull == it) {
    return NS_ERROR_OUT_OF_MEMORY;
  }

  return it->QueryInterface(kIHTMLAttributesIID, (void **) aInstancePtrResult);
}


#if 0


marginwidth; marginheight:  px

border: px

align(DIV): left, right, center, justify
align: left, right, center, abscenter
valign: baseline, top, bottom, middle, center
align(image): abscenter, left, right, texttop, absbottom, baseline, center, bottom, top, middle, absmiddle

dir: rtl, ltr

width:  px, %
height: px, %

vspace; hspace: px

type(list): none, disc, (circle | round), square, decimal, lower-roman, upper-roman, lower-alpha, upper-alpha

href: url
target: string
src: url(string?)
color: color
face: string
suppress: bool

style: string
class: string (list)
id: string
name: string

//a
link; vlink; alink: color

//body
background: url
bgcolor: color
text: color

//frameset
bordercolor: color

//layer
background: url
bgcolor: color

//q
lang: string

//table
background: url
bgcolor: color
bordercolor: color
cellspacing: px
cellpadding: px
toppadding; leftpadding; bottompadding; rightpadding: px
cols: int

//td
background: url
bgcolor: color
nowrap: bool
colspan; rowspan: int

//tr
background: url
bgcolor: color

//colgroup
span: int

//col
repeat: int

//ul;ol
compact: bool
start: int

//li
value: int

//hr;spacer
size: px

//multicol
cols: int
gutter: px

//input
type: string
value: string

//factory methods:
GetStringData
GetBoolData
GetInvBoolData
GetPixelIntData
GetValueOrPctData
GetValueData

//tag methods
getAttribute
getIntAttribute
getKeywordAttribute
getSignedAttribute
getValueOrPctAttribute


//notes
nsIAtom->value table

#endif
