/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
 * Version: NPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.1 (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.org code.
 *
 * The Initial Developer of the Original Code is 
 * Netscape Communications Corporation.
 * Portions created by the Initial Developer are Copyright (C) 1998
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or 
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the NPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the NPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

#include "BasicTableLayoutStrategy.h"
#include "nsIPresContext.h"
#include "nsTableFrame.h"
#include "nsTableColFrame.h"
#include "nsTableCellFrame.h"
#include "nsStyleConsts.h"
#include "nsVoidArray.h"

#ifdef DEBUG_TABLE_STRATEGY
static PRInt32 gsDebugCount = 0;
#endif
// The priority of allocations for columns is as follows
//   1) max(MIN, MIN_ADJ)
//   2) max (PCT, PCT_ADJ) 
//   3) FIX 
//   4) FIX_ADJ
//   5) max(DES_CON, DES_ADJ), but use MIN_PRO if present
//   6) for a fixed width table, the column may get more 
//      space if the sum of the col allocations is insufficient 


// the logic here is kept in synch with that in CalculateTotals.
PRBool CanAllocate(PRInt32          aType,
                   PRInt32          aPrevType,
                   nsTableColFrame* aColFrame)
{
  switch(aType) {
  case PCT:
  case FIX:
  case DES_CON:
    return (WIDTH_NOT_SET == aPrevType);
  case FIX_ADJ:
    return (WIDTH_NOT_SET == aPrevType) || (FIX == aPrevType);
  default:
    NS_ASSERTION(PR_FALSE, "invalid call");
  }
  return PR_FALSE;
}

// this doesn't work for a col frame which might get its width from a col
PRBool
HasPctValue(nsIFrame* aFrame) 
{
  const nsStylePosition* position = aFrame->GetStylePosition();
  if (eStyleUnit_Percent == position->mWidth.GetUnit()) {
    float percent = position->mWidth.GetPercentValue();
    if (percent > 0.0f) {
      return PR_TRUE;
    }
  }
  return PR_FALSE;
}

/* ---------- BasicTableLayoutStrategy ---------- */

MOZ_DECL_CTOR_COUNTER(BasicTableLayoutStrategy)


BasicTableLayoutStrategy::BasicTableLayoutStrategy(nsTableFrame *aFrame, PRBool aIsNavQuirks)
{
  MOZ_COUNT_CTOR(BasicTableLayoutStrategy);
  NS_ASSERTION(nsnull != aFrame, "bad frame arg");

  mTableFrame            = aFrame;
  mCellSpacingTotal      = 0;
  mIsNavQuirksMode       = aIsNavQuirks;
}

BasicTableLayoutStrategy::~BasicTableLayoutStrategy()
{
  MOZ_COUNT_DTOR(BasicTableLayoutStrategy);
}

PRBool BasicTableLayoutStrategy::Initialize(const nsHTMLReflowState& aReflowState)
{
#ifdef DEBUG_TABLE_REFLOW_TIMING
  nsTableFrame::DebugTimeMethod(nsTableFrame::eInit, *mTableFrame, (nsHTMLReflowState&)aReflowState, PR_TRUE);
#endif
  ContinuingFrameCheck();

  PRBool result = PR_TRUE;

  // re-init instance variables
  mCellSpacingTotal = 0;
  mCols             = mTableFrame->GetEffectiveCOLSAttribute();

  mTableFrame->SetHasPctCol(PR_FALSE);

  nscoord boxWidth = mTableFrame->CalcBorderBoxWidth(aReflowState);
  PRBool hasPctCol = AssignNonPctColumnWidths(boxWidth, aReflowState);

  mTableFrame->SetHasPctCol(hasPctCol);

  // calc the min, desired, preferred widths from what we know so far
  nscoord minWidth, prefWidth;
  mTableFrame->CalcMinAndPreferredWidths(aReflowState, PR_FALSE, minWidth, prefWidth);
  if (hasPctCol && mTableFrame->IsAutoWidth()) {
    prefWidth = CalcPctAdjTableWidth(aReflowState, boxWidth);
  }
  // calc the desired width, considering if there is a specified width. 
  // don't use nsTableFrame::CalcDesiredWidth because it is based on table column widths.
  nscoord desWidth = (mTableFrame->IsAutoWidth()) ? PR_MIN(prefWidth, aReflowState.availableWidth)
                                                  : prefWidth;
  desWidth = PR_MAX(desWidth, minWidth);

  mTableFrame->SetMinWidth(minWidth);
  mTableFrame->SetDesiredWidth(desWidth);
  mTableFrame->SetPreferredWidth(prefWidth);

  mTableFrame->SetNeedStrategyInit(PR_FALSE);

#ifdef DEBUG_TABLE_REFLOW_TIMING
  nsTableFrame::DebugTimeMethod(nsTableFrame::eInit, *mTableFrame, (nsHTMLReflowState&)aReflowState, PR_FALSE);
#endif
  return result;
}

void BasicTableLayoutStrategy::ContinuingFrameCheck()
{
#ifdef NS_DEBUG
  nsIFrame* tablePIF = nsnull;
  mTableFrame->GetPrevInFlow(&tablePIF);
  NS_ASSERTION(!tablePIF, "never ever call me on a continuing frame!");
#endif
}

PRBool BCW_Wrapup(const nsHTMLReflowState&  aReflowState,
                  BasicTableLayoutStrategy* aStrategy, 
                  nsTableFrame*             aTableFrame, 
                  PRInt32*                  aAllocTypes)
{
  if (aAllocTypes)
    delete [] aAllocTypes;
#ifdef DEBUG_TABLE_STRATEGY
  printf("BalanceColumnWidths ex \n"); aTableFrame->Dump(PR_FALSE, PR_TRUE, PR_FALSE);
#endif
#ifdef DEBUG_TABLE_REFLOW_TIMING
  nsTableFrame::DebugTimeMethod(nsTableFrame::eBalanceCols, *aTableFrame, (nsHTMLReflowState&)aReflowState, PR_FALSE);
#endif
  return PR_TRUE;
}

void
ResetPctValues(nsTableFrame* aTableFrame,
               PRInt32       aNumCols)
{
  // initialize the col percent and cell percent values to 0.
  PRInt32 colX;
  for (colX = 0; colX < aNumCols; colX++) { 
    nsTableColFrame* colFrame = aTableFrame->GetColFrame(colX); 
    if (colFrame) {
      colFrame->SetWidth(PCT, WIDTH_NOT_SET);
      colFrame->SetWidth(PCT_ADJ, WIDTH_NOT_SET);
    }
  }
}

PRBool 
BasicTableLayoutStrategy::BalanceColumnWidths(const nsHTMLReflowState& aReflowState)
{
#ifdef DEBUG_TABLE_STRATEGY
  printf("BalanceColumnWidths en count=%d \n", gsDebugCount++); mTableFrame->Dump(PR_FALSE, PR_TRUE, PR_FALSE);
#endif
#ifdef DEBUG_TABLE_REFLOW_TIMING
  nsTableFrame::DebugTimeMethod(nsTableFrame::eBalanceCols, *mTableFrame, (nsHTMLReflowState&)aReflowState, PR_TRUE);
#endif
  float p2t;
  mTableFrame->GetPresContext()->GetScaledPixelsToTwips(&p2t);

  ContinuingFrameCheck();

  PRInt32 numCols = mTableFrame->GetColCount();
  PRBool tableIsAutoWidth = mTableFrame->IsAutoWidth();

  nscoord horOffset; 
  // get the reduction in available horizontal space due to borders and padding
  nsMargin offset = mTableFrame->GetChildAreaOffset(&aReflowState);
  horOffset = offset.left + offset.right;

  // determine if the table is auto/fixed and get the fixed width if available
  nscoord maxWidth = mTableFrame->CalcBorderBoxWidth(aReflowState);
  if (NS_UNCONSTRAINEDSIZE == maxWidth) {
    maxWidth = PR_MIN(maxWidth, aReflowState.availableWidth);
    if (NS_UNCONSTRAINEDSIZE == maxWidth) {
      NS_ASSERTION(NS_UNCONSTRAINEDSIZE != maxWidth, "cannot balance with an unconstrained width");
      return PR_FALSE;
    }
  }
  // initialize the col percent and cell percent values to 0.
  ResetPctValues(mTableFrame, numCols);

  // An auto table returns a new table width based on percent cells/cols if they exist
  nscoord perAdjTableWidth = 0;
  if (mTableFrame->HasPctCol()) {
    perAdjTableWidth = AssignPctColumnWidths(aReflowState, maxWidth, tableIsAutoWidth, p2t);
    if (perAdjTableWidth > 0) {
      // if an auto table has a pct col or cell, set the preferred table width 
      // here so that CalcPctAdjTableWidth wont't need to be called by the table
      mTableFrame->SetPreferredWidth(perAdjTableWidth);
    }
    perAdjTableWidth = PR_MIN(perAdjTableWidth, maxWidth);
    perAdjTableWidth -= horOffset;
    perAdjTableWidth = PR_MAX(perAdjTableWidth, 0);
  }

  // reduce the maxWidth by border and padding, since we will be dealing with content width
  maxWidth -= horOffset;
  maxWidth = PR_MAX(0, maxWidth);

  // we will re-calc mCellSpacingTotal in case longer rows were added after Initialize was called
  mCellSpacingTotal = 0;
  nscoord spacingX = mTableFrame->GetCellSpacingX();

  PRInt32 numNonZeroWidthCols = 0;
  // set the table's columns to the min width
  nscoord minTableWidth = 0;
  PRInt32 colX;
  for (colX = 0; colX < numCols; colX++) { 
    nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); 
    if (!colFrame) continue;
    nscoord colMinWidth = colFrame->GetMinWidth();
    mTableFrame->SetColumnWidth(colX, colMinWidth);
    minTableWidth += colMinWidth;
    if ((colFrame->GetMinWidth() > 0) || (colFrame->GetDesWidth() > 0) ||
        (colFrame->GetFixWidth() > 0) || (colFrame->GetPctWidth() > 0) || 
        (colFrame->GetWidth(MIN_PRO) > 0)) {
      numNonZeroWidthCols++;
    }
    if (mTableFrame->GetNumCellsOriginatingInCol(colX) > 0) {
      mCellSpacingTotal += spacingX;
    }
  }
  if (mCellSpacingTotal > 0) {
    mCellSpacingTotal += spacingX; // add last cell spacing on right
  }
  minTableWidth += mCellSpacingTotal;

  // if the max width available is less than the min content width for fixed table, we're done
  if (!tableIsAutoWidth && (maxWidth < minTableWidth)) {
    return BCW_Wrapup(aReflowState, this, mTableFrame, nsnull);
  }

  // if the max width available is less than the min content width for auto table
  // that had no % cells/cols, we're done
  if (tableIsAutoWidth && (maxWidth < minTableWidth) && (0 == perAdjTableWidth)) {
    return BCW_Wrapup(aReflowState, this, mTableFrame, nsnull);
  }

  // the following are of size NUM_WIDTHS, but only MIN_CON, DES_CON, FIX, FIX_ADJ, PCT
  // are used and they account for colspan ADJusted values
  PRInt32 totalWidths[NUM_WIDTHS]; // sum of col widths of a particular type 
  PRInt32 totalCounts[NUM_WIDTHS]; // num of cols of a particular type
  PRInt32 dupedWidths[NUM_WIDTHS];
  PRInt32 num0Proportional;

  CalculateTotals(totalCounts, totalWidths, dupedWidths, num0Proportional);
  // auto width table's adjusted width needs cell spacing
  if (tableIsAutoWidth && perAdjTableWidth > 0) { 
    maxWidth = perAdjTableWidth;
  }
  nscoord totalAllocated = totalWidths[MIN_CON] + mCellSpacingTotal;
  
  // allocate and initialize arrays indicating what col gets set
  PRInt32* allocTypes = new PRInt32[numCols];
  if (!allocTypes) return PR_FALSE;
 
  for (colX = 0; colX < numCols; colX++) {
    allocTypes[colX] = -1;
  }

  // allocate PCT/PCT_ADJ cols
  if (totalCounts[PCT] > 0) {
    if (totalAllocated + totalWidths[PCT] - dupedWidths[PCT] <= maxWidth) {
      AllocateFully(totalAllocated, allocTypes, PCT);
      //NS_WARN_IF_FALSE(totalAllocated <= maxWidth, "over allocated");
    }
    else {
      AllocateConstrained(maxWidth - totalAllocated, PCT, PR_FALSE, allocTypes, p2t);
      return BCW_Wrapup(aReflowState, this, mTableFrame, allocTypes);
    }
  }
  // allocate FIX cols
  if ((totalAllocated < maxWidth) && (totalCounts[FIX] > 0)) {
    if (totalAllocated + totalWidths[FIX] - dupedWidths[FIX] <= maxWidth) { 
      AllocateFully(totalAllocated, allocTypes, FIX);
      //NS_WARN_IF_FALSE(totalAllocated <= maxWidth, "over allocated");
    }
    else {
      AllocateConstrained(maxWidth - totalAllocated, FIX, PR_TRUE, allocTypes, p2t);
      return BCW_Wrapup(aReflowState, this, mTableFrame, allocTypes);
    }
  }
  // allocate fixed adjusted cols
  if ((totalAllocated < maxWidth) && (totalCounts[FIX_ADJ] > 0)) {
    if (totalAllocated + totalWidths[FIX_ADJ] - dupedWidths[FIX_ADJ] <= maxWidth) { 
      AllocateFully(totalAllocated, allocTypes, FIX_ADJ);
      //NS_WARN_IF_FALSE(totalAllocated <= maxWidth, "over allocated");
    }
    else {
      AllocateConstrained(maxWidth - totalAllocated, FIX_ADJ, PR_TRUE, allocTypes, p2t);
      return BCW_Wrapup(aReflowState, this, mTableFrame, allocTypes);
    }
  }

  // allocate proportional and auto cols together
  if ((totalAllocated < maxWidth) && (totalCounts[MIN_PRO] + totalCounts[DES_CON] > 0)) {
    if (totalAllocated + totalWidths[MIN_PRO] - dupedWidths[MIN_PRO] +
        totalWidths[DES_CON] - dupedWidths[DES_CON] <= maxWidth) { 
      AllocateFully(totalAllocated, allocTypes, DES_CON);
      //NS_WARN_IF_FALSE(totalAllocated <= maxWidth, "over allocated");
    }
    else {
      AllocateConstrained(maxWidth - totalAllocated, DES_CON, PR_TRUE, allocTypes, p2t);
      return BCW_Wrapup(aReflowState, this, mTableFrame, allocTypes);
    }
  }

  // if this is a nested non auto table and pass1 reflow, we are done
  if ((maxWidth == NS_UNCONSTRAINEDSIZE) && (!tableIsAutoWidth))  {
    return BCW_Wrapup(aReflowState, this, mTableFrame, allocTypes);
  }

  // allocate the rest to auto columns, with some exceptions
  if ( (tableIsAutoWidth && (perAdjTableWidth - totalAllocated > 0)) ||
       (!tableIsAutoWidth && (totalAllocated < maxWidth)) ) {
    // determine how many cols have width either because of a min, fixed, des, or pct value
    PRBool excludePct  = (totalCounts[PCT] != numNonZeroWidthCols);
    PRBool excludeFix  = (totalCounts[PCT] + totalCounts[FIX] + totalCounts[FIX_ADJ] < numNonZeroWidthCols);
    PRBool excludePro  = (totalCounts[DES_CON] > 0);
    PRBool exclude0Pro = (totalCounts[MIN_PRO] != num0Proportional);
    if (tableIsAutoWidth) {
      AllocateUnconstrained(perAdjTableWidth - totalAllocated, allocTypes, excludePct,
                            excludeFix, excludePro, exclude0Pro, p2t);
    }
    else {
      AllocateUnconstrained(maxWidth - totalAllocated, allocTypes, excludePct,
                            excludeFix, excludePro, exclude0Pro, p2t);
    }
  }

  return BCW_Wrapup(aReflowState, this, mTableFrame, allocTypes);
}

