#include "HeadSpin.h"
#include "pfTextFile.h"
#include <stdio.h>
#include <ctype.h>

//-----------------------------------------------------------------------------

namespace dev
{


class TextFile::TextFileImpl
{
public:
    TextFile::ErrorType     err;
    int                     line;

    explicit TextFileImpl( const char* filename )
    {
        err             = TextFile::ERROR_NONE;
        line            = 1;
        m_peeked        = false;
        m_peekedChar    = 0;
        m_file          = fopen( filename, "rt" );

        if ( !m_file )
            err = TextFile::ERROR_OPEN;
    }

    ~TextFileImpl()
    {
        if ( m_file )
        {
            fclose( m_file );
            m_file = 0;
        }
    }

    bool eof() const
    {
        if ( err )
            return true;
        return 0 != feof(m_file);
    }

    bool peekChar( char* ch )
    {
        if ( err )
            return false;

        if ( !m_peeked )
        {
            int c = getc( m_file );
            if ( EOF != c )
            {
                m_peeked = true;
                m_peekedChar = (char)c;
            }
            else
            {
                if ( ferror(m_file) )
                    err = TextFile::ERROR_READ;
            }
        }

        if ( m_peeked )
            *ch = m_peekedChar;
        return m_peeked;
    }

    bool readChar( char* ch )
    {
        if ( err )
            return false;

        bool more = peekChar( ch );
        m_peeked = false;
        if ( more && *ch == '\n' )
            ++line;
        return more;
    }

    bool skipWhitespace()
    {
        if ( err )
            return false;

        char ch;
        while ( peekChar(&ch) )
        {
            if ( !isspace(ch) )
                break;
            readChar( &ch );
        }
        return !eof();
    }

    bool readString( char* buf, int size )
    {
        if ( err )
            return false;

        skipWhitespace();

        int count = 0;
        char ch;
        while ( peekChar(&ch) )
        {
            if ( isspace(ch) )
                break;
            if ( count+1 < size )
                buf[count++] = ch;
            readChar( &ch );
        }
        if ( size > 0 )
            buf[count] = 0;
        return count > 0;
    }

    void skipLine()
    {
        if ( err )
            return;

        char ch;
        while ( readChar(&ch) )
        {
            if ( ch == '\n' )
                break;
        }
    }

    long readHex()
    {
        if ( err )
            return 0;

        skipWhitespace();

        // hex must start with alphanumeric character
        char ch;
        if ( !peekChar(&ch) || !isalnum(ch) )
        {
            err = TextFile::ERROR_PARSE;
            return 0;
        }

        long x = 0;
        while ( peekChar(&ch) )
        {
            switch ( ch )
            {
            case '0':   x <<= 4; x += 0; break;
            case '1':   x <<= 4; x += 1; break;
            case '2':   x <<= 4; x += 2; break;
            case '3':   x <<= 4; x += 3; break;
            case '4':   x <<= 4; x += 4; break;
            case '5':   x <<= 4; x += 5; break;
            case '6':   x <<= 4; x += 6; break;
            case '7':   x <<= 4; x += 7; break;
            case '8':   x <<= 4; x += 8; break;
            case '9':   x <<= 4; x += 9; break;
            case 'a':
            case 'A':   x <<= 4; x += 0xA; break;
            case 'b':
            case 'B':   x <<= 4; x += 0xB; break;
            case 'c':
            case 'C':   x <<= 4; x += 0xC; break;
            case 'd':
            case 'D':   x <<= 4; x += 0xD; break;
            case 'e':
            case 'E':   x <<= 4; x += 0xE; break;
            case 'f':
            case 'F':   x <<= 4; x += 0xF; break;
            default:    return x;
            }
            readChar( &ch );
        }
        return x;
    }

private:
    bool    m_peeked;
    char    m_peekedChar;
    FILE*   m_file;

    TextFileImpl( const TextFileImpl& );
    TextFileImpl& operator=( const TextFileImpl& );
};

//-----------------------------------------------------------------------------

TextFile::TextFile( const char* filename )
{
    m_this = new TextFileImpl( filename );
}

TextFile::~TextFile()
{
    delete m_this;
}

bool TextFile::readString( char* buf, int size )
{
    return m_this->readString( buf, size );
}

void TextFile::skipLine()
{
    m_this->skipLine();
}

long TextFile::readHex()
{
    return m_this->readHex();
}

bool TextFile::skipWhitespace()
{
    return m_this->skipWhitespace();
}

TextFile::ErrorType TextFile::error() const
{
    return m_this->err;
}

bool TextFile::readChar( char* ch )
{
    return m_this->readChar( ch );
}

bool TextFile::peekChar( char* ch )
{
    return m_this->peekChar( ch );
}

bool TextFile::eof() const
{
    return m_this->eof();
}

int TextFile::line() const
{
    return m_this->line;
}


} // dev
/*
 * Copyright (c) 2001 Jani Kajala
 *
 * Permission to use, copy, modify, distribute and sell this
 * software and its documentation for any purpose is hereby
 * granted without fee, provided that the above copyright notice
 * appear in all copies and that both that copyright notice and
 * this permission notice appear in supporting documentation.
 * Jani Kajala makes no representations about the suitability 
 * of this software for any purpose. It is provided "as is" 
 * without express or implied warranty.
 */
