/* -*- 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 "nsIStyleContext.h"
#include "nsStyleConsts.h"
#include "nsString.h"
#include "nsUnitConversion.h"
#include "nsIContent.h"
#include "nsIPresContext.h"
#include "nsIStyleRule.h"
#include "nsISupportsArray.h"
#include "nsCRT.h"

#include "nsIFrame.h"

//#define DEBUG_REFS

#ifdef NS_DEBUG
static PRBool gsDebug = PR_FALSE;
#else
static const PRBool gsDebug = PR_FALSE;
#endif

static NS_DEFINE_IID(kStyleFontSID, NS_STYLEFONT_SID);
static NS_DEFINE_IID(kStyleColorSID, NS_STYLECOLOR_SID);
static NS_DEFINE_IID(kStyleSpacingSID, NS_STYLESPACING_SID);
static NS_DEFINE_IID(kStyleBorderSID, NS_STYLEBORDER_SID);
static NS_DEFINE_IID(kStyleListSID, NS_STYLELIST_SID);
static NS_DEFINE_IID(kStylePositionSID, NS_STYLEPOSITION_SID);
static NS_DEFINE_IID(kStyleTextSID, NS_STYLETEXT_SID);
static NS_DEFINE_IID(kStyleDisplaySID, NS_STYLEDISPLAY_SID);

static NS_DEFINE_IID(kIStyleContextIID, NS_ISTYLECONTEXT_IID);


// --------------------
// nsStyleFont
//
nsStyleFont::nsStyleFont(const nsFont& aFont)
  : mFont(aFont)
{
}

nsStyleFont::~nsStyleFont(void)
{
}

struct StyleFontImpl : public nsStyleFont {
  StyleFontImpl(const nsFont& aFont)
    : nsStyleFont(aFont)
  {}
  ~StyleFontImpl()  
  {}

  virtual const nsID& GetID(void)
  { return kStyleFontSID; }

  virtual void InheritFrom(const nsStyleFont& aCopy);

private:  // These are not allowed
  StyleFontImpl(const StyleFontImpl& aOther);
  StyleFontImpl& operator=(const StyleFontImpl& aOther);
};

void StyleFontImpl::InheritFrom(const nsStyleFont& aCopy)
{
  mFont = aCopy.mFont;
  mThreeD = aCopy.mThreeD;
}

// --------------------
// nsStyleColor
//
nsStyleColor::nsStyleColor() { }
nsStyleColor::~nsStyleColor() { }

struct StyleColorImpl: public nsStyleColor {
  StyleColorImpl(void)
  {
    mBackgroundAttachment = NS_STYLE_BG_ATTACHMENT_SCROLL;
    mBackgroundFlags = NS_STYLE_BG_COLOR_TRANSPARENT;
    mBackgroundRepeat = NS_STYLE_BG_REPEAT_OFF;

    mBackgroundColor = NS_RGB(192,192,192);
    mCursor = NS_STYLE_CURSOR_INHERIT;
  }

  ~StyleColorImpl(void)
  {}

  virtual const nsID& GetID(void)
  { return kStyleColorSID;  }

  virtual void InheritFrom(const nsStyleColor& aCopy);

private:  // These are not allowed
  StyleColorImpl(const StyleColorImpl& aOther);
  StyleColorImpl& operator=(const StyleColorImpl& aOther);
};

void StyleColorImpl::InheritFrom(const nsStyleColor& aCopy)
{
  mColor = aCopy.mColor;
  mCursor = NS_STYLE_CURSOR_INHERIT;/* XXX right? */
  mBackgroundFlags = NS_STYLE_BG_COLOR_TRANSPARENT;
}


// --------------------
// nsStyleSpacing
//
nsStyleSpacing::nsStyleSpacing(void)
  : mMargin(0, 0, 0, 0),
    mPadding(0, 0, 0, 0),
    mBorderPadding(0, 0, 0, 0)
{
}

nsStyleSpacing::~nsStyleSpacing(void)
{
}

struct StyleSpacingImpl: public nsStyleSpacing {
  StyleSpacingImpl(void)
    : nsStyleSpacing()
  {}

  ~StyleSpacingImpl(void)
  {}

  virtual const nsID& GetID(void)
  { return kStyleSpacingSID;  }

  virtual void InheritFrom(const nsStyleSpacing& aCopy);
};

void StyleSpacingImpl::InheritFrom(const nsStyleSpacing& aCopy)
{
  // spacing values not inherited
}

// --------------------
// nsStyleBorder
//

