OZ++ Class: ArrayT
/******************************************************************************
 *
 * Copyright (c) 2014 Antillia.com TOSHIYUKI ARAI. ALL RIGHTS RESERVED.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions, and the following disclaimer.
 *  
 * 2. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *
 *  ArrayT.h
 *
 *****************************************************************************/

//2014/12/09 Modified the size of array to be automatical expanded.

#pragma once

#include <oz++/CommonObject.h>
#include <oz++/Sortable.h>
#include <oz++/Exception.h>

namespace OZ {
  
template <class T> class ArrayT :public CommonObject, public Sortable {

private:
  size_t  capacity;
  size_t  size;
  T*      array;
 
private:
  bool copy(const ArrayT& arr)
  {
    const T* ar = arr.array;
    const size_t size = arr.size;

    assert(ar);
    assert(size>0);

    bool rc = false;
    T* temp = new T[size];
    if(temp) {
      for(size_t i = 0; i<size; i++) {
        temp[i] = ar[i];
      }

      if(this->array != ar && this->array !=NULL) {
        clear();
      }
      this->array = temp;
      this->size = size;
      this->capacity = size;
        
      rc = true;
    } else {
      Exception("Failed to allocate a memory").display();
      assert(temp == NULL);
      //Memory allocation error.
    }
    return rc;
  }

  void clear()
  {
    if (this->array) {
      delete [] this->array;
    }
    this->array =NULL;

    this->size  = 0;
    this->capacity = 0;
  }

  static bool memcpy(T* dest, size_t dlen, T* src, size_t slen)
  //memcpy(T* dest, size_t dlen, T* src, size_t slen)
  {
    bool rc = true;
    if (dest && src && dlen >=slen && dlen >0) {
      for (size_t i = 0; i<slen; i++) {
        dest[i] = src[i];
      }
    } else {
      throw IException("Invalid argument");
    }
    return rc;
  }

  static bool memset(T* dest, T value, size_t len)
  {
    bool rc = true;
    if (dest && len >0) {
      for (size_t i = 0; i<len; i++) {
        dest[i] = value;
      }
    } else {
      throw IException("Invalid argument");
    }
    return rc;
  }

  //static comparison methods
protected:
  static  int compare(const T* a, const T* b)
  {
    if (*a == *b) {
      return 0;
    } else {
      if (*a < *b) {
        return -1;
      } else {
        return 1;
      }
    }
  }

protected:
  static  int _compare(const T* a, const T* b)
  {
    return -compare(a, b);
  }


protected:
  virtual void getFormat(char* format, size_t size, size_t v)
  {
    assert(format);
    assert(size>=128);
    snprintf(format, size, "%%0%zdx ", v*2);
  }

private:
  //2014/12/09
  static  const int DEFAULT_SIZE   = 100;

  static  const int AUTO_INCREMENT = 100;

////////////////////////////////////////////////////////////////////////
public:
  /**
   * Constructor
   */
  ArrayT()
  :capacity(DEFAULT_SIZE),
  size(DEFAULT_SIZE),
  array(NULL)
  {
    array = new T[size]; //2015/05/13 
  }

  /**
   * Constructor
   */
  ArrayT(T* ar, size_t s)
  :capacity(s),
  size(s),
  array(NULL)
  {
    deepCopy(ar, size);
  }

  /**
   * Constructor
   */
  ArrayT(const ArrayT* arr)
  :size(0),
  array(NULL)
  {
    if (arr) {
      assert(arr);
      copy(*arr);
    }
  }

  /**
   * Constructor
   */
  ArrayT(const ArrayT& arr)
  :size(0),
  array(NULL)
  {
    copy(arr);
  }

  /**
   * Destructor
   */
  ~ArrayT()
  {
    clear();
  }
  
  const T*   getArray()
  {
    return this->array;
  }
  

//ISO C++ says that these are ambiguous,even though the worst conversion for the 
//first is better than the worst conversion for the second:
// 2014/12/11 commented out the following operator to avoid the above warning.
/*
  operator const T*() const
  {
    return this->array;
  }
*/
  const T* getData() const
  {
    return this->array;
  }

  size_t getSize() const
  {
    return this->size;
  }

  bool isEmpty()
  {
    bool rc = false;
    if (this->size==0 && this->array ==NULL) {
      rc = true;
    }
    return rc;
  }

  void deepCopy(T* arr, size_t s)
  {
    assert(arr);
    assert(s > 0);
    if (this->array != arr) {
      clear();
    }
    this->size = s;
    this->capacity = s;
    this->array = new T[this->size];
    for(size_t i = 0; i<this->size; i++) {
      this->array[i] = arr[i];
    }
  }

  void shallowCopy(T* arr, size_t s)
  {
    assert(arr);
    assert(s>0);

    if (this->array != arr) {
      clear();
    }
    this->array   = arr;
    this->size = s;
      this->capacity = s;
  }


  ArrayT& operator=(const ArrayT& array)
  {
    if (copy(array) == false) {
      throw IException("Failed to copy an array");
    }
    return *this;
  }

  bool operator==(const ArrayT& arr)
  {
     bool rc = false;

    if (this->array != NULL) {
      size_t s1 = this->size;
      const size_t s2 = arr.getSize();
      const T* data = (const T*)arr;
      if (s1 != s2) {
        return false;
      } else {
        rc = true;
        for(size_t i = 0; i<this->size; i++) {
          if ( this->array[i] != data[i]) {
            rc = false;
            break;
          }
        }
      }
    }
    return rc;
  }

  bool memcpy(size_t pos, const T* data, size_t len)
  {
    bool rc = true;
    if (pos >=0 && data != NULL && (pos + len)<=size) {
      for (size_t i = 0; i<len; i++) {
        array[pos + i] = data[i];
      }
    } else {
      throw IException("Invalid argument");
    }
    return rc;
  }

  bool resize(size_t len)
  {
    bool rc = false;
    if (len <= 0) {
      throw IException("Invalid argument");
    }
    size_t nlen =  len;
    T* temp = new T[nlen];

    if(temp) {
         for (size_t i = 0; i<nlen; i++) {
             temp[i] = (T)NULL;
        }
      //memset(temp, (T)0, nlen);
      //memcpy(temp, nlen, this->array, this->size);
        for (size_t n = 0; n< this->size; n++) {
          temp[n] = array[n];
        }
    } else {
      return rc;
    }
    this -> size = nlen;
    this -> capacity = nlen;
      
    delete [] this->array;
    this -> array = temp;
    rc = true;

    return rc;
  }

  T&  operator[](size_t pos)
  {
    if (pos>=0 && pos < size) {
      //2015/05/12
      if (array == NULL) {
        array = new T[size];
      }
      return array[pos];
     
    } else if (pos >= size) {
      //2014/12/09
      resize( pos + AUTO_INCREMENT);
      //size = pos;
    
      return array[pos];    
    } else {
      throw IException("Index:out of range, %d", pos);
    }
  }


  T&  at(size_t pos)
  {
    if (pos>=0 && pos < size) {
      //2015/05/12
      if (array == NULL) {
         array = new T[size]; 
      }
      return array[pos];
      
    } else if (pos >= size) {
      //2014/12/09
      resize( pos + AUTO_INCREMENT);
      //size = pos;
        
      return array[pos];    
    } else {
      throw IException("Index:out of range, %d", pos);
    }
  }
    
  T  getNth(size_t pos)
  {
    T val = (T)0;
    if(pos>=0 && pos < size) {
      if (array == NULL) {
         array = new T[size];
      }
      val = array[pos];
    } else {

        throw IException("Position: out of range, %d", pos);
    }
    return val;
  }

  void   setNth(size_t pos, T value)
  {
    if(pos>=0 && pos < size) {
      //2015/05/12
      if (array == NULL) {
        array = new T[size];
      }
      array[pos] = value;
    } else if (pos >= size) {
      //2014/12/09
      resize( pos + AUTO_INCREMENT);
      array[pos] = value;    
    } else {
      throw IException("Index:out of range, %d", pos);
    }
  }

  void sort(Sortable::SortDirection direction= ASCENDING)
  {
    if (direction == Sortable::ASCENDING) {
      ::qsort(array, size, sizeof(T), (__compar_fn_t)ArrayT<T>::compare) ;
    } else {
      ::qsort(array, size, sizeof(T), (__compar_fn_t)ArrayT<T>::_compare) ;
    }
  }

  void remove(int index)
  {
    if (index >= 0 && index <size) {
      for (int i = index; i<size-1; i++) {
        array[i] = array[i+1]; 
      }
    }
  }

};

}