/*
  ==============================================================================

   This file is part of the JUCE library - "Jules' Utility Class Extensions"
   Copyright 2004-6 by Raw Material Software ltd.

  ------------------------------------------------------------------------------

   JUCE can be redistributed and/or modified under the terms of the
   GNU General Public License, as published by the Free Software Foundation;
   either version 2 of the License, or (at your option) any later version.

   JUCE is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with JUCE; if not, visit www.gnu.org/licenses or write to the
   Free Software Foundation, Inc., 59 Temple Place, Suite 330, 
   Boston, MA 02111-1307 USA

  ------------------------------------------------------------------------------

   If you'd like to release a closed-source product which uses JUCE, commercial
   licenses are also available: visit www.rawmaterialsoftware.com/juce for
   more information.

  ==============================================================================
*/

#include "../../../../juce_core/basics/juce_StandardHeader.h"

BEGIN_JUCE_NAMESPACE

#include "juce_DirectoryContentsList.h"
#include "../../graphics/imaging/juce_ImageCache.h"

void* juce_findFileStart (const String& directory, const String& wildCard, String& firstResultFile, bool& isDirectory);
bool juce_findFileNext (void* handle, String& resultFile, bool& isDirectory);
void juce_findFileClose (void* handle);


//==============================================================================
DirectoryContentsList::DirectoryContentsList (const FileFilter* const fileFilter_,
                                              TimeSliceThread& thread_)
   : fileFilter (fileFilter_),
     thread (thread_),
     includeDirectories (false),
     includeFiles (false),
     fileFindHandle (0),
     shouldStop (true)
{
}

DirectoryContentsList::~DirectoryContentsList()
{
    clear();
}

//==============================================================================
const File& DirectoryContentsList::getDirectory() const throw()
{
    return root;
}

void DirectoryContentsList::setDirectory (const File& directory,
                                          const bool includeDirectories_,
                                          const bool includeFiles_)
{
    if (directory != root
         || includeDirectories != includeDirectories_
         || includeFiles != includeFiles_)
    {
        clear();

        root = directory;
        includeDirectories = includeDirectories_;
        includeFiles = includeFiles_;

        refresh();
    }
}

void DirectoryContentsList::clear()
{
    shouldStop = true;
    thread.removeTimeSliceClient (this);

    if (fileFindHandle != 0)
    {
        juce_findFileClose (fileFindHandle);
        fileFindHandle = 0;
    }

    if (files.size() > 0)
    {
        files.clear();
        changed();
    }
}

void DirectoryContentsList::refresh()
{
    clear();

    if (root.isDirectory())
    {
        String fileFound;
        bool fileFoundIsDir;

        String path (root.getFullPathName());
        if (! path.endsWithChar (File::separator))
            path += File::separator;

        fileFindHandle = juce_findFileStart (path, T("*"), fileFound, fileFoundIsDir);

        if (fileFindHandle != 0 && fileFound.isNotEmpty())
        {
            if (addFile (fileFound, fileFoundIsDir))
                changed();
        }

        shouldStop = false;

        thread.addTimeSliceClient (this);
    }
}

//==============================================================================
int DirectoryContentsList::getNumFiles() const
{
    return files.size();
}

bool DirectoryContentsList::getFileInfo (const int index,
                                         FileInfo& result) const
{
    const ScopedLock sl (fileListLock);
    const FileInfo* const info = files [index];

    if (info != 0)
    {
        result = *info;
        return true;
    }

    return false;
}

const File DirectoryContentsList::getFile (const int index) const
{
    const ScopedLock sl (fileListLock);
    const FileInfo* const info = files [index];

    if (info != 0)
        return root.getChildFile (info->filename);

    return File::nonexistent;
}

bool DirectoryContentsList::isStillLoading() const
{
    return fileFindHandle != 0;
}

void DirectoryContentsList::changed()
{
    sendChangeMessage (this);
}

//==============================================================================
bool DirectoryContentsList::useTimeSlice()
{
    const uint32 startTime = Time::getApproximateMillisecondCounter();
    bool hasChanged = false;

    for (int i = 100; --i >= 0;)
    {
        if (! checkNextFile (hasChanged))
        {
            if (hasChanged)
                changed();

            return false;
        }

        if (shouldStop || (Time::getApproximateMillisecondCounter() > startTime + 150))
            break;
    }

    if (hasChanged)
        changed();

    return true;
}

bool DirectoryContentsList::checkNextFile (bool& hasChanged)
{
    if (fileFindHandle != 0)
    {
        String fileFound;
        bool fileFoundIsDir;

        if (juce_findFileNext (fileFindHandle, fileFound, fileFoundIsDir))
        {
            if (addFile (fileFound, fileFoundIsDir))
                hasChanged = true;

            return true;
        }
        else
        {
            juce_findFileClose (fileFindHandle);
            fileFindHandle = 0;
        }
    }

    return false;
}

class DirectoryContentsListInfoSorter  : public ElementComparator <DirectoryContentsList::FileInfo*>
{
public:
    int compareElements (DirectoryContentsList::FileInfo* first, DirectoryContentsList::FileInfo* second)
    {
        /*if (first->isDirectory != second->isDirectory)
            return first->isDirectory ? -1 : 1;*/

        return first->filename.compareIgnoreCase (second->filename);
    }
};

bool DirectoryContentsList::addFile (const String& filename, const bool isDir)
{
    if (filename == T("..") || filename == T("."))
        return false;

    const File file (root.getChildFile (filename));

    if (((isDir && includeDirectories) || ((! isDir) && includeFiles))
         && (fileFilter == 0
              || ((! isDir) && fileFilter->isFileSuitable (file))
              || (isDir && fileFilter->isDirectorySuitable (file))))
    {
        FileInfo* const info = new FileInfo();

        info->filename = filename;
        info->fileSize = file.getSize();
        info->modificationTime = file.getLastModificationTime();
        info->creationTime = file.getCreationTime();
        info->isDirectory = isDir;
        info->isReadOnly = ! file.hasWriteAccess();

        const ScopedLock sl (fileListLock);

        for (int i = files.size(); --i >= 0;)
        {
            if (files.getUnchecked(i)->filename == info->filename)
            {
                delete info;
                return false;
            }
        }

        DirectoryContentsListInfoSorter sorter;
        files.addSorted (sorter, info);
        return true;
    }

    return false;
}

END_JUCE_NAMESPACE