nscoord GetColWidth(nsTableColFrame* aColFrame,
                    PRInt32          aWidthType)
{
  switch(aWidthType) {
  case DES_CON:
    return aColFrame->GetDesWidth();
  case FIX:
  case FIX_ADJ:
    return aColFrame->GetWidth(aWidthType);
  case PCT:
    return aColFrame->GetPctWidth();
  default:
    NS_ASSERTION(PR_FALSE, "invalid call");
    return WIDTH_NOT_SET;
  }
}

// Allocate aWidthType values to all cols available in aAllocTypes 
void BasicTableLayoutStrategy::AllocateFully(nscoord&        aTotalAllocated,
                                             PRInt32*        aAllocTypes,
                                             PRInt32         aWidthType)
{
  PRInt32 numCols = mTableFrame->GetColCount(); 
  for (PRInt32 colX = 0; colX < numCols; colX++) { 
    nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); 
    if (!colFrame) continue;
    if (!CanAllocate(aWidthType, aAllocTypes[colX], colFrame)) {
      continue;
    }
    nscoord oldWidth = mTableFrame->GetColumnWidth(colX);
    nscoord newWidth = GetColWidth(colFrame, aWidthType);
    // proportional and desired widths are handled together
    PRBool haveProWidth = PR_FALSE;
    if (DES_CON == aWidthType) {
      nscoord proWidth = colFrame->GetWidth(MIN_PRO);
      if (proWidth >= 0) {
        haveProWidth = PR_TRUE;
        newWidth = proWidth;
      }
    }

    if (WIDTH_NOT_SET == newWidth) continue;
   
    if (newWidth > oldWidth) {
      mTableFrame->SetColumnWidth(colX, newWidth);
      aTotalAllocated += newWidth - oldWidth;
    }
    aAllocTypes[colX] = (haveProWidth) ? MIN_PRO : aWidthType;
  }
}

// This method is the last allocation method to get called, but just in case
// code gets added later after, set the allocated cols to some final value.
#define FINISHED 99 
void BasicTableLayoutStrategy::AllocateUnconstrained(PRInt32  aAllocAmount,
                                                     PRInt32* aAllocTypes,
                                                     PRBool   aExcludePct,
                                                     PRBool   aExcludeFix,
                                                     PRBool   aExcludePro,
                                                     PRBool   aExclude0Pro,
                                                     float    aPixelToTwips)
{
  // set up allocTypes to exclude anything but auto cols if possible
  PRInt32 colX;
  PRInt32 numCols = mTableFrame->GetColCount();
  for (colX = 0; colX < numCols; colX++) {
    if (aExcludePct && (PCT == aAllocTypes[colX])) {
      aAllocTypes[colX] = FINISHED;
    }
    else if (aExcludeFix && ((FIX == aAllocTypes[colX]) || (FIX_ADJ == aAllocTypes[colX]))) {
      aAllocTypes[colX] = FINISHED;
    }
    else if (MIN_PRO == aAllocTypes[colX]) {
      if (aExcludePro) {
        aAllocTypes[colX] = FINISHED;
      }
      else {
        if (aExclude0Pro) {
          nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); 
          if (!colFrame) continue;
          if (colFrame->GetConstraint() == e0ProportionConstraint) {
            aAllocTypes[colX] = FINISHED;
          }
        }
      }
    }
  }
    
  nscoord divisor          = 0;
  PRInt32 numColsAllocated = 0; 
  PRInt32 totalAllocated   = 0;
  for (colX = 0; colX < numCols; colX++) { 
    nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); 
    if (!colFrame) continue; 
    PRBool skipColumn = aExclude0Pro && (e0ProportionConstraint == colFrame->GetConstraint());
    if (FINISHED != aAllocTypes[colX] && !skipColumn ) {
      divisor += mTableFrame->GetColumnWidth(colX);
      numColsAllocated++;
    }
  }
  if (!numColsAllocated) {
    // redistribute the space to all columns and prevent a division by zero
    numColsAllocated = numCols;
  }
  for (colX = 0; colX < numCols; colX++) { 
    if (FINISHED != aAllocTypes[colX]) {
      if (aExclude0Pro) {
        nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); 
        if (!colFrame) continue;
        if (e0ProportionConstraint == colFrame->GetConstraint()) {
          continue;
        }
      }
      nscoord oldWidth = mTableFrame->GetColumnWidth(colX);
      float percent = (divisor == 0) 
        ? (1.0f / ((float)numColsAllocated))
        : ((float)oldWidth) / ((float)divisor);
      nscoord addition = nsTableFrame::RoundToPixel(NSToCoordRound(((float)aAllocAmount) * percent), aPixelToTwips);
      if (addition > (aAllocAmount - totalAllocated)) {
        addition = nsTableFrame::RoundToPixel(aAllocAmount - totalAllocated, aPixelToTwips);
        mTableFrame->SetColumnWidth(colX, oldWidth + addition);
        break;
      }
      mTableFrame->SetColumnWidth(colX, oldWidth + addition);
      totalAllocated += addition;
    }
  }
}

nscoord GetConstrainedWidth(nsTableColFrame* colFrame,
                            PRBool           aConsiderPct)
                           
{
  nscoord conWidth = WIDTH_NOT_SET;
  if (aConsiderPct) {
    conWidth = colFrame->GetPctWidth();
  }
  if (conWidth <= 0 ) {
    conWidth = colFrame->GetFixWidth();
  }
  return conWidth;
}

#define LIMIT_PCT   0 // retrict allocations to only the pct  width cols
#define LIMIT_FIX   1 // retrict allocations to only the fix  width cols
#define LIMIT_DES   2 // retrict allocations to only the auto width cols
#define LIMIT_NONE  3 // retrict allocations to only the auto widths unless there are none
                      // and then restrict fix or pct.

