/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 *
 * ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla 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/MPL/
 *
 * 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 of 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 MPL, 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 MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK *****
 * This Original Code has been modified by IBM Corporation. Modifications made by IBM 
 * described herein are Copyright (c) International Business Machines Corporation, 2000.
 * Modifications to Mozilla code or documentation identified per MPL Section 3.3
 *
 * Date             Modified by     Description of modification
 * 04/20/2000       IBM Corp.      OS/2 VisualAge build.
 */

#include "msgCore.h"    // precompiled header...
#include "prlog.h"

#include "MailNewsTypes.h"
#include "nsUInt32Array.h"
#include "nsQuickSort.h"

MOZ_DECL_CTOR_COUNTER(nsUInt32Array)

nsUInt32Array::nsUInt32Array()
{
  MOZ_COUNT_CTOR(nsUInt32Array);
  m_nSize = 0;
  m_nMaxSize = 0;
  m_nGrowBy = 0;
  m_pData = NULL;
}

nsUInt32Array::~nsUInt32Array()
{
  MOZ_COUNT_DTOR(nsUInt32Array);
  SetSize(0);
}

/////////////////////////////////////////////////////////////////////////////

PRUint32 nsUInt32Array::GetSize() const
{
  return m_nSize;
}

PRBool nsUInt32Array::SetSize(PRUint32 nSize,
                              PRBool adjustGrowth,
                              PRUint32 nGrowBy)
{
  if (adjustGrowth)
    m_nGrowBy = nGrowBy;
  
#ifdef MAX_ARR_ELEMS
  if (nSize > MAX_ARR_ELEMS);
  {
    PR_ASSERT(nSize <= MAX_ARR_ELEMS); // Will fail
    return PR_FALSE;
  }
#endif
  
  if (nSize == 0)
  {
    // Remove all elements
    PR_Free(m_pData);
    m_nSize = 0;
    m_nMaxSize = 0;
    m_pData = NULL;
  }
  else if (m_pData == NULL)
  {
    // Create a new array
    m_nMaxSize = PR_MAX(8, nSize);
    m_pData = (PRUint32 *)PR_Calloc(1, m_nMaxSize * sizeof(PRUint32));
    if (m_pData)
      m_nSize = nSize;
    else
      m_nSize = m_nMaxSize = 0;
  }
  else if (nSize <= m_nMaxSize)
  {
    // The new size is within the current maximum size, make sure new
    // elements are to initialized to zero
    if (nSize > m_nSize)
      memset(&m_pData[m_nSize], 0, (nSize - m_nSize) * sizeof(PRUint32));
    
    m_nSize = nSize;
  }
  else
  {
    // The array needs to grow, figure out how much
    PRUint32 nMaxSize;
    nGrowBy  = PR_MAX(m_nGrowBy, PR_MIN(1024, PR_MAX(8, m_nSize / 8)));
    nMaxSize = PR_MAX(nSize, m_nMaxSize + nGrowBy);
#ifdef MAX_ARR_ELEMS
    nMaxSize = PR_MIN(MAX_ARR_ELEMS, nMaxSize);
#endif
    
    PRUint32 *pNewData = (PRUint32 *)PR_Malloc(nMaxSize * sizeof(PRUint32));
    if (pNewData)
    {
      // Copy the data from the old array to the new one
      memcpy(pNewData, m_pData, m_nSize * sizeof(PRUint32));
      
      // Zero out the remaining elements
      memset(&pNewData[m_nSize], 0, (nSize - m_nSize) * sizeof(PRUint32));
      m_nSize = nSize;
      m_nMaxSize = nMaxSize;
      
      // Free the old array
      PR_Free(m_pData);
      m_pData = pNewData;
    }
  }
  
  return nSize == m_nSize;
}

/////////////////////////////////////////////////////////////////////////////

PRUint32 &nsUInt32Array::ElementAt(PRUint32 nIndex)
{
  NS_ASSERTION(nIndex < m_nSize, "array index out of bounds");
  return m_pData[nIndex];
}

PRUint32 nsUInt32Array::GetAt(PRUint32 nIndex) const
{
  NS_ASSERTION(nIndex < m_nSize, "array index out of bounds");
  return m_pData[nIndex];
}

PRUint32 *nsUInt32Array::GetData() 
{
  return m_pData;
}

void nsUInt32Array::SetAt(PRUint32 nIndex, PRUint32 newElement)
{
  NS_ASSERTION(nIndex < m_nSize, "array index out of bounds");
  m_pData[nIndex] = newElement;
}

/////////////////////////////////////////////////////////////////////////////

PRUint32 nsUInt32Array::Add(PRUint32 newElement)
{
  PRUint32 nIndex = m_nSize;
  
#ifdef MAX_ARR_ELEMS
  if (nIndex >= MAX_ARR_ELEMS) 
    return -1;	     
#endif			
  
  SetAtGrow(nIndex, newElement);
  return nIndex;
}

PRUint32 nsUInt32Array::Add(PRUint32 *elementPtr, PRUint32 numElements) 
{ 
  if (SetSize(m_nSize + numElements))
    memcpy(m_pData + m_nSize - numElements, elementPtr, numElements * sizeof(PRUint32)); 
  
  return m_nSize; 
} 

