/* -*- 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 "nsTableRow.h"
#include "nsTableRowFrame.h"
#include "nsTableRowGroup.h"
#include "nsTableCell.h"
#include "nsTableFrame.h"
#include "nsCellLayoutData.h"
#include "nsHTMLParts.h"
#include "nsHTMLContainer.h"
#include "nsTableRowFrame.h"
#include "nsReflowCommand.h"
#include "nsIStyleContext.h"
#include "nsStyleConsts.h"
#include "nsIContentDelegate.h"
#include "nsHTMLIIDs.h"

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


nsTableRow::nsTableRow(nsIAtom* aTag)
  : nsTableContent(aTag),
  mRowGroup(0),
  mRowIndex(0)
{
}

nsTableRow::nsTableRow(nsIAtom* aTag, PRBool aImplicit)
  : nsTableContent(aTag),
  mRowGroup(0),
  mRowIndex(0)
{
  mImplicit = aImplicit;
}

nsTableRow::~nsTableRow()
{
}

int nsTableRow::GetType() 
{
  return nsITableContent::kTableRowType;
}

nsTableRowGroup * nsTableRow::GetRowGroup ()
{
  NS_IF_ADDREF(mRowGroup);
  return mRowGroup;
}

void nsTableRow::SetRowGroup (nsTableRowGroup * aRowGroup)
{
  if (aRowGroup != mRowGroup)
  {
    mRowGroup = aRowGroup;
  }
}

PRInt32 nsTableRow::GetRowIndex ()
{
  return mRowIndex;
}

void nsTableRow::SetRowIndex (int aRowIndex)
{
  mRowIndex = aRowIndex;
}

// Added for debuging purposes -- remove from final build
nsrefcnt nsTableRow::AddRef(void)
{
  if (gsNoisyRefs==PR_TRUE) printf("Add Ref: %x, nsTableRow cnt = %d \n",this, mRefCnt+1);
  return ++mRefCnt;
}

nsrefcnt nsTableRow::Release(void)
{
  if (gsNoisyRefs==PR_TRUE) printf("Release: %x, nsTableRow cnt = %d \n",this,mRefCnt-1);
  if (--mRefCnt == 0) {
    if (gsNoisyRefs==PR_TRUE) printf("Delete: %x, nsTableRow \n",this);
    delete this;
    return 0;
  }
  return mRefCnt;
}

PRInt32 nsTableRow::GetMaxColumns() 
{
  int sum = 0;
  for (int i = 0, n = ChildCount(); i < n; i++) {
    nsTableCell *cell = (nsTableCell *) ChildAt(i); // cell: REFCNT++
    sum += cell->GetColSpan();
    NS_RELEASE(cell);                               // cell: REFCNT--
  }
  return sum;
}

void nsTableRow::ResetCellMap ()
{
  // SEC Assert.Assertion(nsnull!=mTable)
  if (nsnull != mRowGroup)
  {
    mRowGroup->ResetCellMap ();
  }
}

PRBool nsTableRow::AppendChild (nsIContent *aContent)
{
  NS_PRECONDITION(nsnull != aContent, "null ptr");
  PRBool result = PR_FALSE;
  if (nsnull!=aContent)
  {
    // SEC: should verify that it is indeed table content before we do the cast
    nsTableContent *tableContent = (nsTableContent *)aContent;
    const int contentType = tableContent->GetType();
    if (contentType != nsITableContent::kTableCellType)
    {
      if (gsDebug==PR_TRUE) printf ("nsTableRow::AppendChild -- didn't get a cell, giving up to parent.\n");
      if (nsnull != mRowGroup)
        return mRowGroup->AppendChild (aContent); // let parent have it
      return PR_FALSE;
    }
    else
    {
      PRBool result = nsTableContent::AppendChild (aContent);
      if (result)
      {
        ((nsTableCell *)aContent)->SetRow (this);
        ResetCellMap ();
      }
    }
  }
  return result;
}

PRBool nsTableRow::InsertChildAt (nsIContent *aContent, int aIndex)
{
  NS_PRECONDITION(nsnull != aContent, "null ptr");
  PRBool result = PR_FALSE;
  if (nsnull!=aContent)
  {
    nsTableContent *tableContent = (nsTableContent *)aContent;
    const int contentType = tableContent->GetType();
    if (contentType != nsITableContent::kTableCellType)
    {
      return PR_FALSE;
    }
    else
    {
      PRBool result = nsTableContent::InsertChildAt (aContent, aIndex);
      if (result)
      {
        ((nsTableCell *)aContent)->SetRow (this);
        ResetCellMap ();
      }
    }
  }
  return result;
}


PRBool nsTableRow::ReplaceChildAt (nsIContent *aContent, int aIndex)
{
  PRBool result = PR_FALSE;
  // SEC Assert.PreCondition (nsnull!=aContent);
  // SEC Assert.PreCondition (aIndex in range);
  if ((nsnull==aContent) || /*(aIndex not in range)*/PR_FALSE)
    return PR_FALSE;
  else
  {
    nsTableContent *tableContent = (nsTableContent *)aContent;
    const int contentType = tableContent->GetType();
    if (contentType != nsITableContent::kTableCellType)
    {
      return PR_FALSE;
    }
    else
      return PR_FALSE;
    nsIContent * oldChild = ChildAt (aIndex); // oldChild: REFCNT++
    result = nsTableContent::ReplaceChildAt (aContent, aIndex);
    if (result)
    {
      ((nsTableCell *)aContent)->SetRow (this);
      if (nsnull!=oldChild)
        ((nsTableCell *)oldChild)->SetRow (nsnull);
      ResetCellMap ();
    }
    NS_IF_RELEASE(oldChild);                  // oldChild: REFCNT--
  }
  return result;
}

/**
 * Remove a child at the given position. The method is ignored if
 * the index is invalid (too small or too large).
 */
PRBool nsTableRow::RemoveChildAt (int aIndex)
{
  PRBool result = PR_FALSE;
  //Assert aIndex in range
  nsIContent * oldChild = ChildAt (aIndex);   // oldChild: REFCNT++
  if (nsnull!=oldChild)
  {
    result = nsTableContent::RemoveChildAt (aIndex);
    if (result)
    {
      if (nsnull != oldChild)
        ((nsTableCell *)oldChild)->SetRow (nsnull);
      ResetCellMap ();
    }
  }
  NS_IF_RELEASE(oldChild);                    // oldChild: REFCNT--
  return result;
}

nsIFrame* nsTableRow::CreateFrame(nsIPresContext* aPresContext,
                                  PRInt32 aIndexInParent,
                                  nsIFrame* aParentFrame)
{
  nsIFrame* rv;
  nsresult status = nsTableRowFrame::NewFrame(&rv, this, aIndexInParent,
                                              aParentFrame);
  return rv;
}

/* ---------- Global Functions ---------- */

nsresult
NS_NewTableRowPart(nsIHTMLContent** aInstancePtrResult,
               nsIAtom* aTag)
{
  NS_PRECONDITION(nsnull != aInstancePtrResult, "nsnull ptr");
  if (nsnull == aInstancePtrResult) {
    return NS_ERROR_NULL_POINTER;
  }
  nsIHTMLContent* body = new nsTableRow(aTag);
  if (nsnull == body) {
    return NS_ERROR_OUT_OF_MEMORY;
  }
  return body->QueryInterface(kIHTMLContentIID, (void **) aInstancePtrResult);
}