void 
BasicTableLayoutStrategy::ComputeNonPctColspanWidths(const nsHTMLReflowState& aReflowState,
                                                     PRBool                   aConsiderPct,
                                                     float                    aPixelToTwips,
                                                     PRBool*                  aHasPctCol)
{
#ifdef DEBUG_TABLE_REFLOW_TIMING
  nsTableFrame::DebugTimeMethod(nsTableFrame::eNonPctColspans, *mTableFrame, (nsHTMLReflowState&)aReflowState, PR_TRUE);
#endif
  PRInt32 numCols = mTableFrame->GetColCount();
  PRInt32 numEffCols = mTableFrame->GetEffectiveColCount();
  // zero out prior ADJ values 

  PRInt32 colX;
  for (colX = numCols - 1; colX >= 0; colX--) { 
    nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); 
    if (!colFrame) continue;
    colFrame->SetWidth(MIN_ADJ, WIDTH_NOT_SET);
    colFrame->SetWidth(FIX_ADJ, WIDTH_NOT_SET);
    colFrame->SetWidth(DES_ADJ, WIDTH_NOT_SET);
  }

  // For each col, consider the cells originating in it with colspans > 1.
  // Adjust the cols that each cell spans if necessary. Iterate backwards 
  // so that nested and/or overlaping col spans handle the inner ones first, 
  // ensuring more accurated calculations.
  // if more than one  colspan originate in one column, resort the access to 
  // the rows so that the inner colspans are handled first
  PRInt32 numRows = mTableFrame->GetRowCount();
  PRInt32* numColSpans = new PRInt32[numRows];
  if(!numColSpans)
    return;
  PRInt32* rowIndices = new PRInt32[numRows];
  if(!rowIndices) {
    delete [] numColSpans;
    return;
  }
  for (colX = numEffCols - 1; colX >= 0; colX--) { //loop only over effective columns 
    PRInt32 rowX;
    for (rowX = 0; rowX < numRows; rowX++) {
      numColSpans[rowX] = 0;
      rowIndices[rowX] = 0;
    } 
    PRInt32 index = 0;
    for (rowX = 0; rowX < numRows; rowX++) {
      PRBool originates;
      PRInt32 colSpan;
      mTableFrame->GetCellInfoAt(rowX, colX, &originates, &colSpan);
      if (!originates || (1 == colSpan)) {
        continue;
      }
      numColSpans[index] = colSpan;
      rowIndices[index] = rowX;
      index++;
    }
    RowSort(rowIndices, numColSpans, index); // do the row sorting 
    for (PRInt32 i = 0; i < index; i++) {
      PRBool originates;
      PRInt32 colSpan;
      rowX = rowIndices[i];
      nsTableCellFrame* cellFrame = mTableFrame->GetCellInfoAt(rowX, colX, &originates, &colSpan);
      if (!cellFrame || !originates || (1 == colSpan)) {
        continue;
      }
      colSpan = PR_MIN(colSpan, numEffCols - colX);
      // set MIN_ADJ, DES_ADJ, FIX_ADJ
      for (PRInt32 widthX = 0; widthX < NUM_MAJOR_WIDTHS; widthX++) {
        nscoord cellWidth = 0;
        if (MIN_CON == widthX) {
          cellWidth = cellFrame->GetPass1MaxElementWidth();
        }
        else if (DES_CON == widthX) {
          cellWidth = cellFrame->GetMaximumWidth();
        }
        else { // FIX width
          // see if the cell has a style width specified
          const nsStylePosition* cellPosition = cellFrame->GetStylePosition();
          if (eStyleUnit_Coord == cellPosition->mWidth.GetUnit()) {
            // need to add borde and padding into fixed width
            nsMargin borderPadding = nsTableFrame::GetBorderPadding(nsSize(aReflowState.mComputedWidth, 0),
                                                                    aPixelToTwips, cellFrame);
            cellWidth = cellPosition->mWidth.GetCoordValue() + borderPadding.left + borderPadding.right;
            cellWidth = PR_MAX(cellWidth, cellFrame->GetPass1MaxElementWidth());
          }
        }

        if (0 >= cellWidth) continue;

        // first allocate pct cells up to their value if aConsiderPct, then
        // allocate fixed cells up to their value, then allocate auto cells 
        // up to their value, then allocate auto cells proportionally.
        PRInt32 limit = (aConsiderPct) ? LIMIT_PCT : LIMIT_FIX;
        if (MIN_CON != widthX) { // FIX and DES only need to allocate NONE
          limit = LIMIT_NONE;
        }
        while (limit <= LIMIT_NONE) {
          if (ComputeNonPctColspanWidths(widthX, cellFrame, cellWidth, colX, colSpan, 
                                         limit, aPixelToTwips)) {
            break;
          }
          limit++;
        }
      }
      // determine if there is a pct col if we are requested to do so
      if (aHasPctCol && !*aHasPctCol) {
        *aHasPctCol = HasPctValue(cellFrame);
      }
    }
  }
  delete [] numColSpans;
  delete [] rowIndices;
#ifdef DEBUG_TABLE_REFLOW_TIMING
  nsTableFrame::DebugTimeMethod(nsTableFrame::eNonPctColspans, *mTableFrame, (nsHTMLReflowState&)aReflowState, PR_FALSE);
#endif
}

#ifdef DEBUG
static char* widths[] = {"MIN", "DES", "FIX"};
static char* limits[] = {"PCT", "FIX", "DES", "NONE"};
static PRInt32 dumpCount = 0;
void DumpColWidths(nsTableFrame& aTableFrame,
                   char*         aMessage,
                   nsTableCellFrame* aCellFrame,
                   PRInt32       aColIndex,
                   PRInt32       aWidthType,
                   PRInt32       aLimitType)
{
  PRInt32 rowIndex;
  aCellFrame->GetRowIndex(rowIndex);
  printf ("%s (row,col)=(%d,%d) width=%s limit=%s count=%d\n", aMessage, rowIndex, aColIndex, 
          widths[aWidthType], limits[aLimitType], dumpCount++);
  for (PRInt32 i = 0; i < aTableFrame.GetColCount(); i++) {
    printf(" col %d = ", i);
    nsTableColFrame* colFrame = aTableFrame.GetColFrame(i);
    for (PRInt32 j = 0; j < NUM_WIDTHS; j++) {
      printf("%d ", colFrame->GetWidth(j));
    }
    printf("\n");
  }
}
#endif