nsStyleBorder::nsStyleBorder(void)
  : mSize(0, 0, 0, 0)
{
  mSizeFlag[0] = mSizeFlag[1] = mSizeFlag[2] = mSizeFlag[3] = NS_STYLE_BORDER_WIDTH_LENGTH_VALUE;
  mStyle[0] = mStyle[1] = mStyle[2] = mStyle[3] = NS_STYLE_BORDER_STYLE_NONE;
  mColor[0] = mColor[1] = mColor[2] = mColor[3] = NS_RGB(0, 0, 0);
}

nsStyleBorder::~nsStyleBorder(void)
{
}

struct StyleBorderImpl: public nsStyleBorder {
  StyleBorderImpl(void)
    : nsStyleBorder()
  {}

  ~StyleBorderImpl(void)
  {}

  virtual const nsID& GetID(void)
  { return kStyleBorderSID;  }

  virtual void InheritFrom(const nsStyleBorder& aCopy);
};

void StyleBorderImpl::InheritFrom(const nsStyleBorder& aCopy)
{
  // border values not inherited
}

// --------------------
// nsStyleList
//
nsStyleList::nsStyleList() { }
nsStyleList::~nsStyleList() { }

struct StyleListImpl: public nsStyleList {
  StyleListImpl(void)
  {
    mListStyleType = NS_STYLE_LIST_STYLE_BASIC;
    mListStylePosition = NS_STYLE_LIST_STYLE_POSITION_OUTSIDE;
  }

  ~StyleListImpl(void)
  {
  }

  virtual const nsID& GetID(void)
  { return kStyleListSID; }

  virtual void InheritFrom(const nsStyleList& aCopy);
};

void StyleListImpl::InheritFrom(const nsStyleList& aCopy)
{
  mListStyleType = aCopy.mListStyleType;
  mListStyleImage = aCopy.mListStyleImage;
  mListStylePosition = aCopy.mListStylePosition;
}

// --------------------
// nsStylePosition
//
nsStylePosition::nsStylePosition() { }
nsStylePosition::~nsStylePosition() { }

struct StylePositionImpl: public nsStylePosition {
  StylePositionImpl(void)
  {
    mPosition = NS_STYLE_POSITION_STATIC;
    mOverflow = NS_STYLE_OVERFLOW_VISIBLE;
    mLeftOffsetFlags = NS_STYLE_POSITION_VALUE_AUTO;
    mLeftOffset = 0;
    mTopOffsetFlags = NS_STYLE_POSITION_VALUE_AUTO;
    mTopOffset = 0;
    mWidthFlags = NS_STYLE_POSITION_VALUE_AUTO;
    mWidth = 0;
    mHeightFlags = NS_STYLE_POSITION_VALUE_AUTO;
    mHeight = 0;
    mZIndex = 0;
  }

  ~StylePositionImpl(void)
  {}

  virtual const nsID& GetID(void)
  { return kStylePositionSID;  }

  virtual void InheritFrom(const nsStylePosition& aCopy);

private:  // These are not allowed
  StylePositionImpl(const StylePositionImpl& aOther);
  StylePositionImpl& operator=(const StylePositionImpl& aOther);
};

void StylePositionImpl::InheritFrom(const nsStylePosition& aCopy)
{
  // positioning values not inherited
}

// --------------------
// nsStyleText
//

nsStyleText::nsStyleText() { }
nsStyleText::~nsStyleText() { }

struct StyleTextImpl: public nsStyleText {
  StyleTextImpl() {
    mTextAlign = NS_STYLE_TEXT_ALIGN_LEFT;
    mTextDecoration = NS_STYLE_TEXT_DECORATION_NONE;
    mTextTransform = NS_STYLE_TEXT_TRANSFORM_NONE;
    mVerticalAlign = NS_STYLE_VERTICAL_ALIGN_BASELINE;
    mWhiteSpace = NS_STYLE_WHITESPACE_NORMAL;
    mLetterSpacing = 0;
    mLineHeight = 0;
    mTextIndent = 0;
    mWordSpacing = 0;
  }
  ~StyleTextImpl() {
  }

  virtual const nsID& GetID() {
    return kStyleTextSID;
  }

  virtual void InheritFrom(const nsStyleText& aCopy);
};

void StyleTextImpl::InheritFrom(const nsStyleText& aCopy)
{
  // Properties not listed here are not inherited: mVerticalAlign,
  // mTextDecoration
  mTextAlign = aCopy.mTextAlign;
  mTextTransform = aCopy.mTextTransform;
  mWhiteSpace = aCopy.mWhiteSpace;

  mLetterSpacing = aCopy.mLetterSpacing;
  mLineHeight = aCopy.mLineHeight;
  mTextIndent = aCopy.mTextIndent;
  mWordSpacing = aCopy.mWordSpacing;
}

