
/*LICENSE_START*/
/*
 *  Copyright (C) 2014  Washington University School of Medicine
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it 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.
 *
 *  This program 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 this program; if not, write to the Free Software Foundation, Inc.,
 *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 */
/*LICENSE_END*/

#define __CARET_TEMPORARY_FILE_DECLARE__
#include "CaretTemporaryFile.h"
#undef __CARET_TEMPORARY_FILE_DECLARE__

#include <QTemporaryFile>

#include "CaretHttpManager.h"
#include "DataFileException.h"

using namespace caret;


    
/**
 * \class caret::CaretTemporaryFile 
 * \brief Reads and writes a temporary file.
 * \ingroup Common
 *
 * Reads and writes a temporary file.  When an instance of this class
 * goes out of scope, the temporary file will be deleted.  This class
 * is able to read a file that resides on an HTTP server (filename 
 * starts with "http://").
 *
 * QTemporaryFile is encapsulated by this class.
 */

/**
 * Constructor.
 */
CaretTemporaryFile::CaretTemporaryFile()
: DataFile()
{
    m_temporaryFile = NULL;
    initializeCaretTemporaryFile();
}

/**
 * Destructor.
 */
CaretTemporaryFile::~CaretTemporaryFile()
{
    delete m_temporaryFile;
}

/**
 * Initialize the temporary file..
 */
void
CaretTemporaryFile::initializeCaretTemporaryFile()
{
    if (m_temporaryFile != NULL) {
        delete m_temporaryFile;
    }
    m_temporaryFile = new QTemporaryFile();
    setFileName(m_temporaryFile->fileName());
}

/**
 * Clear the temporary file.
 * Destroys the encapsulated QTemporaryFile.
 */
void
CaretTemporaryFile::clear()
{
    DataFile::clear();
    initializeCaretTemporaryFile();
}

/**
 * Is the file empty (contains no data)?
 *
 * @return
 *    true if the file is empty, else false.
 */
bool
CaretTemporaryFile::isEmpty() const
{
    const bool fileEmpty = (m_temporaryFile->size() <= 0);
    return fileEmpty;
}

AString
CaretTemporaryFile::getFileName() const
{
    return m_temporaryFile->fileName();
}

AString
CaretTemporaryFile::getFileNameNoPath() const
{
    CaretTemporaryFile* ctf = const_cast<CaretTemporaryFile*>(this);
    ctf->setFileName(m_temporaryFile->fileName());
    return DataFile::getFileNameNoPath();
}

/**
 * This method does nothing as the temporary file's name
 * is generated by QTemporaryFile.
 *
 * @param filename
 *     Name for file.
 */
void
CaretTemporaryFile::setFileName(const AString& filename)
{
    /*
     * Needed for getFileNameNoPath() functionality.
     */
    DataFile::setFileName(filename);
}

/**
 * Read the file at the given path into the temporary file.
 *
 * @param filename
 *    Name of the data file.
 * @throws DataFileException
 *    If the file was not successfully read.
 */
void
CaretTemporaryFile::readFile(const AString& filename)
{
    if (DataFile::isFileOnNetwork(filename)) {
        /*
         * Read file on network.
         * Sort of a kludge, read from the network as a string of bytes
         * and then write the bytes to a temporary file.
         */
        CaretHttpRequest request;
        request.m_method = CaretHttpManager::GET;
        request.m_url = filename;
        CaretHttpResponse response;
        CaretHttpManager::httpRequest(request,
                                      response);
        if (response.m_ok == false) {
            QString msg = ("HTTP error HTTP Response Code="
                           + AString::number(response.m_responseCode));
            throw DataFileException(filename,
                                    msg);
        }
        
        const int64_t numBytes = response.m_body.size();
        if (numBytes > 0) {
            AString tempPath = QDir::tempPath();
            if (!tempPath.endsWith('/')) tempPath += '/';//qt decided to make this behavior OS-dependent, for no apparent reason
            m_temporaryFile->setFileTemplate(tempPath + "qt_temp.XXXXXX." + filename.section('/', -1).section('.', 1));//strip all but the filename, then take all the parts of the extension
            if (m_temporaryFile->open()) {
                const int64_t numBytesWritten = m_temporaryFile->write(&response.m_body[0],
                                                               numBytes);
                if (numBytesWritten != numBytes) {
                    throw DataFileException(filename,
                                            "  Tried to write "
                                            + QString::number(numBytes)
                                            + " bytes to temporary file "
                                            + m_temporaryFile->fileName()
                                            + " but only wrote "
                                            + AString::number(numBytesWritten)
                                            + " bytes.");
                }
                
                m_temporaryFile->close();
            }
            else {
                throw DataFileException(filename,
                                        "Unable to open temporary file for writing its content.");
            }
        }
        else {
            throw DataFileException(filename,
                                    "Failed to read any data from file.");
        }
    }
    else {
        /*
         * Read local file.
         */
        QFile file(filename);
        checkFileReadability(filename);
        if (file.open(QFile::ReadOnly)) {
            QByteArray byteArray = file.readAll();
            file.close();
            
            const int numBytes = byteArray.length();
            if (numBytes > 0) {
                if (m_temporaryFile->open()) {
                    const int64_t numBytesWritten = m_temporaryFile->write(byteArray);
                    if (numBytesWritten != numBytes) {
                        throw DataFileException(filename,
                                                "  Tried to write "
                                                + QString::number(numBytes)
                                                + " bytes to temporary file "
                                                + m_temporaryFile->fileName()
                                                + " but only wrote "
                                                + AString::number(numBytesWritten)
                                                + " bytes.");
                    }
                    
                    m_temporaryFile->close();
                }
                else {
                    throw DataFileException(m_temporaryFile->fileName(),
                                            "Unable to open temporary file for writing its content.");
                }
            }
            else {
                throw DataFileException(filename,
                                        "No data read from file, is it empty?");
            }
        }
        else {
            throw DataFileException(filename,
                                    "Unable to open file for reading its content.");
        }
    }
}

/**
 * Write the contents of the temporary file to a local file with
 * the given name.
 *
 * @param filename
 *    Name of the local data file.
 * @throws DataFileException
 *    If the file was not successfully written.
 */
void
CaretTemporaryFile::writeFile(const AString& filename)
{
    checkFileWritability(filename);
    
    if (isEmpty()) {
        throw DataFileException(filename,
                                "No data (temporary file is empty) to write to file.");
    }

    const QString tempFileName = m_temporaryFile->fileName();
    QFile fileIn(tempFileName);
    if (fileIn.open(QFile::ReadOnly)) {
        QByteArray byteArray = fileIn.readAll();
        fileIn.close();
        
        QFile fileOut(filename);
        if (fileOut.open(QFile::WriteOnly)) {
            fileOut.write(byteArray);
            fileOut.close();
        }
        else {
            fileOut.close();
            throw DataFileException(filename,
                                    "Unable to open file for writing its content.");
        }
    }
    else {
        fileIn.close();
        throw DataFileException(tempFileName,
                                "Unable to open temporary file for reading its content.");
    }
    
}