// (aWidthIndex == MIN_CON) is called with:
//   (aLimitType == LIMIT_PCT)  to give MIN_ADJ to PCT cols (up to their PCT value) 
//   (aLimitType == LIMIT_FIX)  to give MIN_ADJ to FIX cols (up to their FIX value) 
//   (aLimitType == LIMIT_DES)  to give MIN_ADJ to auto cols (up to their preferred value) 
//   (aLimitType == LIMIT_NONE) to give MIN_ADJ to auto cols  
// (aWidthIndex == FIX), (aWidthIndex == DES) is called with:
//   (aLimitType == LIMIT_NONE) to give FIX_ADJ to auto cols  
PRBool 
BasicTableLayoutStrategy::ComputeNonPctColspanWidths(PRInt32           aWidthIndex,
                                                     nsTableCellFrame* aCellFrame,
                                                     nscoord           aCellWidth,
                                                     PRInt32           aColIndex,
                                                     PRInt32           aColSpan,
                                                     PRInt32&          aLimitType,
                                                     float             aPixelToTwips)
{
#ifdef DEBUG_TABLE_STRATEGY
  DumpColWidths(*mTableFrame, "enter ComputeNonPctColspanWidths", aCellFrame, aColIndex, aWidthIndex, aLimitType);
#endif  
  PRBool result = PR_TRUE;

  nscoord spanCellSpacing   = 0; // total cell spacing cells being spanned
  nscoord minTotal          = 0; // the sum of min widths of spanned cells. 
  nscoord autoMinTotal      = 0; // the sum of min widths of spanned auto cells. 
  nscoord fixMinTotal       = 0; // the sum of min widths of spanned fix cells. 
  nscoord pctMinTotal       = 0; // the sum of min widths of spanned pct cells. 
  nscoord pctTotal          = 0; // the sum of pct widths of spanned cells. 
  nscoord fixTotal          = 0; // the sum of fix widths of spanned cells
  nscoord autoDesTotal      = 0; // the sum of desired widths of spanned cells which are auto
  nscoord pctLimitTotal     = 0; // the sum of pct widths not exceeding aCellWidth
  nscoord fixLimitTotal     = 0; // the sum of fix widths not exceeding aCellWidth
  nscoord desLimitTotal     = 0; // the sum of des widths not exceeding aCellWidth
  PRInt32 numAutoCols       = 0;

  nscoord spacingX = mTableFrame->GetCellSpacingX();
  PRInt32 spanX;
  // accumulate the various divisors to be used later
  for (spanX = 0; spanX < aColSpan; spanX++) {
    nsTableColFrame* colFrame = mTableFrame->GetColFrame(aColIndex + spanX); 
    if (!colFrame) continue;
    nscoord minWidth = PR_MAX(colFrame->GetMinWidth(), 0);
    nscoord pctWidth = PR_MAX(colFrame->GetPctWidth(), 0);
    nscoord fixWidth = PR_MAX(colFrame->GetFixWidth(), 0);
    nscoord desWidth = PR_MAX(colFrame->GetDesWidth(), minWidth);

    minTotal += minWidth;

    if (pctWidth > 0) {
      pctTotal += pctWidth;
      pctMinTotal += minWidth;
    }
    else if (fixWidth > 0) {
      fixTotal += fixWidth;
      fixMinTotal += minWidth;
    }
    if (colFrame->GetWidth(PCT) + colFrame->GetWidth(FIX) <= 0) {
      autoDesTotal += desWidth;
      autoMinTotal += minWidth;
      numAutoCols++;
    }
    if ((spanX > 0) && (mTableFrame->GetNumCellsOriginatingInCol(aColIndex + spanX) > 0)) {
      spanCellSpacing += spacingX;
    }
  }
  nscoord availWidth = aCellWidth - spanCellSpacing;
  if (MIN_CON == aWidthIndex) {
    // MIN widths (unlike the others) are added to existing values, so subtract out the min total
    availWidth -= minTotal;

    pctLimitTotal = ((pctTotal - pctMinTotal) < availWidth) ? pctTotal - pctMinTotal : availWidth;
    fixLimitTotal = ((fixTotal - fixMinTotal) < availWidth) ? fixTotal - fixMinTotal : availWidth;
    desLimitTotal = ((autoDesTotal - autoMinTotal) < availWidth) ? autoDesTotal - autoMinTotal : availWidth;
  }
  else { // FIX or DES_CON with LIMIT_NONE
    if (FIX == aWidthIndex) {
      if (autoDesTotal > 0) {
        availWidth -= pctTotal + fixTotal;
      }
      else if (fixTotal > 0) {
        availWidth -= pctTotal;
      }
    }
    else if (DES_CON == aWidthIndex) {
      availWidth -= pctTotal + fixTotal; 
    }
  }

  // Below are some optimizations which can skip steps.
  if (LIMIT_PCT == aLimitType) {
    NS_ASSERTION(MIN_CON == aWidthIndex, "invalid call to ComputeNonPctColspanWidths");
    // if there are no pct cols, limit fix cols
    if (0 == pctTotal) {
      aLimitType = LIMIT_FIX;
    }
  }
  if (LIMIT_FIX == aLimitType) {
    NS_ASSERTION(MIN_CON == aWidthIndex, "invalid call to ComputeNonPctColspanWidths");
    // if there are no fix cols, limit des
    if (0 == fixTotal) {
      aLimitType = LIMIT_DES;
    }
  }
  if (LIMIT_DES == aLimitType) {
    NS_ASSERTION(MIN_CON == aWidthIndex, "invalid call to ComputeNonPctColspanWidths");
    // if there are no auto cols, limit none
    if (0 == autoDesTotal) {
      aLimitType = LIMIT_NONE;
    }
  }

  // there are some cases where allocating up to aCellWidth will not be satisfied
  switch (aLimitType) {
  case LIMIT_PCT:
    if (pctLimitTotal < availWidth) {
      // the pct cols won't satisfy the request alone
      availWidth = pctLimitTotal;
      result = PR_FALSE;
    }
    break;
  case LIMIT_FIX:
    if (fixLimitTotal < availWidth) {
      // the fix cols won't satisfy the request alone
      availWidth = fixLimitTotal;
      result = PR_FALSE;
    }
    break;
  case LIMIT_DES:
    if (desLimitTotal < availWidth) {
      // the auto cols won't satisfy the request alone
      availWidth = desLimitTotal;
      result = PR_FALSE;
    }
    break;
  default: // LIMIT_NONE
    break;
  }

  if (availWidth > 0) {
    nscoord usedWidth = 0;
    // get the correct numerator in a similar fashion to getting the divisor
    for (spanX = 0; spanX < aColSpan; spanX++) {
      if (usedWidth >= availWidth) break;
      nsTableColFrame* colFrame = mTableFrame->GetColFrame(aColIndex + spanX); 
      if (!colFrame) continue;
      nscoord minWidth = colFrame->GetMinWidth();
      nscoord pctWidth = PR_MAX(colFrame->GetPctWidth(), 0);
      nscoord fixWidth = PR_MAX(colFrame->GetFixWidth(), 0);
      nscoord desWidth = PR_MAX(colFrame->GetDesWidth(), minWidth);

      nscoord colWidth = PR_MAX(colFrame->GetWidth(aWidthIndex), 
                                colFrame->GetWidth(aWidthIndex + NUM_MAJOR_WIDTHS));
      colWidth = PR_MAX(colWidth, minWidth);

      nscoord numeratorPct     = 0;
      nscoord numeratorFix     = 0;
      nscoord numeratorAutoDes = 0;

      if (pctWidth > 0) {
        numeratorPct = pctWidth;
      }
      else if (fixWidth > 0) {
        numeratorFix = fixWidth;
      }
      if (colFrame->GetWidth(PCT) + colFrame->GetWidth(FIX) <= 0) {
        numeratorAutoDes = desWidth;
      }

      nscoord divisor = 0;
      nscoord numerator = 0;

      // let pct cols reach their target 
      if (LIMIT_PCT == aLimitType) {
        if (pctLimitTotal > 0) {
          divisor   = pctTotal - pctMinTotal;
          numerator = PR_MAX(numeratorPct - minWidth, 0);
        }
      }
      // let fix cols reach their target 
      else if (LIMIT_FIX == aLimitType) {
        if (fixLimitTotal > 0) {
          divisor   = fixTotal - fixMinTotal;
          numerator = PR_MAX(numeratorFix - minWidth, 0);
        }
      }
      // let auto cols reach their target 
      else if (LIMIT_DES == aLimitType) {
        if (desLimitTotal > 0) {
          if (autoDesTotal > 0) { // there were auto cols
            divisor   = autoDesTotal - autoMinTotal;
            numerator = PR_MAX(numeratorAutoDes - minWidth, 0);
          }
          else if (fixTotal > 0) {  // there were fix cols but no auto
            divisor   = fixTotal - fixMinTotal;
            numerator = PR_MAX(numeratorFix - minWidth, 0);
          }
          else if (pctTotal > 0) {  // there were only pct cols
            divisor   = pctTotal - pctMinTotal;
            numerator = PR_MAX(numeratorPct - minWidth, 0);
          }
          else continue;
        }
      }
      else if (LIMIT_NONE == aLimitType) { 
        if ((MIN_CON == aWidthIndex) && (0 == minTotal)) {
          // divide evenly among the spanned cols
          divisor = aColSpan;
          numerator = 1;
        }
        else {
          if (autoDesTotal > 0) { // there were auto cols
            divisor   = autoDesTotal;
            numerator = numeratorAutoDes;
          }
          else if (fixTotal > 0) {  // there were fix cols but no auto
            divisor   = fixTotal;
            numerator = numeratorFix;
          }
          else if (pctTotal > 0) {  // there were only pct cols
            divisor   = pctTotal;
            numerator = numeratorPct;
          }
          else continue;

        }
      }
      // calculate the adj col width
      nscoord newColAdjWidth = (0 >= divisor) 
        ? NSToCoordRound( ((float)availWidth) / ((float)aColSpan) )
        : NSToCoordRound( (((float)numerator) / ((float)divisor)) * availWidth);
      newColAdjWidth = nsTableFrame::RoundToPixel(newColAdjWidth, aPixelToTwips);
      // don't let the new allocation exceed the avail total
      newColAdjWidth = PR_MIN(newColAdjWidth, availWidth - usedWidth);
      // put the remainder of the avail total in the last spanned col
      if (spanX == aColSpan - 1) {
        newColAdjWidth = availWidth - usedWidth;
      }
      usedWidth += newColAdjWidth;
      // MIN_CON gets added to what is there, the others don't
      if (MIN_CON == aWidthIndex) {
        newColAdjWidth += colWidth;
      }
      if (newColAdjWidth > colWidth) { 
        if (FIX == aWidthIndex) {
          // a colspan cannot fix a column below what a cell desires on its own
          nscoord desCon = colFrame->GetWidth(DES_CON); // do not consider DES_ADJ
          if (desCon > newColAdjWidth) {
            newColAdjWidth = desCon;
          }
        }
        colFrame->SetWidth(aWidthIndex + NUM_MAJOR_WIDTHS, newColAdjWidth);
      }
      else {        
        if((newColAdjWidth > 0) && (FIX == aWidthIndex)) {
          if(colFrame->GetWidth(FIX) < 0) {
            nscoord desCon = colFrame->GetWidth(DES_CON);
            if (desCon > newColAdjWidth) {
              newColAdjWidth = desCon;
            }
          }
          if(colFrame->GetWidth(FIX_ADJ) < newColAdjWidth) {
            colFrame->SetWidth(FIX_ADJ, PR_MAX(colWidth,newColAdjWidth));
          }
        }
      }
    }
  }
#ifdef DEBUG_TABLE_STRATEGY
  DumpColWidths(*mTableFrame, "exit ComputeNonPctColspanWidths", aCellFrame, aColIndex, aWidthIndex, aLimitType);
#endif
  return result;
}


// XXX percent left and right padding are not figured in the calculations
// The table frame needs to be used as the percent base because the reflow
// state may have an unconstrained width. There should probably be a frame
// state bit indicating that horizontal padding is percentage based.

// Determine min, desired, fixed, and proportional sizes for the cols and 
// and calculate min/max table width. Return true if there is at least one pct cell or col
PRBool 
BasicTableLayoutStrategy::AssignNonPctColumnWidths(nscoord                  aMaxWidth,
                                                   const nsHTMLReflowState& aReflowState)
{
#ifdef DEBUG_TABLE_REFLOW_TIMING
  nsTableFrame::DebugTimeMethod(nsTableFrame::eNonPctCols, *mTableFrame, (nsHTMLReflowState&)aReflowState, PR_TRUE);
#endif
#ifdef DEBUG_TABLE_STRATEGY
  printf("AssignNonPctColWidths en max=%d count=%d \n", aMaxWidth, gsDebugCount++); mTableFrame->Dump(PR_FALSE, PR_TRUE, PR_FALSE);
#endif
  PRInt32 numRows = mTableFrame->GetRowCount();
  PRInt32 numCols = mTableFrame->GetColCount();
  nscoord spacingX = mTableFrame->GetCellSpacingX();
  PRInt32 colX, rowX; 
  mCellSpacingTotal = 0;
  PRBool hasPctCol = PR_FALSE; // return value
  float pixelToTwips;
  mTableFrame->GetPresContext()->GetScaledPixelsToTwips(&pixelToTwips);

  PRInt32 rawPropTotal = -1; // total of numbers of the type 1*, 2*, etc 
  PRInt32 numColsForColsAttr = 0; // Nav Quirks cols attribute for equal width cols
  if (NS_STYLE_TABLE_COLS_NONE != mCols) {
    numColsForColsAttr = (NS_STYLE_TABLE_COLS_ALL == mCols) ? numCols : mCols;
  }

  // For every column, determine it's min and desired width based on cell style
  // base on cells which do not span cols. Also, determine mCellSpacingTotal
  for (colX = 0; colX < numCols; colX++) { 
    nscoord minWidth = 0;
    nscoord desWidth = 0;
    nscoord fixWidth = WIDTH_NOT_SET;
    
    // Get column frame and reset it
    nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); 
    if (!colFrame) continue;
    NS_ASSERTION(nsnull != colFrame, "bad col frame");
    colFrame->ResetSizingInfo();

    if (mTableFrame->GetNumCellsOriginatingInCol(colX) > 0) {
      mCellSpacingTotal += spacingX;
    }

    // Scan the cells in the col that have colspan = 1 and find the maximum
    // min, desired, and fixed cells.
    nsTableCellFrame* fixContributor = nsnull;
    nsTableCellFrame* desContributor = nsnull;
    for (rowX = 0; rowX < numRows; rowX++) {
      PRBool originates;
      PRInt32 colSpan;
      nsTableCellFrame* cellFrame = mTableFrame->GetCellInfoAt(rowX, colX, &originates, &colSpan);
      // skip cells that don't originate at (rowX, colX); colspans are handled in the
      // next pass, row spans don't need to be handled
      if (!cellFrame || !originates || (colSpan > 1)) { 
        continue;
      }
      // these values include borders and padding
      minWidth = PR_MAX(minWidth, cellFrame->GetPass1MaxElementWidth());
      nscoord cellDesWidth = cellFrame->GetMaximumWidth();
      if (cellDesWidth > desWidth) {
        desContributor = cellFrame;
        desWidth = cellDesWidth;
      }
      // see if the cell has a style width specified
      const nsStylePosition* cellPosition = cellFrame->GetStylePosition();
      if (eStyleUnit_Coord == cellPosition->mWidth.GetUnit()) {
        nscoord coordValue = cellPosition->mWidth.GetCoordValue();
        if (coordValue > 0) { // ignore if width == 0
          // need to add border and padding into fixed width
          nsSize percentBase(aReflowState.mComputedWidth, 0);
          nsMargin borderPadding = nsTableFrame::GetBorderPadding(percentBase, pixelToTwips, cellFrame);
          nscoord newFixWidth = coordValue + borderPadding.left + borderPadding.right;
          // 2nd part of condition is Nav/IE Quirk like below
          if ((newFixWidth > fixWidth) || ((newFixWidth == fixWidth) && (desContributor == cellFrame))) {
            fixWidth = newFixWidth;
            fixContributor = cellFrame;
          }
        }
      }
      if (!hasPctCol && HasPctValue(cellFrame)) { // see if there is a pct cell
        hasPctCol = PR_TRUE;
      }
    }

    // Nav/IE Quirk like above
    if (fixWidth > 0) {
      if (mIsNavQuirksMode && (desWidth > fixWidth) && (fixContributor != desContributor)) {
        fixWidth = WIDTH_NOT_SET;
        fixContributor = nsnull;
      }
    }
    desWidth = PR_MAX(desWidth, minWidth);

    // cache the computed column info
    colFrame->SetWidth(MIN_CON, minWidth);
    colFrame->SetWidth(DES_CON, desWidth);
    if (fixWidth > 0) {
      colFrame->SetWidth(FIX, fixWidth);
    }

    nsStyleCoord colStyleWidth = colFrame->GetStyleWidth();
    // determine if there is a proportional column either from html4 
    // proportional width on a col or Nav Quirks cols attr
    if (fixWidth <= 0) {
      nscoord proportion = WIDTH_NOT_SET;
      if (eStyleUnit_Proportional == colStyleWidth.GetUnit()) {
        proportion = colStyleWidth.GetIntValue();
      }
      else if (colX < numColsForColsAttr) {
        proportion = 1;
        if ((eStyleUnit_Percent == colStyleWidth.GetUnit()) &&
            (colStyleWidth.GetPercentValue() > 0.0f)) {
          proportion = WIDTH_NOT_SET;
        }
      }
      if (proportion >= 0) {
        rawPropTotal = PR_MAX(rawPropTotal, 0); // it was initialized to -1
        colFrame->SetWidth(MIN_PRO, proportion);
        nsColConstraint colConstraint = (0 == proportion) 
          ? e0ProportionConstraint : eProportionConstraint;
        rawPropTotal += proportion;
        colFrame->SetConstraint(colConstraint);
      }
    }
    if (!hasPctCol) { // see if there is a pct col
      if (eStyleUnit_Percent == colStyleWidth.GetUnit()) {
        float percent = colStyleWidth.GetPercentValue();
        if (percent > 0.0f) {
          hasPctCol = PR_TRUE;
        }
      }
    }
  }
  if (mCellSpacingTotal > 0) {
    mCellSpacingTotal += spacingX; // add last cell spacing on right
  }