// --------------------
// nsStyleDisplay
//

nsStyleDisplay::nsStyleDisplay() { }
nsStyleDisplay::~nsStyleDisplay() { }

struct StyleDisplayImpl: public nsStyleDisplay {
  StyleDisplayImpl() {
    mDirection = NS_STYLE_DIRECTION_LTR;
    mDisplay = NS_STYLE_DISPLAY_INLINE;
    mFloats = NS_STYLE_FLOAT_NONE;
    mBreakType = NS_STYLE_CLEAR_NONE;
    mBreakBefore = PR_FALSE;
    mBreakAfter = PR_FALSE;
  }
  ~StyleDisplayImpl() {
  }

  virtual const nsID& GetID() {
    return kStyleDisplaySID;
  }

  virtual void InheritFrom(const nsStyleDisplay& aCopy);
};

void StyleDisplayImpl::InheritFrom(const nsStyleDisplay& aCopy)
{
  mDirection = aCopy.mDirection;
}

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

class StyleContextImpl : public nsIStyleContext {
public:
  StyleContextImpl(nsIStyleContext* aParent, nsISupportsArray* aRules, nsIPresContext* aPresContext);
  ~StyleContextImpl();

  void* operator new(size_t sz) {
    void* rv = new char[sz];
    nsCRT::zero(rv, sz);
    return rv;
  }

  NS_DECL_ISUPPORTS

  virtual nsIStyleContext*  GetParent(void) const;
  virtual nsISupportsArray* GetStyleRules(void) const;

  virtual PRBool    Equals(const nsIStyleContext* aOther) const;
  virtual PRUint32  HashValue(void) const;

  virtual nsStyleStruct* GetData(const nsIID& aSID);

  virtual void InheritFrom(const StyleContextImpl& aParent);
  virtual void PostProcessData(void);

  nsIStyleContext*  mParent;
  PRUint32          mHashValid: 1;
  PRUint32          mHashValue: 31;
  nsISupportsArray* mRules;

  // the style data...
  StyleFontImpl     mFont;
  StyleColorImpl    mColor;
  StyleSpacingImpl  mSpacing;
  StyleBorderImpl   mBorder;
  StyleListImpl     mList;
  StylePositionImpl mPosition;
  StyleTextImpl     mText;
  StyleDisplayImpl  mDisplay;
};

#ifdef DEBUG_REFS
static PRInt32 gInstanceCount;
#endif

StyleContextImpl::StyleContextImpl(nsIStyleContext* aParent,
                                   nsISupportsArray* aRules, 
                                   nsIPresContext* aPresContext)
  : mParent(aParent), // weak ref
    mRules(aRules),
    mFont(aPresContext->GetDefaultFont()),
    mColor(),
    mSpacing(),
    mBorder(),
    mList(),
    mPosition(),
    mText(),
    mDisplay()
{
  NS_INIT_REFCNT();
  NS_IF_ADDREF(mRules);

  if (nsnull != aParent) {
    InheritFrom((StyleContextImpl&)*aParent);
  }

  if (nsnull != mRules) {
    PRInt32 index = mRules->Count();
    while (0 < index) {
      nsIStyleRule* rule = (nsIStyleRule*)mRules->ElementAt(--index);
      rule->MapStyleInto(this, aPresContext);
      NS_RELEASE(rule);
    }
  }
#ifdef DEBUG_REFS
  ++gInstanceCount;
  fprintf(stdout, "%d + StyleContext\n", gInstanceCount);
#endif
}

StyleContextImpl::~StyleContextImpl()
{
  mParent = nsnull; // weak ref
  NS_IF_RELEASE(mRules);
#ifdef DEBUG_REFS
  --gInstanceCount;
  fprintf(stdout, "%d - StyleContext\n", gInstanceCount);
#endif
}

NS_IMPL_ISUPPORTS(StyleContextImpl, kIStyleContextIID)

nsIStyleContext* StyleContextImpl::GetParent(void) const
{
  NS_IF_ADDREF(mParent);
  return mParent;
}

nsISupportsArray* StyleContextImpl::GetStyleRules(void) const
{
  NS_IF_ADDREF(mRules);
  return mRules;
}


PRBool StyleContextImpl::Equals(const nsIStyleContext* aOther) const
{
  PRBool  result = PR_TRUE;
  const StyleContextImpl* other = (StyleContextImpl*)aOther;

  if (other != this) {
    if (mParent != other->mParent) {
      result = PR_FALSE;
    }
    else {
      if ((nsnull != mRules) && (nsnull != other->mRules)) {
        result = mRules->Equals(other->mRules);
      }
      else {
        result = PRBool((nsnull == mRules) && (nsnull == other->mRules));
      }
    }
  }
  return result;
}

