/*
 *  Copyright (C)  2000-2001 Marc Wandschneider <mw@kiltdown.org>
 *
 *  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.
 *
 *  For more information look at the file COPYRIGHT in this package
 *
 */
#include <ILifetime.h>
#include <basetypes.h>
#include <stdio.h>

#include <istream.h>
#include "filestream.h"

/**
 *=-------------------------------------------------------------------------=
 * FileStream::FileStream
 *=-------------------------------------------------------------------------=
 * create a new object
 *
 * Notes:
 */
FileStream::FileStream()
{
    /**
     * we're alive, so have at least one ref
     */
    m_cref = 1;
    m_file = NULL;
}

/**
 *=-------------------------------------------------------------------------=
 * FileStream::~FileStream
 *=-------------------------------------------------------------------------=
 * destroy the object and delete and resources we're using, such as file
 * handles
 *
 * Notes:
 */
FileStream::~FileStream()
{
    /**
     * we're done -- kill the file descriptor if we've got one
     *
     * UNDONE: marcw, 2.6.2000 ASSERT on refcounts
     */
    if (m_file != NULL)
        fclose(m_file);

    m_file = NULL;
}

/**
 *=-------------------------------------------------------------------------=
 * FileStream::create
 *=-------------------------------------------------------------------------=
 * static creation method -- use this method to instantiate a new object
 * and it'll create all the required resources.
 *
 * Parameters:
 *        char *        - [in]  filename to open
 *        BOOL          - [in]  whether the stream is to be read-only or not
 *        FileStream ** - [out] resulting object created
 *
 * Output:
 *        BOOL          - false if something wrong, errno is set
 *
 * Notes:
 */
BOOL FileStream::create(char *filename,
                        BOOL readOnly,
                        FileStream **ppout)
{
    FileStream *frs = new FileStream();

    if ((*ppout = frs) == NULL) return FALSE;

    frs->m_readOnly = readOnly;

    /**
     * call the init function and return
     */
    if (!frs->init(filename)) {
        delete frs;
        *ppout = NULL;
        return FALSE;
    }
    return TRUE;
}

/**
 *=-------------------------------------------------------------------------=
 * FileStream::init
 *=-------------------------------------------------------------------------=
 * small little initalization routine that opens the file appropriately,
 * depending on whether we're read only or not
 *
 * Parameters:
 *        char *        - [in]  filename to open
 *
 * Output:
 *        BOOL          - false if error, errno set.
 *
 * Notes:
 */
BOOL FileStream::init(const char *filename)
{
    const char *flags;

    /**
     * UNDONE: marcw, 2.6.2000 -- this will not allow appending to existing
     *         files -- we should investigate "r+"
     */
    flags = m_readOnly ? "r" : "w+";
    
    /**
     * try to open the file they've given us
     */
    m_file = fopen(filename, flags);

    return m_file != NULL;
}

/**
 *=-------------------------------------------------------------------------=
 * FileStream::addRef                                            [ILifeTime]
 *=-------------------------------------------------------------------------=
 * implementation of ILifeTime 
 */
int FileStream::addRef(void)
{
    return ++m_cref;
}

/**
 *=-------------------------------------------------------------------------=
 * FileStream::release                                           [ILifeTime]
 *=-------------------------------------------------------------------------=
 * implementation of ILifeTime
 */
int FileStream::release(void)
{
    m_cref--;
    if (m_cref == 0) {
        delete this;
        return 0;
    } else
        return m_cref;
}

/**
 *=-------------------------------------------------------------------------=
 * FileStream::readData                                   [ICharReadStream]
 *=-------------------------------------------------------------------------=
 * reads in the requested amount of data
 *
 * Parameters:
 *        int           - [in]  number of chars to read in
 *        char *        - [in]  where to put said data
 *
 * Output:
 *        int           - number of chars read, or -1 if error, errno set
 *
 * Notes:
 */
int FileStream::readData(int count, char *pdata)
{
    return readData(count, (BYTE *)pdata);
}

/**
 *=-------------------------------------------------------------------------=
 * FileStream::readLine                                    [ICharReadStream]
 *=-------------------------------------------------------------------------=
 * reads in a line of text from the input stream, putting the resulting
 * string in the given buffer.  the newline is retained, and the string is
 * terminated with a '\0' character
 *
 * Parameters:
 *    int                - [in]  max number of chars to write
 *    char *             - [in]  where to put the characters
 *
 * Output:
 *    int                - -1 means an error occurred
 *                          0 means EOF
 *                          1 means all is well
 *
 * Notes:
 *    this routine reads in at most one less than the first parameter
 *    number of characters to ensure that there is room for the \0
 *    character.  This routine is but a thin wrapper for fgets().
 */
int FileStream::readLine(int count, char *pdata)
{
    char *p = fgets(pdata, count, m_file);

    if (p)
        return 1;
    else
        return ferror(m_file) ? -1 : 0;
}

