/*
	Trig Wars.
	Copyright (c) 2008, Darragh Coy.
	All rights reserved.
	Original work(s), source code & author(s) details available at: http://www.darraghcoy.com

	Redistribution and use in source and binary forms, with or without modification, are permitted 
	provided that the following conditions are met:

		* 	Redistributions of source code must retain the above copyright notice, this list of 
			conditions and the following disclaimer.
		* 	Redistributions in binary form must reproduce the above copyright notice, this list of 
			conditions and the following disclaimer in the documentation.
		* 	Neither the name of the author nor the names of contributors may be used to endorse or 
			promote products derived from this software without specific prior written permission.
		* 	The use of any third party libraries, modules or data files provided with the source 
			form of the program must follow and respect the individual licensing terms of these 
			components. You do not have permission to modify or circumvent the licensing terms for 
			provided third party components unless the third party licenses specifically allow this. 
			If you do not agree to the terms of provided third party components then you do not have 
			permission to use them in your derived work(s).
		* 	The terms of this license do not apply to separate (but related) distributions if supplied 
			by the author(s), only to the contents of this source distribution, derived works, and the 
			binaries produced by either modified or unmodified versions of the source code. The terms 
			of this license do not also apply to any third party libraries or components provided with 
			the source distribution. Such third party elements are licensed under their own separate 
			agreements.

	THIS SOFTWARE IS PROVIDED BY DARRAGH COY ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, 
	BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
	ARE DISCLAIMED. IN NO EVENT SHALL DARRAGH COY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
	EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 
	OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 
	OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
	IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

//=====================================================================================================================
/// @brief		Serves as a base class for all classes that buffer a 
//				collection of data (file, embedded resource etc.) into 
//				memory and allow that data to be read.
//=====================================================================================================================

#include "Precompiled.h"
#include "Files\BufferedFile.h"

using namespace Game;

//=====================================================================================================================
/// @brief		Default constructor for a buffered file. Initialises all fields.
//=====================================================================================================================

BufferedFile::BufferedFile()
{
    // Initialise all variables:

    m_data			= 0;
    m_pointer		= 0;
    m_end_pointer	= 0;
    m_read_error	= true;
}

//=====================================================================================================================
/// @brief		Destructor for a buffered file. Cleans up resources.
//=====================================================================================================================

BufferedFile::~BufferedFile()
{
    // Close the current file if it is open

    Close();
}

//=====================================================================================================================
/// @brief		Copy constructor for a buffered file
/// @param		other - Other buffered file to copy into this
//=====================================================================================================================

BufferedFile::BufferedFile( const BufferedFile & other )
{
    // See if the other buffered file has an array of data:

	if ( other.m_data != 0 && other.Size() > 0 )
    {
        // Other file has data: copy it into this file

        m_data = new unsigned char[ other.Size() ]; memcpy( m_data , other.m_data , other.Size() );
    }
    else
    {
        // Other file has no data: do no copying

        m_data = 0;
    }

    // Copy all attributes of the other file into this one

    m_pointer		= other.m_pointer;
    m_end_pointer	= other.m_end_pointer;
    m_read_error	= other.m_read_error;
}

//=====================================================================================================================
/// @brief		Overloaded assignment operator for a buffered file
/// @param		other - Other buffered file to copy into this
/// @returns	This object
//=====================================================================================================================

BufferedFile & BufferedFile::operator = ( const BufferedFile & other )
{
    // Make sure the other resource file is not this file:

    if ( &other != this )
    {
        // Close this file first

        Close();

        // See if the other resource file has an array of data:

		if ( other.m_data != 0 && other.Size() > 0 )
        {
            // Other file has data: copy it into this file

            m_data = new unsigned char[ other.Size() ]; memcpy( m_data , other.m_data , other.Size() );
        }
        else
        {
            // Other file has no data: do no copying

            m_data = 0;
        }

        // Copy all attributes of the other file into this one

        m_pointer		= other.m_pointer;
        m_end_pointer	= other.m_end_pointer;
        m_read_error	= other.m_read_error;
    }

    // Return this buffered file

    return * this;
}

//=====================================================================================================================
/// @brief		Closes the current buffered file.
//=====================================================================================================================

void BufferedFile::Close()
{
    // See if there is any file data:

    if ( m_data )
    {
        // Delete the file data:

        delete[] m_data; m_data = 0;
    }

    // Reset all pointers and error codes

    m_pointer		= 0;
    m_end_pointer	= 0;
    m_read_error	= true;
}

//=====================================================================================================================
/// @brief		Tells the size of the buffered file
/// @returns	Buffered file size
//=====================================================================================================================

unsigned BufferedFile::Size() const
{
    // Return the size of file:

    return unsigned(m_end_pointer) - unsigned(m_data);
}

//=====================================================================================================================
/// @brief		Tells the file is open
/// @returns	true if file is open
//=====================================================================================================================

bool BufferedFile::IsOpen() const
{
    return m_data != 0;
}

//=====================================================================================================================
/// @brief		Tells if the current read pointer is at the end of the file
/// @returns	True if the end of the file has been reached
//=====================================================================================================================

bool BufferedFile::IsAtEof() const
{
    return m_pointer >= m_end_pointer;
}

//=====================================================================================================================
/// @brief		Tells if an error occurred while reading the file
/// @returns	True if an error occured, false if not
//=====================================================================================================================

bool BufferedFile::HasError() const
{
    return m_read_error;
}

//=====================================================================================================================
/// @brief		Tells if the file is good for reading
/// @returns	True if everything is ok, false if not
//=====================================================================================================================

bool BufferedFile::IsGood() const
{
    return ( m_pointer < m_end_pointer ) && ( m_read_error == false ) && ( m_data != 0 );
}

//=====================================================================================================================
/// @brief		Tells the current position in the file
/// @returns	The current position in the file
//=====================================================================================================================

unsigned BufferedFile::GetPosition() const
{
    // Return the current offset in the file:

    return ( unsigned(m_end_pointer) - unsigned(m_data) ) - ( unsigned(m_end_pointer) - unsigned(m_pointer) );
}

//=====================================================================================================================
/// @brief		Gives a pointer to the file data at current seek position
/// @returns	Pointer to file data
//=====================================================================================================================

void * BufferedFile::GetDataAhead() const
{
    return m_pointer;
}

//=====================================================================================================================
/// @brief		Moves read position to a given position in the file
/// @param		offset - Position to seek to
/// @param		s - What to seek relative to
//=====================================================================================================================

void BufferedFile::Seek( int offset , SeekMode s )
{
    // See which seek mode has been chosen:

    if ( s == FROM_BEG )
    {
        // Seek relative to the beginning:

        m_pointer = m_data + offset;
    }
    else if ( s == FROM_END )
    {
        // Seek relative to the end:

        m_pointer = m_end_pointer - offset;
    }
    else if ( s == FROM_CUR )
    {
        // Seek relative to the current position in the file:

        m_pointer += offset;
    }

    // Make sure the current read pointer is within the bounds of the file  

    if ( m_pointer < m_data )
    {
        // Pointer before data: clamp

        m_pointer = m_data;

        // Set an error:

        m_read_error = true;
    }

    if ( m_pointer > m_end_pointer )
    {
        // Pointer after end of data: clamp

        m_pointer = m_end_pointer;

        // Set an error:

        m_read_error = true;
    }
}

//=====================================================================================================================
/// @brief		Reads a number of bytes into the given destination array.
/// @param		dest - Destination byte array to store bytes. 
/// @param		bytes - Number of bytes to read.
//=====================================================================================================================

void BufferedFile::Read( void * dest  , unsigned bytes )
{
	// Do nothing if no bytes to read

	if ( bytes == 0 ) return;

    // Make sure we are not reading beyond the end of the file:

    if ( m_pointer + bytes > m_end_pointer )
    {
        // Reading beyond the end of the file: set the error flag

        m_read_error = true;
    }
    else
    {
        // Not reading beyond the end of the file: do a memory copy

        memcpy( dest , m_pointer , bytes );

        // Move on the file pointer:

        m_pointer += bytes;
    }
}

//=====================================================================================================================
/// @brief		Tells if the specified number of bytes can be read without reaching 
///				the end of the file.
/// @param		bytes - number of bytes to make sure exist
/// @returns	True if the specified number of bytes can be read
//=====================================================================================================================

bool BufferedFile::EnsureBytes( unsigned bytes ) const
{
	// If no bytes are given then return true

	if ( bytes == 0 ) return true;

	// Otherwise if the operation will produce an eof

	if ( m_pointer + bytes > m_end_pointer ) return false; else return true;
}