//set the table col fixed width if present
  for (colX = 0; colX < numCols; colX++) { 
    nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); 
    if (!colFrame) continue;
    nscoord fixColWidth = colFrame->GetWidth(FIX);
    // use the style width of a col only if the col hasn't gotten a fixed width from any cell
    if (fixColWidth <= 0) {
      nsStyleCoord colStyleWidth = colFrame->GetStyleWidth();
      if (eStyleUnit_Coord == colStyleWidth.GetUnit()) {
        fixColWidth = colStyleWidth.GetCoordValue();
        if (fixColWidth > 0) {
          colFrame->SetWidth(FIX, fixColWidth);
        }
      }
    }
  }
  PRBool* pctRequest = (hasPctCol) ? nsnull : &hasPctCol;
  ComputeNonPctColspanWidths(aReflowState, PR_FALSE, pixelToTwips, pctRequest);
  PRInt32 numEffCols = mTableFrame->GetEffectiveColCount();
  // figure the proportional widths for porportional cols
  if (rawPropTotal > 0)  {
    // get the total desired widths
    nscoord desTotal = 0;
    for (colX = 0; colX < numEffCols; colX++) { 
      nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX);
      if (!colFrame) continue;
      // consider 1*, 2*, etc cols, but ignore 0* cols
      if (colFrame->GetWidth(MIN_PRO) > 0) {
        desTotal += colFrame->GetDesWidth();
      }
    }
    // find the largest combined prop size considering each prop col and
    // its desired size
    nscoord maxPropTotal = 0;
    for (colX = 0; colX < numEffCols; colX++) { 
      nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX);
      nscoord rawProp = colFrame->GetWidth(MIN_PRO);
      if (rawProp > 0) {
        nscoord desWidth = colFrame->GetDesWidth();
        nscoord propTotal = NSToCoordRound( ((float)desWidth) * ((float)rawPropTotal) / (float)rawProp );
        nsTableFrame::RoundToPixel(propTotal, pixelToTwips);
        maxPropTotal = PR_MAX(maxPropTotal, propTotal);
      }
    }
    // set MIN_PRO widths based on the maxPropTotal
    for (colX = 0; colX < numEffCols; colX++) { 
      nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX);
      if (!colFrame) continue;
      nscoord rawProp = colFrame->GetWidth(MIN_PRO);
      if (0 == rawProp) {
        // a 0* col gets only the min width
        colFrame->SetWidth(MIN_PRO, colFrame->GetMinWidth());
      }
      else if ((rawProp > 0) && (rawPropTotal > 0)) {
        nscoord propWidth = NSToCoordRound( ((float)maxPropTotal) * ((float)rawProp) / (float)rawPropTotal ) ;
        propWidth = nsTableFrame::RoundToPixel(propWidth, pixelToTwips);
        colFrame->SetWidth(MIN_PRO, PR_MAX(propWidth, colFrame->GetMinWidth()));
      }
    }
  }

  // Set the table col width for each col to the content min. 
  for (colX = 0; colX < numCols; colX++) { 
    nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); 
    if (!colFrame) continue;
    nscoord minWidth = colFrame->GetMinWidth();
    mTableFrame->SetColumnWidth(colX, minWidth);
  }

#ifdef DEBUG_TABLE_STRATEGY
  printf("AssignNonPctColWidths ex\n"); mTableFrame->Dump(PR_FALSE, PR_TRUE, PR_FALSE);
#endif
#ifdef DEBUG_TABLE_REFLOW_TIMING
  nsTableFrame::DebugTimeMethod(nsTableFrame::eNonPctCols, *mTableFrame, (nsHTMLReflowState&)aReflowState, PR_FALSE);
#endif
  return hasPctCol;
}

void
BasicTableLayoutStrategy::ReduceOverSpecifiedPctCols(nscoord aExcess)
{
  nscoord numCols = mTableFrame->GetColCount();
  for (PRInt32 colX = numCols - 1; (colX >= 0) && (aExcess > 0); colX--) {
    nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); 
    if (!colFrame) continue;
    nscoord pctWidth = colFrame->GetWidth(PCT);
    nscoord reduction = 0;
    if (pctWidth > 0) {
      reduction = (aExcess > pctWidth) ? pctWidth : aExcess;
      nscoord newPctWidth = (reduction == pctWidth) ? WIDTH_NOT_SET : pctWidth - reduction;
      colFrame->SetWidth(PCT, PR_MAX(newPctWidth, colFrame->GetMinWidth()));
    }
    else {
      nscoord pctAdjWidth = colFrame->GetWidth(PCT_ADJ);
      if (pctAdjWidth > 0) {
        reduction = (aExcess > pctAdjWidth) ? pctAdjWidth : aExcess;
        nscoord newPctAdjWidth = (reduction == pctAdjWidth) ? WIDTH_NOT_SET : pctAdjWidth - reduction;
        colFrame->SetWidth(PCT_ADJ, PR_MAX(newPctAdjWidth, colFrame->GetMinWidth()));
      }
    }
    aExcess -= reduction;
  }
}

#ifdef DEBUG_TABLE_REFLOW_TIMING
nscoord WrapupAssignPctColumnWidths(nsTableFrame*            aTableFrame,
                                    const nsHTMLReflowState& aReflowState,
                                    nscoord                  aValue) 
{
  nsTableFrame::DebugTimeMethod(nsTableFrame::ePctCols, *aTableFrame, (nsHTMLReflowState&)aReflowState, PR_FALSE);
  return aValue;
}
#else
inline nscoord WrapupAssignPctColumnWidths(nsTableFrame*            aTableFrame,
                                           const nsHTMLReflowState& aReflowState,
                                           nscoord                  aValue) 
{
  return aValue;
}
#endif

