/* -*- 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 "nsVoidArray.h"
#include "nsCRT.h"
#include "nsISizeOfHandler.h"

static PRInt32 kGrowArrayBy = 8;

nsVoidArray::nsVoidArray()
{
  mArray = nsnull;
  mArraySize = 0;
  mCount = 0;
}

nsVoidArray::nsVoidArray(PRInt32 aCount)
{
  NS_PRECONDITION(aCount > 0, "bad count");
  mCount = mArraySize = aCount;
  mArray = new void*[mCount];
  nsCRT::memset(mArray, 0, mCount * sizeof(void*));
}

nsVoidArray& nsVoidArray::operator=(const nsVoidArray& other)
{
  if (nsnull != mArray) {
    delete mArray;
  }
  PRInt32 otherCount = other.mCount;
  mArraySize = otherCount;
  mCount = otherCount;
  if (otherCount != 0) {
    mArray = new void*[otherCount];
    nsCRT::memcpy(mArray, other.mArray, otherCount * sizeof(void*));
  } else {
    mArray = nsnull;
  }
  return *this;
}

nsVoidArray::~nsVoidArray()
{
  if (nsnull != mArray) {
    delete [] mArray;
  }
}
void
nsVoidArray::SizeOf(nsISizeOfHandler* aHandler) const
{
  aHandler->Add(sizeof(*this));
  aHandler->Add(sizeof(void*) * mArraySize);
}

void* nsVoidArray::ElementAt(PRInt32 aIndex) const
{
  if (PRUint32(aIndex) >= PRUint32(mCount)) {
    return nsnull;
  }
  return mArray[aIndex];
}

PRInt32 nsVoidArray::IndexOf(void* aPossibleElement) const
{
  void** ap = mArray;
  void** end = ap + mCount;
  while (ap < end) {
    if (*ap == aPossibleElement) {
      return ap - mArray;
    }
    ap++;
  }
  return -1;
}

PRBool nsVoidArray::InsertElementAt(void* aElement, PRInt32 aIndex)
{
  PRInt32 oldCount = mCount;
  if (PRUint32(aIndex) > PRUint32(oldCount)) {
    // An invalid index causes the insertion to fail
    return PR_FALSE;
  }

  if (oldCount + 1 > mArraySize) {
    // We have to grow the array
    PRInt32 newCount = oldCount + kGrowArrayBy;
    void** newArray = new void*[newCount];
    if (mArray != nsnull && aIndex != 0)
      nsCRT::memcpy(newArray, mArray, aIndex * sizeof(void*));
    PRInt32 slide = oldCount - aIndex;
    if (0 != slide) {
      // Slide data over to make room for the insertion
      nsCRT::memcpy(newArray + aIndex + 1, mArray + aIndex,
                    slide * sizeof(void*));
    }
    if (mArray != nsnull)
      delete [] mArray;
    mArray = newArray;
    mArraySize = newCount;
  } else {
    // The array is already large enough
    PRInt32 slide = oldCount - aIndex;
    if (0 != slide) {
      // Slide data over to make room for the insertion
      nsCRT::memmove(mArray + aIndex + 1, mArray + aIndex,
                     slide * sizeof(void*));
    }
  }
  mArray[aIndex] = aElement;
  mCount++;

  return PR_TRUE;
}

PRBool nsVoidArray::ReplaceElementAt(void* aElement, PRInt32 aIndex)
{
  if (PRUint32(aIndex) >= PRUint32(mArraySize)) {

    PRInt32 requestedCount = aIndex + 1;
    PRInt32 growDelta = requestedCount - mCount;
    PRInt32 newCount = mCount + (growDelta > kGrowArrayBy ? growDelta : kGrowArrayBy);
    void** newArray = new void*[newCount];
    nsCRT::memset(newArray, 0, newCount * sizeof(void*));
    if (newArray==nsnull)
       return PR_FALSE;
    if (mArray != nsnull && aIndex != 0) {
      nsCRT::memcpy(newArray, mArray, mCount * sizeof(void*));
      if (mArray != nsnull)
         delete [] mArray;
    }
    mArray = newArray;
    mArraySize = newCount;
  }
  mArray[aIndex] = aElement;
  if (aIndex >= mCount)
     mCount = aIndex+1;
  return PR_TRUE;
}

PRBool nsVoidArray::RemoveElementAt(PRInt32 aIndex)
{
  PRInt32 oldCount = mCount;
  if (PRUint32(aIndex) >= PRUint32(oldCount)) {
    // An invalid index causes the replace to fail
    return PR_FALSE;
  }

  // We don't need to move any elements if we're removing the
  // last element in the array
  if (aIndex < (oldCount - 1)) {
    nsCRT::memmove(mArray + aIndex, mArray + aIndex + 1,
                   (oldCount - 1 - aIndex) * sizeof(void*));
  }

  mCount--;
  return PR_TRUE;
}

PRBool nsVoidArray::RemoveElement(void* aElement)
{
  void** ep = mArray;
  void** end = ep + mCount;
  while (ep < end) {
    void* e = *ep++;
    if (e == aElement) {
      ep--;
      return RemoveElementAt(PRInt32(ep - mArray));
    }
  }
  return PR_FALSE;
}

void nsVoidArray::Clear()
{
  mCount = 0;
}

void nsVoidArray::Compact()
{
  PRInt32 count = mCount;
  if (mArraySize != count) {
    void** newArray = new void*[count];
    if (nsnull != newArray) {
      nsCRT::memcpy(newArray, mArray, count * sizeof(void*));
      delete [] mArray;
      mArray = newArray;
      mArraySize = count;
    }
  }
}

PRBool nsVoidArray::EnumerateForwards(nsVoidArrayEnumFunc aFunc, void* aData)
{
  PRInt32 index = -1;
  PRBool  running = PR_TRUE;

  while (running && (++index < mCount)) {
    running = (*aFunc)(mArray[index], aData);
  }
  return running;
}

PRBool nsVoidArray::EnumerateBackwards(nsVoidArrayEnumFunc aFunc, void* aData)
{
  PRInt32 index = mCount;
  PRBool  running = PR_TRUE;

  while (running && (0 <= --index)) {
    running = (*aFunc)(mArray[index], aData);
  }
  return running;
}