PRUint32 *nsUInt32Array::CloneData() 
{ 
  PRUint32 *copyOfData = (PRUint32 *)PR_Malloc(m_nSize * sizeof(PRUint32)); 
  if (copyOfData) 
    memcpy(copyOfData, m_pData, m_nSize * sizeof(PRUint32)); 
  
  return copyOfData; 
} 

void nsUInt32Array::InsertAt(PRUint32 nIndex, PRUint32 newElement, PRUint32 nCount)
{
  NS_ASSERTION(nCount > 0, "can't insert 0 elements");
  
  if (nIndex >= m_nSize)
  {
    // If the new element is after the end of the array, grow the array
    SetSize(nIndex + nCount);
  }
  else
  {
    // The element is being insert inside the array
    int nOldSize = m_nSize;
    SetSize(m_nSize + nCount);
    
    // Move the data after the insertion point
    memmove(&m_pData[nIndex + nCount], &m_pData[nIndex],
      (nOldSize - nIndex) * sizeof(PRUint32));
  }
  
  // Insert the new elements
  NS_ASSERTION(nIndex + nCount <= m_nSize, "setting size failed");
  while (nCount--)
    m_pData[nIndex++] = newElement;
}

void nsUInt32Array::InsertAt(PRUint32 nStartIndex, const nsUInt32Array *pNewArray)
{
  NS_ASSERTION(pNewArray, "inserting null array");
  
  if (pNewArray)
  {
    if (pNewArray->GetSize() > 0)
    {
      InsertAt(nStartIndex, pNewArray->GetAt(0), pNewArray->GetSize());
      for (PRUint32 i = 1; i < pNewArray->GetSize(); i++)
        m_pData[nStartIndex + i] = pNewArray->GetAt(i);
    }
  }
}

void nsUInt32Array::RemoveAll()
{
  SetSize(0);
}

void nsUInt32Array::RemoveAt(PRUint32 nIndex, PRUint32 nCount)
{
  NS_ASSERTION(nIndex + nCount <= m_nSize, "removing past end of array");
  
  if (nCount > 0)
  {
    // Make sure not to overstep the end of the array
    int nMoveCount = m_nSize - (nIndex + nCount);
    if (nCount && nMoveCount)
      memmove(&m_pData[nIndex], &m_pData[nIndex + nCount],
      nMoveCount * sizeof(PRUint32));
    
    m_nSize -= nCount;
  }
}

void nsUInt32Array::SetAtGrow(PRUint32 nIndex, PRUint32 newElement)
{
  if (nIndex >= m_nSize)
    SetSize(nIndex+1);
  m_pData[nIndex] = newElement;
}

PRBool nsUInt32Array:: RemoveElement(PRUint32 element)
{
  for (PRUint32 i = 0; i < GetSize(); i++)
  {
    if ((PRUint32)(m_pData[i]) == element)
    {
      RemoveAt(i, 1);
      return PR_TRUE;
    }
  }
  return PR_FALSE;
}


PRUint32 nsUInt32Array::IndexOf(PRUint32 element)
{
  for (PRUint32 i = 0; i < GetSize(); i++)
  {
    if ((PRUint32)(m_pData[i]) == element)
    {
      return i;
    }
  }
  return (PRUint32) kNotFound;
}

/////////////////////////////////////////////////////////////////////////////

void nsUInt32Array::CopyArray(nsUInt32Array *oldA)
{
  CopyArray(*oldA);
}

void nsUInt32Array::CopyArray(nsUInt32Array &oldA)
{
  if (m_pData)
    PR_Free(m_pData);
  m_nSize = oldA.m_nSize;
  m_nMaxSize = oldA.m_nSize;
  m_pData = (PRUint32 *)PR_Malloc(m_nSize * sizeof(PRUint32));
  if (m_pData)
    memcpy(m_pData, oldA.m_pData, m_nSize * sizeof(PRUint32));
}

/////////////////////////////////////////////////////////////////////////////

static int PR_CALLBACK CompareDWord (const void *v1, const void *v2, void *)
{
  // QuickSort callback to compare array values
  PRUint32 i1 = *(PRUint32 *)v1;
  PRUint32 i2 = *(PRUint32 *)v2;
  return i1 < i2 ? -1 : (i1 == i2 ? 0 : 1);
}

void nsUInt32Array::QuickSort (int (* PR_CALLBACK compare) (const void *elem1, const void *elem2, void *data))
{
  if (m_nSize > 1)
    NS_QuickSort(m_pData, m_nSize, sizeof(PRUint32), compare ? compare : CompareDWord, nsnull);
}

// Does a binary search - assumes array is sorted in ascending order.
PRUint32 nsUInt32Array::IndexOfSorted(PRUint32 element)
{
  PRInt32 msgIndex = 0;
  PRInt32 hi = m_nSize - 1;
  PRInt32 lo = 0;
  
  while (lo <= hi)
  {
    msgIndex = (lo + hi) / 2;
    if (m_pData[msgIndex] == element)
      return msgIndex;
    if (m_pData[msgIndex] > element)
      hi = msgIndex -1;
    else if (m_pData[msgIndex] <  element)
      lo = msgIndex + 1;
  }
  return kNotFound;
}