nscoord 
BasicTableLayoutStrategy::CalcPctAdjTableWidth(const nsHTMLReflowState& aReflowState,
                                               nscoord                  aAvailWidthIn)
{
  NS_ASSERTION(mTableFrame->IsAutoWidth() && mTableFrame->HasPctCol(), "invalid call");

  PRInt32 numRows  = mTableFrame->GetRowCount();
  PRInt32 numCols  = mTableFrame->GetColCount(); // consider cols at end without orig cells 
  PRInt32 colX, rowX; 
  float pixelToTwips;
  mTableFrame->GetPresContext()->GetScaledPixelsToTwips(&pixelToTwips);

  // For an auto table, determine the potentially new percent adjusted width based 
  // on percent cells/cols. This probably should only be a NavQuirks thing, since
  // a percentage based cell or column on an auto table should force the column to auto
  nscoord basis = 0;                 
  float* rawPctValues = new float[numCols]; // store the raw pct values, allow for spans past the effective numCols
  if (!rawPctValues) return NS_ERROR_OUT_OF_MEMORY;
  for (colX = 0; colX < numCols; colX++) {
    rawPctValues[colX] = 0.0f;
  }

  nsMargin borderPadding = mTableFrame->GetContentAreaOffset(&aReflowState);
  nscoord availWidth = aAvailWidthIn;
  if (NS_UNCONSTRAINEDSIZE != availWidth) {
    // adjust the avail width to exclude table border, padding and cell spacing
    availWidth -= borderPadding.left + borderPadding.right + mCellSpacingTotal;
  }

  for (colX = 0; colX < numCols; colX++) { 
    nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); 
    if (!colFrame) continue;
    nscoord maxColBasis = -1;
    // Scan the cells in the col 
    for (rowX = 0; rowX < numRows; rowX++) {
      PRBool originates;
      PRInt32 colSpan;
      nsTableCellFrame* cellFrame = mTableFrame->GetCellInfoAt(rowX, colX, &originates, &colSpan);
      if (!originates) continue; // skip  cells that don't originate in the col
      // see if the cell has a style percent width specified
      const nsStylePosition* cellPosition = cellFrame->GetStylePosition();
      if (eStyleUnit_Percent == cellPosition->mWidth.GetUnit()) {
        float percent = cellPosition->mWidth.GetPercentValue();
        if (percent > 0.0f) {
          // calculate the preferred width of the cell based on fix, des, widths of the cols it spans
          nscoord cellDesWidth  = 0;
          float spanPct = percent / float(colSpan);
          for (PRInt32 spanX = 0; spanX < colSpan; spanX++) {
            nsTableColFrame* spanFrame = mTableFrame->GetColFrame(colX + spanX);
            if (!spanFrame) continue;
            cellDesWidth += spanFrame->GetWidth(DES_CON); // don't consider DES_ADJ
            // crudely allocate pct values to the spanning cols so that we can check if they exceed 100 pct below
            rawPctValues[colX + spanX] = PR_MAX(rawPctValues[colX + spanX], spanPct);
          }
          // consider the cell's preferred width 
          cellDesWidth = PR_MAX(cellDesWidth, cellFrame->GetMaximumWidth());
          nscoord colBasis = nsTableFrame::RoundToPixel(NSToCoordRound((float)cellDesWidth / percent), pixelToTwips);
          maxColBasis = PR_MAX(maxColBasis, colBasis);
        }
      }
    }
    if (-1 == maxColBasis) {
      // see if the col has a style percent width specified
      nsStyleCoord colStyleWidth = colFrame->GetStyleWidth();
      if (eStyleUnit_Percent == colStyleWidth.GetUnit()) {
        float percent = colStyleWidth.GetPercentValue();
        maxColBasis = 0;
        if (percent > 0.0f) {
          rawPctValues[colX] = PR_MAX(rawPctValues[colX], percent);
          nscoord desWidth = colFrame->GetWidth(DES_CON); // don't consider DES_ADJ
          maxColBasis = nsTableFrame::RoundToPixel(NSToCoordRound((float)desWidth / percent), pixelToTwips);
        }
      }
    }
    basis = PR_MAX(basis, maxColBasis);
  } // end for (colX ..

  float   perTotal         = 0.0f; // total of percentage constrained cols and/or cells in cols
  PRInt32 numPerCols       = 0;    // number of colums that have percentage constraints
  nscoord fixDesTotal      = 0;    // total of fix or des widths of cols 
  nscoord fixDesTotalNoPct = 0;    // total of fix or des widths of cols without pct

  for (colX = 0; colX < numCols; colX++) {
    nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); 
    if (!colFrame) continue;
    nscoord fixWidth = colFrame->GetFixWidth();
    nscoord fixDesWidth = (fixWidth > 0) ? fixWidth : colFrame->GetDesWidth();
    fixDesTotal += fixDesWidth;
    if (rawPctValues[colX] + perTotal > 1.0f) {
      rawPctValues[colX] = PR_MAX(1.0f - perTotal, 0.0f);
    }
    if (rawPctValues[colX] > 0.0f) {
      numPerCols++;
      perTotal += rawPctValues[colX];
    }
    else {
      fixDesTotalNoPct += fixDesWidth;
    }
  }
  delete [] rawPctValues; // destroy the raw pct values
  // If there are no pct cells or cols, there is nothing to do.
  if ((0 == numPerCols) || (0.0f == perTotal)) {
    NS_ASSERTION(PR_FALSE, "invalid call");
    return basis; 
  }
  // If there is only one col and it is % based, it won't affect anything
  if ((1 == numCols) && (numCols == numPerCols)) {
    return basis + borderPadding.left + borderPadding.right + mCellSpacingTotal;
  }

  // compute a basis considering total percentages and the desired width of everything else
  if ((perTotal > 0.0f) && (perTotal < 1.0f)) {
    nscoord otherBasis = nsTableFrame::RoundToPixel(NSToCoordRound((float)fixDesTotalNoPct / (1.0f - perTotal)), pixelToTwips);
    basis = PR_MAX(basis, otherBasis);
  }
  else if ((fixDesTotalNoPct > 0) && (NS_UNCONSTRAINEDSIZE != availWidth)) { // make the basis as big as possible 
    basis = availWidth; // the 100% cols force as big a width as possible
  }
  basis = PR_MAX(basis, fixDesTotal);
  basis = PR_MIN(basis, availWidth); // don't exceed the max we were given

  if (NS_UNCONSTRAINEDSIZE != availWidth) {
    // add back the table border, padding and cell spacing
    basis += borderPadding.left + borderPadding.right + mCellSpacingTotal;
  }

  return basis;
}