PRUint32 StyleContextImpl::HashValue(void) const
{
  if (0 == mHashValid) {
    ((StyleContextImpl*)this)->mHashValue = ((nsnull != mParent) ? mParent->HashValue() : 0);
    if (nsnull != mRules) {
      PRInt32 index = mRules->Count();
      while (0 <= --index) {
        nsIStyleRule* rule = (nsIStyleRule*)mRules->ElementAt(index);
        PRUint32 hash = rule->HashValue();
        ((StyleContextImpl*)this)->mHashValue ^= (hash & 0x7FFFFFFF);
        NS_RELEASE(rule);
      }
    }
    ((StyleContextImpl*)this)->mHashValid = 1;
  }
  return mHashValue;
}


nsStyleStruct* StyleContextImpl::GetData(const nsIID& aSID)
{
  if (aSID.Equals(kStyleFontSID)) {
    return &mFont;
  }
  if (aSID.Equals(kStyleColorSID)) {
    return &mColor;
  }
  if (aSID.Equals(kStyleSpacingSID)) {
    return &mSpacing;
  }
  if (aSID.Equals(kStyleBorderSID)) {
    return &mBorder;
  }
  if (aSID.Equals(kStyleListSID)) {
    return &mList;
  }
  if (aSID.Equals(kStylePositionSID)) {
    return &mPosition;
  }
  if (aSID.Equals(kStyleTextSID)) {
    return &mText;
  }
  if (aSID.Equals(kStyleDisplaySID)) {
    return &mDisplay;
  }
  return nsnull;
}

void StyleContextImpl::InheritFrom(const StyleContextImpl& aParent)
{
  mFont.InheritFrom(aParent.mFont);
  mColor.InheritFrom(aParent.mColor);
  mSpacing.InheritFrom(aParent.mSpacing);
  mBorder.InheritFrom(aParent.mBorder);
  mList.InheritFrom(aParent.mList);
  mText.InheritFrom(aParent.mText);
  mPosition.InheritFrom(aParent.mPosition);
  mDisplay.InheritFrom(aParent.mDisplay);
}

static void CalcBorderSize(nscoord& aSize, PRUint8 aFlag)
{
  static const nscoord kBorderSize[3] = // XXX need real numbers here (this is probably wrong anyway)
      { NS_POINTS_TO_TWIPS_INT(1), 
        NS_POINTS_TO_TWIPS_INT(3), 
        NS_POINTS_TO_TWIPS_INT(5) };
  if (aFlag < NS_STYLE_BORDER_WIDTH_LENGTH_VALUE) {
    aSize = kBorderSize[aFlag];
  }
}

void StyleContextImpl::PostProcessData(void)
{
  CalcBorderSize(mBorder.mSize.top, mBorder.mSizeFlag[NS_SIDE_TOP]);
  CalcBorderSize(mBorder.mSize.right, mBorder.mSizeFlag[NS_SIDE_RIGHT]);
  CalcBorderSize(mBorder.mSize.bottom, mBorder.mSizeFlag[NS_SIDE_BOTTOM]);
  CalcBorderSize(mBorder.mSize.left, mBorder.mSizeFlag[NS_SIDE_LEFT]);

  mSpacing.mBorder = mBorder.mSize;
  mSpacing.mBorderPadding = mSpacing.mPadding;
  mSpacing.mBorderPadding += mBorder.mSize;
}

NS_LAYOUT nsresult
NS_NewStyleContext(nsIStyleContext** aInstancePtrResult,
                   nsISupportsArray* aRules,
                   nsIPresContext* aPresContext,
                   nsIContent* aContent,
                   nsIFrame* aParentFrame)
{
  NS_PRECONDITION(nsnull != aInstancePtrResult, "null ptr");
  if (nsnull == aInstancePtrResult) {
    return NS_ERROR_NULL_POINTER;
  }

  nsIStyleContext* parent = nsnull;
  if (nsnull != aParentFrame) {
    aParentFrame->GetStyleContext(aPresContext, parent);
    NS_ASSERTION(nsnull != parent, "parent frame must have style context");
  }

  StyleContextImpl* context = new StyleContextImpl(parent, aRules, aPresContext);
  NS_IF_RELEASE(parent);
  if (nsnull == context) {
    return NS_ERROR_OUT_OF_MEMORY;
  }
  context->PostProcessData();

  return context->QueryInterface(kIStyleContextIID, (void **) aInstancePtrResult);
}
