#include "DirectoryList.hpp"

#include "StringUtils.hpp"
#include <algorithm>
#include <cctype>

DirectoryList::DirectoryList( UnixFile* dir, bool allow_listing) 
: allow_listing( allow_listing),
  readable( true) ,
  compareFunction( &NamePrecedence)
{
  this->dir= dir;
  if (this->dir == NULL)  this->dir= new UnixFile("");  // Zur Sicherheit...
  syncStatistics();  // to set statistics values to 0
}

DirectoryList::~DirectoryList() {
  clear();
  delete dir;
}

UnixFile& DirectoryList::getDirectory() {  return *dir; }


vector<UnixFile*>::iterator  DirectoryList::find( UnixFile* f) {
  vector<UnixFile*>::iterator i= file_list.begin();
  while ( i != file_list.end() && !(f->getName() == (**i).getName()) ) { ++i; }
  return i;
}
    

void DirectoryList::insert( UnixFile* f){   
  if ( f->exists() )  {
    readable= true;
    file_list.push_back( f);
  }
  else {
    if ( f->state == UnixFile::ACCESS_DENIED && isEmpty())  readable= false; 
    delete f;
  }
}

void DirectoryList::insert_unique( UnixFile* f) {
  if ( find( f) == file_list.end() )  insert( f);
  else  delete f;
}

void DirectoryList::clear() {
  vector<UnixFile*>::iterator pos;
  for( pos= file_list.begin(); pos != file_list.end(); ++pos) {
    delete (*pos);
  }
  // Would like to use vector::clear(), but my standard lib doesn't have it
  file_list.erase( file_list.begin(), file_list.end());
  syncStatistics();
}

void DirectoryList::syncStatistics() {
  allFilesCount=0;
  regFilesCount=0;
  regFilesSize= 0;
  
  vector<UnixFile*>::const_iterator pos; 

  for (pos= this->file_list.begin(); pos != this->file_list.end(); ++pos) {
    UnixFile* f= (*pos); 
    if (f != NULL) {  // we should be able to assert this!
      allFilesCount++;
      if (f->isFile()) {
        regFilesCount++;
        regFilesSize += f->getSize();
      }
    }
  }
}

void DirectoryList::list( bool with_hidden_files, bool with_special_dirs) { 
  if (!allow_listing)  return;
  
  UnixDirectoryLister lister( this->getDirectory(), with_hidden_files );
  if ( lister.isOpen() ) {
    while (true) {
      UnixFile* f= lister.getEntry();
      if (f != NULL) {
        if ( !with_special_dirs && f->isSpecialDirectory() ) {
          delete f;  continue;
        }
        
        this->insert( f);
      }
      else break;
    }
  }
  else  readable= false;  // directory could not be opened for reading
  syncStatistics();
}

void DirectoryList::setCompareFunction( CompareFunction_t f) {
  this->compareFunction= f;
}

void DirectoryList::ReOrder() {
  if (compareFunction != NULL)
    sort( file_list.begin(), file_list.end(), compareFunction );
  if (reversed_order)
    reverse( file_list.begin(), file_list.end());
}


//--- non class member functions

bool NamePrecedence(  UnixFile* f1,  UnixFile* f2) {
  if (!f1->isNavigable() &&  f2->isNavigable())  return false;
  if ( f1->isNavigable() && !f2->isNavigable())  return true;
  return icompare( f1->getName(), f2->getName() ) < 0;
}

bool ModDatePrecedence( UnixFile* f1,  UnixFile* f2) {
  return (f1->getLastModified() > f2->getLastModified());
}

bool AccessDatePrecedence( UnixFile* f1,  UnixFile* f2) {
  return (f1->getLastAccessed() > f2->getLastAccessed());
}

bool StatusChangeDatePrecedence( UnixFile* f1,  UnixFile* f2) {
  return (f1->getLastStatusChanged() > f2->getLastStatusChanged());
}

bool FileSizePrecedence( UnixFile* f1,  UnixFile* f2) {
  if (f1->isNavigable())
    if (f2->isNavigable())  return icompare( f1->getName(), f2->getName()) < 0;
    else                    return true;
  else
    if (f2->isNavigable())  return false;
    else                    return (f1->getSize() > f2->getSize());
}

bool ExtensionPrecedence( UnixFile* f1,  UnixFile* f2) {
  if (f1->isNavigable())
    if (f2->isNavigable())  return icompare( f1->getName(), f2->getName()) < 0;
    else                    return true;
  else
    if (f2->isNavigable())  return false;
    else {
      int res= icompare( f1->getNameExtension(), f2->getNameExtension());
      if (res==0)
        return icompare( f1->getName(), f2->getName()) < 0;
      else
        return res<0;
    }
}



/*

APrecedence::APrecedence( APrecedence* next) {
  m_next= next;
}

APrecedence::~APrecedence() {
  delete m_next;
}

DirectoriesFirst::DirectoriesFirst( APrecedence* next)
: APrecedence( next) {}

bool DirectoriesFirst::operator() ( UnixFile* f1, UnixFile* f2) {
  if (f1->isNavigable()) {
    if (f2->isNavigable())  return (*m_next)( f1, f2);
    else                   return true;
  }
  else {
    if (f2->isNavigable())  return false;
    else                   return (*m_next)( f1, f2);
  }
}

NamesAlphabethical::NamesAlphabethical( APrecedence* next)
: APrecedence( next) {}

bool NamesAlphabethical::operator() ( UnixFile* f1, UnixFile* f2) {
  int res= icompare( f1->getNameExtension(), f2->getNameExtension() );
  if (res==0)  return (*m_next)( f1, f2);
  else         return res<0;
}
*/

/*
template<typename Next>
bool DirectoriesFirst<Next>::operator() ( UnixFile* f1, UnixFile* f2) {
  if (f1->isNavigable()) {
    if (f2->isNavigable())  return m_next( f1, f2);
    else                   return true;
  }
  else {
    if (f2->isNavigable())  return false;
    else                   return m_next( f1, f2);
  }
}

template<typename Next>
bool ByName<Next>::operator() ( UnixFile* f1, UnixFile* f2) {
  int res= icompare( f1->getName(), f2->getName() );
  if (res==0)  return (*m_next)( f1, f2);
  else         return res<0;
}


DirectoriesFirst<ByName<Last> > criteria1;
*/