// Determine percentage col widths for each col frame
nscoord 
BasicTableLayoutStrategy::AssignPctColumnWidths(const nsHTMLReflowState& aReflowState,
                                                nscoord                  aAvailWidth,
                                                PRBool                   aTableIsAutoWidth,
                                                float                    aPixelToTwips)
{
#ifdef DEBUG_TABLE_REFLOW_TIMING
  nsTableFrame::DebugTimeMethod(nsTableFrame::ePctCols, *mTableFrame, (nsHTMLReflowState&)aReflowState, PR_TRUE);
#endif
  mTableFrame->SetHasCellSpanningPctCol(PR_FALSE); // this gets refigured below
  PRInt32 numRows = mTableFrame->GetRowCount();
  PRInt32 numCols = mTableFrame->GetColCount(); // consider cols at end without orig cells 
  PRInt32 numEffCols = mTableFrame->GetEffectiveColCount();
  nscoord spacingX = mTableFrame->GetCellSpacingX();
  PRInt32 colX, rowX; 

  NS_ASSERTION(NS_UNCONSTRAINEDSIZE != aAvailWidth, "AssignPctColumnWidths has unconstrained avail width");  
  // For an auto table, determine the potentially new percent adjusted width based 
  // on percent cells/cols. This probably should only be a NavQuirks thing, since
  // a percentage based cell or column on an auto table should force the column to auto
  nscoord basis = (aTableIsAutoWidth) 
                  ? CalcPctAdjTableWidth(aReflowState, aAvailWidth)
                  : aAvailWidth;

  // adjust the basis to exclude table border, padding and cell spacing
  nsMargin borderPadding = mTableFrame->GetContentAreaOffset(&aReflowState);
  basis -= borderPadding.left + borderPadding.right + mCellSpacingTotal;

  nscoord colPctTotal = 0;
  
  // Determine the percentage contribution for cols and for cells with colspan = 1
  // Iterate backwards, similarly to the reasoning in AssignNonPctColumnWidths
  for (colX = numCols - 1; colX >= 0; colX--) { 
    nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); 
    if (!colFrame) continue;
    nscoord maxColPctWidth = WIDTH_NOT_SET;
    float maxColPct = 0.0f;

    nsTableCellFrame* percentContributor = nsnull;
    // Scan the cells in the col that have colspan = 1; assign PER widths
    for (rowX = 0; rowX < numRows; rowX++) {
      PRBool originates;
      PRInt32 colSpan;
      nsTableCellFrame* cellFrame = mTableFrame->GetCellInfoAt(rowX, colX, &originates, &colSpan);
      // skip cells that don't originate at (rowX, colX); colspans are handled in the
      // next pass, row spans don't need to be handled
      if (!cellFrame || !originates || (colSpan > 1)) { 
        continue;
      }
      // see if the cell has a style percent width specified
      const nsStylePosition* cellPosition = cellFrame->GetStylePosition();
      if (eStyleUnit_Percent == cellPosition->mWidth.GetUnit()) {
        float percent = cellPosition->mWidth.GetPercentValue();
        if (percent > maxColPct) {
          maxColPct = percent;
          maxColPctWidth = nsTableFrame::RoundToPixel(NSToCoordRound( ((float)basis) * maxColPct ), aPixelToTwips);
          percentContributor = cellFrame;
          if (!mIsNavQuirksMode) { 
            // need to add border and padding
            nsMargin borderPadding = nsTableFrame::GetBorderPadding(nsSize(basis, 0), aPixelToTwips, cellFrame);
            maxColPctWidth += borderPadding.left + borderPadding.right;
          }
        }
      }
    }
    if (WIDTH_NOT_SET == maxColPctWidth) {
      // see if the col has a style percent width specified
      nsStyleCoord colStyleWidth = colFrame->GetStyleWidth();
      if (eStyleUnit_Percent == colStyleWidth.GetUnit()) {
        maxColPct = colStyleWidth.GetPercentValue();
        maxColPctWidth = nsTableFrame::RoundToPixel(NSToCoordRound( ((float)basis) * maxColPct ), aPixelToTwips);
      }
    }
    // conflicting pct/fixed widths are recorded. Nav 4.x may be changing the
    // fixed width value if it exceeds the pct value and not recording the pct
    // value. This is not being done and IE5 doesn't do it either.
    if (maxColPctWidth > 0) {
      maxColPctWidth = PR_MAX(maxColPctWidth, colFrame->GetWidth(MIN_CON));
      colFrame->SetWidth(PCT, maxColPctWidth);
      colPctTotal += NSToCoordRound(100.0f * (float)maxColPct);
    }
  }

  // if the percent total went over 100%, adjustments need to be made to right most cols
  if (colPctTotal > 100) {
    ReduceOverSpecifiedPctCols(nsTableFrame::RoundToPixel(NSToCoordRound(((float)(colPctTotal - 100)) * 0.01f * (float)basis), aPixelToTwips));
    colPctTotal = 100;
  }

  // check to see if a cell spans a percentage col. This will cause the MIN_ADJ,
  // FIX_ADJ, and DES_ADJ values to be recomputed 
  PRBool done = PR_FALSE;
  for (colX = 0; (colX < numCols) && !done; colX++) { 
    for (rowX = 0; (rowX < numRows) && !done; rowX++) {
      PRBool originates;
      PRInt32 colSpan;
      mTableFrame->GetCellInfoAt(rowX, colX, &originates, &colSpan);
      if (!originates || 1 == colSpan) {
        continue;
      }
      // determine if the cell spans cols which have a pct value
      for (PRInt32 spanX = 0; (spanX < colSpan) && !done; spanX++) {
        nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX + spanX); 
        if (!colFrame) continue;
        if (colFrame->GetWidth(PCT) > 0) {
          mTableFrame->SetHasCellSpanningPctCol(PR_TRUE);
          // recompute the MIN_ADJ, FIX_ADJ, and DES_ADJ values
          ComputeNonPctColspanWidths(aReflowState, PR_TRUE, aPixelToTwips, nsnull);
          done = PR_TRUE;
          break;
        }
      }
    }
  }

  // For each col, consider the cells originating in it with colspans > 1.
  // Adjust the cols that each cell spans if necessary.
  // if more than one  colspan originate in one column, resort the access to 
  // the rows so that the inner colspans are handled first
  PRInt32* numColSpans = new PRInt32[numRows];
  if(!numColSpans)
    return basis;
  PRInt32* rowIndices = new PRInt32[numRows];
  if(!rowIndices) {
    delete numColSpans;
    return basis;
  }
  for (colX = numEffCols - 1; colX >= 0; colX--) { // loop only over effective columns
    PRInt32 rowX;
    for (rowX = 0; rowX < numRows; rowX++) {
      numColSpans[rowX] = 0;
      rowIndices[rowX] = 0;
    } 
    PRInt32 index = 0;  
    for (rowX = 0; rowX < numRows; rowX++) {
      PRBool originates;
      PRInt32 colSpan;
      mTableFrame->GetCellInfoAt(rowX, colX, &originates, &colSpan);
      if (!originates || (1 == colSpan)) {
        continue;
      }
      numColSpans[index] = colSpan;
      rowIndices[index] = rowX;
      index++;
    }
    RowSort(rowIndices, numColSpans, index); // do the sorting
    for (PRInt32 i = 0; i < index; i++) {
      PRBool originates;
      PRInt32 colSpan;
      rowX = rowIndices[i];
      nsTableCellFrame* cellFrame = mTableFrame->GetCellInfoAt(rowX, colX, &originates, &colSpan);
      if (!cellFrame || !originates || (1 == colSpan)) {
        continue;
      }
      colSpan = PR_MIN(colSpan,numEffCols-colX);
      nscoord cellPctWidth = WIDTH_NOT_SET;
      // see if the cell has a style percentage width specified
      const nsStylePosition* cellPosition = cellFrame->GetStylePosition();
      float cellPct = 0.0f;
      if (eStyleUnit_Percent == cellPosition->mWidth.GetUnit()) {
        cellPct = cellPosition->mWidth.GetPercentValue();
        if (colSpan == numEffCols)
          cellPct = 1.0f; // overwrite spurious percent colspan width's - bug 46944
        cellPctWidth = nsTableFrame::RoundToPixel(NSToCoordRound( ((float)basis) * cellPct ), aPixelToTwips);
        if (!mIsNavQuirksMode) { 
          // need to add border and padding 
          nsMargin borderPadding = nsTableFrame::GetBorderPadding(nsSize(basis, 0), aPixelToTwips, cellFrame);
          cellPctWidth += borderPadding.left + borderPadding.right;
        }
      }
      if (cellPctWidth > 0) {
        nscoord spanCellSpacing = 0;
        nscoord spanTotal = 0;
        nscoord spanTotalNoPctAdj = 0;
        nscoord colPctWidthTotal = 0; // accumulate the PCT width 
        nscoord colPctAdjTotal = 0;   // accumulate the PCT_ADJ width or  the max of PCT_ADJ and PCT width if a 
                                      // PCT width is specified
        PRBool canSkipPctAdj = PR_FALSE;
        // accumulate the spanTotal as the max of MIN, DES, FIX, PCT
        PRInt32 spanX;
        for (spanX = 0; spanX < colSpan; spanX++) {
          nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX + spanX); 
          if (!colFrame) continue;
          nscoord colPctWidth = colFrame->GetWidth(PCT);
          if (colPctWidth > 0) { // skip pct cols
            colPctWidthTotal += colPctWidth;
            colPctAdjTotal += PR_MAX(colFrame->GetWidth(PCT_ADJ), colPctWidth);
            continue;
          }
          colPctAdjTotal += PR_MAX(colFrame->GetWidth(PCT_ADJ), 0);
          nscoord colWidth = PR_MAX(colFrame->GetMinWidth(), colFrame->GetFixWidth());
          colWidth = PR_MAX(colWidth, colFrame->GetDesWidth()); // XXX check this
          //colWidth = PR_MAX(colWidth, colFrame->GetPctWidth());
          spanTotal += colWidth;
          if (colFrame->GetWidth(PCT_ADJ) <= 0) { // if we have a cell that is neither PCT or PCT_ADJ we have
                                                  // other places where we can drop the width
            canSkipPctAdj = PR_TRUE; 
            spanTotalNoPctAdj += colWidth;
          }
          if ((spanX > 0) && (mTableFrame->GetNumCellsOriginatingInCol(colX + spanX) > 0)) {
            spanCellSpacing += spacingX;
          }
        }
        cellPctWidth += spanCellSpacing; // add it back in since it was subtracted from aBasisIn
        if (cellPctWidth <= 0) {
          continue;
        }
        spanTotal = canSkipPctAdj ? spanTotalNoPctAdj: spanTotal; // if we can skip the PCT_ADJ ignore theire width
      
        colPctWidthTotal = canSkipPctAdj ? colPctAdjTotal: colPctWidthTotal; 
        
        if ((PR_MAX(colPctWidthTotal, colPctAdjTotal)+ spanCellSpacing) < cellPctWidth) { 
          // we have something to distribute ...
          // record the percent contributions for the spanned cols
          PRInt32 usedColumns = colSpan;
          for (spanX = colSpan-1; spanX >= 0; spanX--) {
            nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX + spanX); 
            if (!colFrame) continue;
            if ((colFrame->GetWidth(PCT) > 0) || (canSkipPctAdj && (colFrame->GetWidth(PCT_ADJ) > 0))) {
              // dont use pct cols or if we can skip the pct adj event do not take the PCT_ADJ cols
              usedColumns--;
              continue;
            } // count the pieces 
            if (usedColumns == 0)
              usedColumns = 1; // avoid division by 0 later
            nscoord minWidth = colFrame->GetMinWidth();
            nscoord colWidth = PR_MAX(minWidth, colFrame->GetFixWidth());
            colWidth = PR_MAX(colWidth, colFrame->GetDesWidth()); // XXX check this
            float avail = (float)PR_MAX(cellPctWidth - colPctWidthTotal, 0);
            float colPctAdj = (0 == spanTotal) 
                              ? avail / ((float) usedColumns) / ((float)basis)
                              : (avail / (float)basis) * (((float)colWidth) / (float)spanTotal);
            if (colPctAdj > 0) {
              nscoord colPctAdjWidth = colFrame->GetWidth(PCT_ADJ);
              nscoord newColPctAdjWidth = nsTableFrame::RoundToPixel(NSToCoordRound(colPctAdj * (float)basis), aPixelToTwips);
              if (newColPctAdjWidth > colPctAdjWidth) {
                newColPctAdjWidth = PR_MAX(newColPctAdjWidth, minWidth); 
                if (newColPctAdjWidth > colFrame->GetWidth(PCT)) {
                  colFrame->SetWidth(PCT_ADJ, newColPctAdjWidth);
                  if(0 != basis) { // I am paranoid
                    colPctTotal += NSToCoordRound(100.0f *(newColPctAdjWidth-colPctAdjWidth) / (float)basis); 
                    // accumulate the new distributed percents
                  }
                }
              }
              else {
                usedColumns--;
                colPctWidthTotal += colPctAdjWidth; // accumulate the already distributed percents
              }
            }
          }
        }
      }
    } // end for (rowX ..
  } // end for (colX ..
  delete [] numColSpans;
  delete [] rowIndices;
  // if the percent total went over 100%, adjustments need to be made to right most cols
  if (colPctTotal > 100) {
    ReduceOverSpecifiedPctCols(nsTableFrame::RoundToPixel(NSToCoordRound(((float)(colPctTotal - 100)) * 0.01f * (float)basis), aPixelToTwips));
  }

  // adjust the basis to include table border, padding and cell spacing
  basis += borderPadding.left + borderPadding.right + mCellSpacingTotal;
  return WrapupAssignPctColumnWidths(mTableFrame, aReflowState, basis); 
}


// calculate totals by width type. The logic here is kept in synch with 
// that in CanAllocate. aDupedWidths (duplicatd) are widths that will be 
// allocated in BalanceColumnWidths before aTotalsWidths (e.g. aTotalWidths[PCT] 
// will have aDuplicatedWidths[PCT] consisting of the MIN widths of cols which
// have a PCT width).
void BasicTableLayoutStrategy::CalculateTotals(PRInt32* aTotalCounts,
                                               PRInt32* aTotalWidths,
                                               PRInt32* aDupedWidths, 
                                               PRInt32& a0ProportionalCount)
{
  //mTableFrame->Dump(PR_TRUE, PR_FALSE);
  for (PRInt32 widthX = 0; widthX < NUM_WIDTHS; widthX++) {
    aTotalCounts[widthX]      = 0;
    aTotalWidths[widthX]      = 0;
    aDupedWidths[widthX]      = 0;
  }
  a0ProportionalCount = 0;

  PRInt32 numEffCols = mTableFrame->GetEffectiveColCount();
  PRInt32 colX;

  for (colX = 0; colX < numEffCols; colX++) { 
    nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); 
    if (!colFrame) continue;
    nscoord minCol = colFrame->GetMinWidth();
    aTotalCounts[MIN_CON]++;
    aTotalWidths[MIN_CON] += minCol;

    if (e0ProportionConstraint == colFrame->GetConstraint()) {
      a0ProportionalCount++;
    }

    nscoord pct    = colFrame->GetPctWidth();
    nscoord fix    = colFrame->GetWidth(FIX);
    nscoord fixAdj = colFrame->GetWidth(FIX_ADJ);
    // if there is a pct width then no others are considered
    if (pct > 0) {
      aTotalCounts[PCT]++;
      aTotalWidths[PCT] += PR_MAX(pct, minCol);
      aDupedWidths[PCT] += minCol;
    }
    else if ((fix > 0) || (fixAdj > 0)) {
      if (fix > 0) {
        aTotalCounts[FIX]++;
        aTotalWidths[FIX] += PR_MAX(fix, minCol);
        aDupedWidths[FIX] += minCol;
      }
      if (fixAdj > 0) {
        if (fixAdj > fix) {
          aTotalCounts[FIX_ADJ]++;
          aTotalWidths[FIX_ADJ] += PR_MAX(fixAdj, minCol);
          if (fix > 0) {
            aDupedWidths[FIX_ADJ] += fix;
          }
          else { // there was no fix
            aDupedWidths[FIX_ADJ] += minCol;
          }
        }
      }
    }
    else if ((eProportionConstraint == colFrame->GetConstraint()) ||
             (e0ProportionConstraint == colFrame->GetConstraint())) {
      aTotalCounts[MIN_PRO]++;
      aTotalWidths[MIN_PRO] += PR_MAX(colFrame->GetWidth(MIN_PRO), minCol);
      aDupedWidths[MIN_PRO] += minCol;
    }
    else {
      // desired alone is lowest priority
      aTotalCounts[DES_CON]++;
      aTotalWidths[DES_CON] += PR_MAX(colFrame->GetDesWidth(), minCol);
      aDupedWidths[DES_CON] += minCol;
    }
  }
}


struct ColInfo {
  ColInfo(nsTableColFrame* aFrame,
          PRInt32          aIndex,
          PRInt32          aMinWidth,
          PRInt32          aWidth,
          PRInt32          aMaxWidth)
    : mFrame(aFrame), mIndex(aIndex), mMinWidth(aMinWidth), 
      mWidth(aWidth), mMaxWidth(aMaxWidth), mWeight(0)
  {}
  nsTableColFrame* mFrame;
  PRInt32          mIndex;
  PRInt32          mMinWidth;
  PRInt32          mWidth;
  PRInt32          mMaxWidth;
  float            mWeight;
};

