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

   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_PropertiesFile.h"
#include "../../juce_core/io/files/juce_FileInputStream.h"
#include "../../juce_core/io/files/juce_FileOutputStream.h"
#include "../../juce_core/io/streams/juce_BufferedInputStream.h"
#include "../../juce_core/io/streams/juce_SubregionStream.h"
#include "../../juce_core/io/streams/juce_GZIPDecompressorInputStream.h"
#include "../../juce_core/io/streams/juce_GZIPCompressorOutputStream.h"
#include "../../juce_core/basics/juce_SystemStats.h"


//==============================================================================
static const int propFileMagicNumber            = ((int) littleEndianInt ("PROP"));
static const int propFileMagicNumberCompressed  = ((int) littleEndianInt ("CPRP"));


//==============================================================================
PropertiesFile::PropertiesFile (const File& f,
                                const int millisecondsBeforeSaving,
                                const bool ignoreCaseOfKeyNames,
                                const bool useCompression_)
    : PropertySet (ignoreCaseOfKeyNames),
      file (f),
      timerInterval (millisecondsBeforeSaving),
      needsWriting (false),
      useCompression (useCompression_)
{
    InputStream* fileStream = f.createInputStream();

    if (fileStream != 0)
    {
        int magicNumber = fileStream->readInt();

        if (magicNumber == propFileMagicNumberCompressed)
        {
            fileStream = new SubregionStream (fileStream, 4, -1, true);
            fileStream = new GZIPDecompressorInputStream (fileStream, true);

            magicNumber = propFileMagicNumber;
        }

        if (magicNumber == propFileMagicNumber)
        {
            BufferedInputStream in (fileStream, 2048, true);

            int numValues = in.readInt();

            while (--numValues >= 0 && ! in.isExhausted())
            {
                const String key (in.readString());
                const String value (in.readString());

                jassert (key.isNotEmpty());
                if (key.isNotEmpty())
                    getAllProperties().set (key, value);
            }
        }
    }
}

PropertiesFile::~PropertiesFile()
{
    ensureWrittenToFile();
}

bool PropertiesFile::ensureWrittenToFile()
{
    const ScopedLock sl (getLock());

    if (needsWriting)
    {
        needsWriting = false;
        stopTimer();

        const File tempFile (file.getNonexistentSibling (false));

        OutputStream* out = tempFile.createOutputStream();

        if (out != 0)
        {
            if (useCompression)
            {
                out->writeInt (propFileMagicNumberCompressed);
                out->flush();

                out = new GZIPCompressorOutputStream (out, 9, true);
            }
            else
            {
                out->writeInt (propFileMagicNumber);
            }

            out->writeInt (getAllProperties().size());

            for (int i = 0; i < getAllProperties().size(); ++i)
            {
                out->writeString (getAllProperties().getAllKeys() [i]);
                out->writeString (getAllProperties().getAllValues() [i]);
            }

            out->flush();
            delete out;

            if (tempFile.moveFileTo (file))
                return true;

            tempFile.deleteFile();
        }

        return false;
    }

    return true;
}

void PropertiesFile::timerCallback()
{
    ensureWrittenToFile();
}

void PropertiesFile::propertyChanged()
{
    sendChangeMessage (this);

    needsWriting = true;

    if (timerInterval > 0)
        startTimer (timerInterval);
    else if (timerInterval == 0)
        ensureWrittenToFile();
}

const File PropertiesFile::getFile() const throw()
{
    return file;
}

//==============================================================================
PropertiesFile* PropertiesFile::createDefaultAppPropertiesFile (const String& applicationName,
                                                                const String& fileNameSuffix,
                                                                const String& folderName,
                                                                const int millisecondsBeforeSaving,
                                                                const bool ignoreCaseOfKeyNames,
                                                                const bool useCompression)
{
    // mustn't have illegal characters in this name..
    jassert (applicationName == File::createLegalFileName (applicationName));

#ifdef JUCE_MAC
    File dir (T("~/Library/Preferences"));

    if (folderName.isNotEmpty())
        dir = dir.getChildFile (folderName);
#endif

#ifdef JUCE_LINUX
    const File dir (T("~/") + (folderName.isNotEmpty() ? folderName
                                                       : (T(".") + applicationName)));
#endif

#ifdef JUCE_WIN32
    if (SystemStats::getUserApplicationDataDirectory().isEmpty())
        return 0;

    File dir (SystemStats::getUserApplicationDataDirectory());
    dir = dir.getChildFile (folderName.isNotEmpty() ? folderName
                                                    : applicationName);

#endif

    if (! dir.createDirectory())
        return 0;

    return new PropertiesFile (dir.getChildFile (applicationName)
                                  .withFileExtension (fileNameSuffix),
                               millisecondsBeforeSaving,
                               ignoreCaseOfKeyNames,
                               useCompression);
}

END_JUCE_NAMESPACE
