//////////////////////////////////////////////////////////////////////
//
//	Crytek SuperFramework Source code
//	
//	History:
//	-:Created by Vladimir Kajalin
//
//////////////////////////////////////////////////////////////////////

#ifndef LIST_3DENGINE_H
#define LIST_3DENGINE_H

//  dynamic management of objects

#ifndef ASSERT
#define ASSERT assert
#endif

#include "assert.h"

template <class T> class list2
{
  T * elements;
  int count;
  int allocated_count;
public:
  int GetAllocatedMB() { return (allocated_count*sizeof(T))/(1024*1024); }
  list2() { count=0; elements=0; Reset(); }
  int valid;

  list2(list2<T> & new_list)
  { 
    count=0; elements=0; 
    *this = new_list;
  }

  ~list2() { Reset(); }

  void Free()
  {
      Reset();
  }
  
  void Reset() 
  {    
//    if(sizeof(T)<200) // !!!
  //  for(int i=0; i<count; i++)
    //  elements[i].~T();// release depend

    //ASSERT((unsigned int)elements != 0xdddddddd);
    if(elements)// && (unsigned int)elements != 0xdddddddd) 
      free (elements); 
    count=0; 
    elements=0;
    allocated_count= 0; 
  }

  void SetCount(int value)
  {
    count=value;      
  }

  void Clear()
  {
    count=0; 
  }

  int Find(T & p)
  {
    for(int i=0; i<count; i++)
      if(p == (*this)[i])
        return i;

    return -1;;
  }

  void AddCopy(T p)
  {
    if( count >= allocated_count )
    {
      allocated_count = count*3/2 + 2;

      T * new_elements = (T*)calloc(allocated_count,sizeof(T));
      if(!new_elements)
      {
        char buff[256]="";
        sprintf(buff, "list2<>: no memory error %.2f MB", (float)((float)allocated_count*sizeof(T))/(1024*1024));
//        MessageBox(0,buff,"list",MB_OK);
        //text_to_console(buff);
        exit(0);
      }
      memcpy(new_elements, elements, sizeof(T)*count);
      if(elements) 
        free (elements);
      elements = new_elements;
    }

    elements[count] = p;
    count++;
  }

  void Add(T & p)
  {
    if( count >= allocated_count )
    {
      allocated_count = count*3/2 + 2;

      T * new_elements = (T*)calloc(allocated_count,sizeof(T));
      if(!new_elements)
      {
        char buff[256]="";
        sprintf(buff, "list2<>: no memory error %.2f MB", (float)((float)allocated_count*sizeof(T))/(1024*1024));
//        MessageBox(0,buff,"list",MB_OK);
        //text_to_console(buff);
        exit(0);
      }
      memcpy(new_elements, elements, sizeof(T)*count);
      if(elements) 
        free (elements);
      elements = new_elements;
    }

    memcpy(&elements[count],&p,sizeof(elements[count]));
    count++;
  }

  void InsertBefore(T & p, const unsigned int before)
  {
    ASSERT( before>=0 && before<(unsigned int)count );

    T tmp = elements[before];
    Add(tmp); // add empty object to increase memory buffer

    memmove(&(elements[before+1]), &(elements[before]), sizeof(T)*(count-before-1));

    elements[before] = p;
  }

  void PreAllocate(int elem_count)
  {
    if( elem_count > allocated_count )
    {
      allocated_count = elem_count;

      T * new_elements = (T*)calloc(allocated_count,sizeof(T));
      if(!new_elements)
      {
        char buff[256]="";
        sprintf(buff, "list2<>: no memory error %.2f MB", (float)((float)allocated_count*sizeof(T))/(1024*1024));
//        AfxMessageBox(buff);
        //text_to_console(buff);
        exit(0);
      }
      memcpy(new_elements, elements, sizeof(T)*count);
      free (elements);
      elements = new_elements;
    }
  }

  void Delete(int d)
  {
    ASSERT( d>=0 && d<count );
    memcpy(&(elements[d]), &(elements[d+1]), sizeof(T)*(count-d-1));
    count--;
  }

  void Delete(T & del)
  {
    for( int i=0; i<Count(); i++ )
    {
      if(elements[i] == del)
      { 
        Delete(i); 
        i--; 
      }
    }
  }

  inline int Count() { return count; }
  inline unsigned int Size() { return count; }
  
  inline int IsEmpty() { return count==0; }

  inline T & operator [] (int i) { ASSERT( i>=0 && i<count ); return elements[i]; }
  inline T & GetAt (int i) { ASSERT( i>=0 && i<count ); return  elements[i]; }
  inline T * Get   (int i) { ASSERT( i>=0 && i<count ); return &elements[i]; }
  inline T & GetValid(int i) 
  { 
    ASSERT(count);
    while(i<0) i+=count;
    while(i>=count) i-=count;
    return  elements[i];
  }

  inline T & Last() { ASSERT(count); return elements[count-1]; }
  void DeleteLast() { ASSERT(count); count--; }

  T * Clone(int * pcount)
  {
    *pcount = count;

    if(!count)
      return 0;

    T * clone = (T *)calloc(sizeof(T),count);
    if(!clone)
    {
      MessageBox(NULL,"Error","list<>: no memory error",MB_OK|MB_DEFAULT_DESKTOP_ONLY);
      text_to_console("list<>: no memory error");
      exit(0);
    }
    ASSERT(clone);
    memcpy(clone, elements, count*sizeof(T));
    return clone;
  }

  void RemoveRepeats()
  {
    list2<T> new_list;
    for( int p=0; p<Count(); p++ )
    {
      if(new_list.IsEmpty())
        new_list.Add(elements[p]);
      else
      {
        if(new_list.Last() != elements[p])
          new_list.Add(elements[p]);
      }
    }

    if( Count() && new_list.Last() == new_list[0] )
      new_list.DeleteLast();

    if(Count())
      *this=new_list;
  }

  inline operator = (list2<T> & source_list)
  {
    Reset();

    for(int i=0; i<source_list.Count(); i++)
      Add(source_list[i]);
    
    return 0;
  }
  
  bool operator == (list2<T> & l)
  {
    if(Count() != l.Count())
      return 0;

    if(memcmp(elements, l.elements, count*sizeof(T))!=0)
      return 0;

    return 1;
  }

  bool operator != (list2<T> & l)
  {
    return !(*this == l);
  }

  void Save(const char * file_name)
  {
    FILE * f = fopen(file_name, "wb");
    if(f)
    {
      int size = sizeof(T);
      fwrite(&size, 4, 1, f);

      if(count)
        fwrite(elements, sizeof(T), count, f);
      fclose(f);
    }
//    else
  //    MessageBox(0, "Internal error", "template <class T> class list: Save: fopen",MB_OK);
  }

  void Load(const char * file_name)
  {
    Clear();
    FILE * f = fopen(file_name, "rb");
    if(!f)
      return;

    int size = 0;
    fread(&size, 4, 1, f);

    while(!feof(f) && sizeof(T)==size)
    {
      T tmp;
      if(fread(&tmp, 1, sizeof(T), f) == sizeof(T))
        Add(tmp);
    }

    fclose(f);
  }

  static int Cmp_Ptrs1(const void* v1, const void* v2)
  {
    T* p1 = (T*)v1;
    T* p2 = (T*)v2;

    if(p1->distance > p2->distance)
      return 1;
    else if(p1->distance < p2->distance)
      return -1;

    return 0;
  }

  static int Cmp_Ptrs2(const void* v1, const void* v2)
  {
    T* p1 = (T*)v1;
    T* p2 = (T*)v2;

    if(p1->distance > p2->distance)
      return -1;
    else if(p1->distance < p2->distance)
      return 1;

    return 0;
  }

  void SortByDistanceMember(bool front_to_back = true)
  {
    if(front_to_back)
      qsort(&elements[0], count, sizeof(T), Cmp_Ptrs1);
    else
      qsort(&elements[0], count, sizeof(T), Cmp_Ptrs2);
/*
    if(front_to_back)
    {
      bool not_ready = 1;
      for( int j=count; j>0 && not_ready; j-- )
      {
        not_ready=0;
        for( int i=1; i<j; i++ )
        {
          if(elements[i-1].distance > elements[i].distance)
          {
            T tmp = elements[i-1];
            elements[i-1] = elements[i];
            elements[i] = tmp;
            not_ready=1;
          }
        }
      }
    }
    else
    {
      bool not_ready = 1;
      for( int j=count; j>0 && not_ready; j-- )
      {
        not_ready=0;
        for( int i=1; i<j; i++ )
        {
          if(elements[i-1].distance < elements[i].distance)
          {
            T tmp = elements[i-1];
            elements[i-1] = elements[i];
            elements[i] = tmp;
            not_ready=1;
          }
        }
      }
    }*/
  }
};

#endif // LIST_3DENGINE_H