void
AC_Wrapup(nsTableFrame* aTableFrame,
          PRInt32       aNumItems, 
          ColInfo**     aColInfo,
          PRBool        aAbort = PR_FALSE)
{
  if (aColInfo) {
    for (PRInt32 i = 0; i < aNumItems; i++) {
      if (aColInfo[i]) {
        if (!aAbort) {
          aTableFrame->SetColumnWidth(aColInfo[i]->mIndex, aColInfo[i]->mWidth);
        }
        delete aColInfo[i];
      }
    }
    delete [] aColInfo;
  }
}

void
AC_Increase(PRInt32     aNumAutoCols,
            ColInfo**   aColInfo,
            PRInt32     aDivisor,
            PRInt32&    aAvailWidth,
            float       aPixelToTwips)
{
  for (PRInt32 i = 0; i < aNumAutoCols; i++) {
    if ((aAvailWidth <= 0) || (aDivisor <= 0)) {
      break;
    }
    // aDivisor represents the sum of unallocated space (diff between max and min values)
    float percent = ((float)aColInfo[i]->mMaxWidth - (float)aColInfo[i]->mMinWidth) / (float)aDivisor;
    aDivisor -= aColInfo[i]->mMaxWidth - aColInfo[i]->mMinWidth;

    nscoord addition = nsTableFrame::RoundToPixel(NSToCoordRound(((float)(aAvailWidth)) * percent), aPixelToTwips);
    // if its the last col, try to give what's left to it
    if ((i == aNumAutoCols - 1) && (addition < aAvailWidth)) {
      addition = aAvailWidth;
    }
    // don't let the addition exceed what is available to add
    addition = PR_MIN(addition, aAvailWidth);
    // don't go over the col max
    addition = PR_MIN(addition, aColInfo[i]->mMaxWidth - aColInfo[i]->mWidth);
    aColInfo[i]->mWidth += addition;
    aAvailWidth -= addition;
  }
}

void
AC_Decrease(PRInt32     aNumAutoCols,
            ColInfo**   aColInfo,
            PRInt32     aDivisor,
            PRInt32&    aExcess,
            float       aPixelToTwips)
{
  for (PRInt32 i = 0; i < aNumAutoCols; i++) {
    if ((aExcess <= 0) || (aDivisor <= 0)) {
      break;
    }
    float percent = ((float)aColInfo[i]->mMaxWidth) / (float)aDivisor;
    aDivisor -= aColInfo[i]->mMaxWidth;
    nscoord reduction = nsTableFrame::RoundToPixel(NSToCoordRound(((float)(aExcess)) * percent), aPixelToTwips);
    // if its the last col, try to remove the remaining excess from it
    if ((i == aNumAutoCols - 1) && (reduction < aExcess)) {
      reduction = aExcess;
    }
    // don't let the reduction exceed what is available to reduce
    reduction = PR_MIN(reduction, aExcess);
    // don't go under the col min
    reduction = PR_MIN(reduction, aColInfo[i]->mWidth - aColInfo[i]->mMinWidth);
    aColInfo[i]->mWidth -= reduction;
    aExcess -= reduction;
  }
}

void 
AC_Sort(ColInfo** aColInfo, PRInt32 aNumCols)
{
  // sort the cols based on the Weight 
  for (PRInt32 j = aNumCols - 1; j > 0; j--) {
    for (PRInt32 i = 0; i < j; i++) { 
      if (aColInfo[i]->mWeight < aColInfo[i+1]->mWeight) { // swap them
        ColInfo* save = aColInfo[i];
        aColInfo[i]   = aColInfo[i+1];
        aColInfo[i+1] = save;
      }
    }
  }
}

void 
BasicTableLayoutStrategy::RowSort(PRInt32* aRowIndices, PRInt32* aColSpans,
                                  PRInt32 aIndex)
{
  PRInt32 swapCol, swapRow;
  // sort the rows based on the colspan size 
  for (PRInt32 j = aIndex - 1; j > 0; j--) {
    for (PRInt32 i = 0; i < j; i++) { 
      if (aColSpans[i] > aColSpans[i+1]) { // swap them
        swapCol = aColSpans[i];
        swapRow = aRowIndices[i];
        aColSpans[i]   = aColSpans[i+1];
        aRowIndices[i] = aRowIndices[i+1];
        aColSpans[i+1]   = swapCol;
        aRowIndices[i+1] = swapRow; 
      }
    }
  }
}
// this assumes that the table has set the width for each col to be its min
void BasicTableLayoutStrategy::AllocateConstrained(PRInt32  aAvailWidth,
                                                   PRInt32  aWidthType,
                                                   PRBool   aStartAtMin,        
                                                   PRInt32* aAllocTypes,
                                                   float    aPixelToTwips)
{
  if ((0 == aAvailWidth) || (aWidthType < 0) || (aWidthType >= NUM_WIDTHS)) {
    NS_ASSERTION(PR_TRUE, "invalid args to AllocateConstrained");
    return;
  }

  PRInt32 numCols = mTableFrame->GetColCount();
  PRInt32 numConstrainedCols = 0;
  nscoord sumMaxConstraints  = 0;
  nscoord sumMinConstraints  = 0;
  PRInt32 colX;
  // find out how many constrained cols there are
  for (colX = 0; colX < numCols; colX++) {
    nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); 
    if (!colFrame) continue;
    if (!CanAllocate(aWidthType, aAllocTypes[colX], colFrame)) {
      continue;
    }
    numConstrainedCols++;
  }

  // allocate storage for the constrained cols. Only they get adjusted.
  ColInfo** colInfo = new ColInfo*[numConstrainedCols];
  if (!colInfo) return;
  memset(colInfo, 0, numConstrainedCols * sizeof(ColInfo *));

  PRInt32 maxMinDiff = 0;
  PRInt32 constrColX = 0;
  // set the col info entries for each constrained col
  for (colX = 0; colX < numCols; colX++) {
    nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); 
    if (!colFrame) continue;
    if (!CanAllocate(aWidthType, aAllocTypes[colX], colFrame)) {
      continue;
    }
    nscoord minWidth = mTableFrame->GetColumnWidth(colX);
    nscoord maxWidth = GetColWidth(colFrame, aWidthType);
    // proportional and desired widths are handled together
    PRBool haveProWidth = PR_FALSE;
    if (DES_CON == aWidthType) {
      // Take into acount MIN_adj width as it has been included in totals before
      maxWidth = PR_MAX(maxWidth, colFrame->GetWidth(MIN_ADJ));
      nscoord proWidth = colFrame->GetWidth(MIN_PRO);
      if (proWidth >= 0) {
        haveProWidth = PR_TRUE;
        maxWidth = proWidth;
      }
    }

    if (maxWidth <= 0) continue;

    sumMaxConstraints += maxWidth;
    sumMinConstraints += minWidth;

    maxWidth = PR_MAX(maxWidth, minWidth);
    maxMinDiff += maxWidth - minWidth;
    nscoord startWidth = (aStartAtMin) ? minWidth : maxWidth;
    colInfo[constrColX] = new ColInfo(colFrame, colX, minWidth, startWidth, maxWidth);
    if (!colInfo[constrColX]) {
      AC_Wrapup(mTableFrame, numConstrainedCols, colInfo, PR_TRUE);
      return;
    }
    aAllocTypes[colX] = (haveProWidth) ? MIN_PRO : aWidthType;
    constrColX++;
  }

  if (constrColX < numConstrainedCols) {
    // some of the constrainted cols might have been 0 and skipped
    numConstrainedCols = constrColX;
  }

  PRInt32 i;
  if (aStartAtMin) { // allocate extra space 
    nscoord availWidth = aAvailWidth; 
    for (i = 0; i < numConstrainedCols; i++) {
      // the weight here is a relative metric for determining when cols reach their max constraint. 
      // A col with a larger weight will reach its max before one with a smaller value.
      nscoord delta = colInfo[i]->mMaxWidth - colInfo[i]->mWidth;
      colInfo[i]->mWeight = (delta <= 0) 
        ? 1000000 // cols which have already reached their max get a large value
        : ((float)colInfo[i]->mMaxWidth) / ((float)delta);
    }
      
    // sort the cols based on the weight so that in one pass cols with higher 
    // weights will get their max earlier than ones with lower weights
    // This is an innefficient bubble sort, but unless there are an unlikely 
    // large number of cols, it is not an issue.
    AC_Sort(colInfo, numConstrainedCols);
   
    // compute the proportion to be added to each column, don't go beyond the col's
    // max. This algorithm assumes that the Weight works as stated above
    AC_Increase(numConstrainedCols, colInfo, sumMaxConstraints - sumMinConstraints, 
                availWidth, aPixelToTwips);
  }
  else { // reduce each col width 
    nscoord reduceWidth = maxMinDiff - aAvailWidth;
    if (reduceWidth < 0) {
      NS_ASSERTION(PR_TRUE, "AllocateConstrained called incorrectly");
      AC_Wrapup(mTableFrame, numConstrainedCols, colInfo);
      return;
    }
    for (i = 0; i < numConstrainedCols; i++) {
      // the weight here is a relative metric for determining when cols reach their min. 
      // A col with a larger weight will reach its min before one with a smaller value.
      nscoord delta = colInfo[i]->mWidth - colInfo[i]->mMinWidth;
      colInfo[i]->mWeight = (delta <= 0) 
        ? 1000000 // cols which have already reached their min get a large value
        : ((float)colInfo[i]->mWidth) / ((float)delta);
    }
      
    // sort the cols based on the Weight 
    AC_Sort(colInfo, numConstrainedCols);
   
    // compute the proportion to be subtracted from each column, don't go beyond 
    // the col's min. This algorithm assumes that the Weight works as stated above
    AC_Decrease(numConstrainedCols, colInfo, sumMaxConstraints, reduceWidth, aPixelToTwips);
  }
  AC_Wrapup(mTableFrame, numConstrainedCols, colInfo);
}

#ifdef DEBUG_TABLE_STRATEGY
void BasicTableLayoutStrategy::Dump(PRInt32 aIndent)
{
  char* indent = new char[aIndent + 1];
  if (!indent) return;
  for (PRInt32 i = 0; i < aIndent + 1; i++) {
    indent[i] = ' ';
  }
  indent[aIndent] = 0;

  printf("%s**START BASIC STRATEGY DUMP** table=%p cols=%X",
         indent, mTableFrame, mCols);
  printf("\n%s cellSpacing=%d propRatio=%.2f navQuirks=%d",
    indent, mCellSpacingTotal, mMinToDesProportionRatio, mIsNavQuirksMode);
  printf(" **END BASIC STRATEGY DUMP** \n");
  delete [] indent;
}
#endif