/**
 *=-------------------------------------------------------------------------=
 * FileStream::readChar                                    [ICharReadStream]
 *=-------------------------------------------------------------------------=
 * reads the next character from the input stream.  This routine wraps the
 * Std C routine fgetc
 *
 * Parameters:
 *    char *               - [out] where to put the char we got
 *
 * Output:
 *    int                  - -1 means an error occurred,
 *                            0 means EOF
 *                            1 means all is well
 *
 * Notes:
 */
int FileStream::readChar(char *pc)
{
    int i = fgetc(m_file);

    if (i == 0) {
        return feof(m_file) ? 0 : -1;
    }

    *pc = (char)i;
    return 1;
}


/**
 *=-------------------------------------------------------------------------=
 * FileStream::seek                                       [ICharReadStream]
 *=-------------------------------------------------------------------------=
 * seeks to the given point, from the given starting reference point.  This
 * routine basically wraps the stdio 'fseek' routine.
 *
 * Parameters:
 *    SEEKPOINT            - [in] where to start the seek from.  See the
 *                                SEEKPOINT enumeration in istream.h
 *    int                  - [in] an offset from the seek starting point
 *
 * Output:
 *    BOOL                 - FALSE on failure, TRUE on success
 *
 * Notes:
 */
BOOL FileStream::seek(SEEKPOINT start, int offset)
{
    int whence;
    
    switch (start) {

        case SEEKPOINT_BEGIN:
            whence = SEEK_SET;
            break;
        case SEEKPOINT_CURRENT:
            whence = SEEK_CUR;
            break;
        case SEEKPOINT_END:
            whence = SEEK_END;
            break;
    }

    return fseek(m_file, offset, whence) == 0;
}
        

/**
 *=-------------------------------------------------------------------------=
 * FileStream::readData                                   [IByteReadStream]
 *=-------------------------------------------------------------------------=
 * reads in the requested amount of data
 *
 * Parameters:
 *        int           - [in]  number of BYTEs to read in
 *        char *        - [in]  where to put said data
 *
 * Output:
 *        int           - number of BYTEs read, or -1 if error, errno set
 *
 * Notes:
 */
int FileStream::readData(int count, BYTE *pdata)
{
    int c;

    if (m_file == NULL) {
         // UNDONE:  ASSERT & set errno
        return -1;
    }

    /**
     * param checking
     */
    if (count < 0 || pdata == NULL)
        return -1;

    c = fread(pdata, sizeof(BYTE), count, m_file);

    /**
     * if we didn't read any chars, then return 0 if there truly weren't any
     * to read, or -1 if it was an error
     */
    if (c == 0)
        return feof(m_file) ? 0 : -1;

    return c;
}


/**
 *=-------------------------------------------------------------------------=
 * FileStream::writeData                                 [ICharWriteStream]
 *=-------------------------------------------------------------------------=
 * writes out the given chars
 *
 * Parameters:
 *        int           - [in]  number of chars to write out
 *        char *        - [in]  buffer holding chars to write out
 *
 * Output:
 *        int           - number of chars  written, or -1 for error,
 *                        errno is set
 *
 * Notes:
 */
int FileStream::writeData(int count, char *pdata)
{
    return writeData(count, (BYTE *)pdata);
}

/**
 *=-------------------------------------------------------------------------=
 * FileStream::writeLine                                 [ICharWriteStream]
 *=-------------------------------------------------------------------------=
 * writes out the given null-terminated line of data, adding on a newline
 * character at the end of input.  This routine wraps the fputs routine
 * in the C runtime libraries and adds a '\n'
 *
 * Parameters:
 *        char *        - [in]  null-terminated string to write out
 *
 * Output:
 *        BOOL          - TRUE means success, FALSE means the data couldn't
 *                        be written out
 *
 * Notes:
 */
BOOL FileStream::writeLine(char *pline)
{
    int result = fputs(pline, m_file);
    if (result == EOF) return FALSE;

    return fputc('\n', m_file) == '\n';
}


/**
 *=-------------------------------------------------------------------------=
 * FileStream::writeData                                 [IByteWriteStream]
 *=-------------------------------------------------------------------------=
 * writes out the given BYTES
 *
 * Parameters:
 *        int           - [in]  number of BYTEs to write out
 *        char *        - [in]  buffer holding BYTEs to write out
 *
 * Output:
 *        int           - number of BYTEs  written, or -1 for error,
 *                        errno is set
 *
 * Notes:
 */                                                  
int FileStream::writeData(int count, BYTE *pdata)
{
    int c;

    if (m_file == NULL) {
        // UNDONE: marcw, 9/19/2000:  ASSERT & set errno
        return -1;
    }

    /**
     * param checking
     */
    if (count < 0 || pdata == NULL)
        return -1;

    c = fwrite(pdata, sizeof(BYTE), count, m_file);

    /**
     * errors, errno is set
     */
    if (c < count)
        return -1;

    return c;
}


syntax highlighted by Code2HTML, v. 0.9.1