/** \file itasc/kdl/utilities/utility_io.cpp
 *  \ingroup itasc
 */
/*****************************************************************************
 *  \author 
 *  	Erwin Aertbelien, Div. PMA, Dep. of Mech. Eng., K.U.Leuven
 *
 *  \version 
 *		ORO_Geometry V0.2
 *
 *	\par History
 *		- $log$
 *
 *	\par Release
 *		$Name:  $ 
 * \todo
 *   make IO routines more robust against the differences between DOS/UNIX end-of-line style.
 ****************************************************************************/


#include "utility_io.h"
#include "error.h"

#include <stdlib.h>
#include <ctype.h>
#include <string.h>

namespace KDL {

//
//  _functions are private functions 
//

    void _check_istream(std::istream& is)
    {
        if ((!is.good())&&(is.eof()) )
            {
            throw Error_BasicIO_File();
            }
    }
// Eats until the end of the line
static int _EatUntilEndOfLine( std::istream& is, int* countp=NULL) {
    int ch;
    int count;
    count = 0;
    do {
        ch = is.get();
        count++;
        _check_istream(is);
    } while (ch!='\n');
    if (countp!=NULL) *countp = count;
    return ch;
}

// Eats until the end of the comment
static int _EatUntilEndOfComment( std::istream& is, int* countp=NULL) {
    int ch;
    int count;
    count = 0;
    int prevch;
    ch = 0;
    do {
        prevch = ch;
        ch = is.get();
        count++;
        _check_istream(is);
        if ((prevch=='*')&&(ch=='/')) {
            break;
        }
    } while (true);
    if (countp!=NULL) *countp = count;
    ch = is.get();
    return ch;
}

// Eats space-like characters and comments
// possibly returns the number of space-like characters eaten.
static int _EatSpace( std::istream& is,int* countp=NULL) {
    int ch;
    int count;
    count=-1;
    do { 
        _check_istream(is);

        ch = is.get(); 
        count++;
        if (ch == '#') {
            ch = _EatUntilEndOfLine(is,&count);
        }
        if (ch == '/') {
            ch = is.get();
            if (ch == '/') {
                ch = _EatUntilEndOfLine(is,&count);
            } else  if (ch == '*') {
                ch = _EatUntilEndOfComment(is,&count);
            } else {
                is.putback(ch);
                ch = '/';
            }
        }
    } while ((ch==' ')||(ch=='\n')||(ch=='\t'));
    if (countp!=NULL) *countp =  count;
    return ch;
}



// Eats whites, returns, tabs and the delim character
//  Checks whether delim char. is encountered.
void Eat( std::istream& is, int delim )
{   
    int ch;
    ch=_EatSpace(is);
    if (ch != delim) {
       throw Error_BasicIO_Exp_Delim();
    }
    ch=_EatSpace(is);   
    is.putback(ch);
}

// Eats whites, returns, tabs and the delim character
//  Checks whether delim char. is encountered.
// EatEnd does not eat all space-like char's at the end.
void EatEnd( std::istream& is, int delim )
{   
    int ch;
    ch=_EatSpace(is);
    if (ch != delim) {
       throw Error_BasicIO_Exp_Delim();
    }
}



// For each space in descript, this routine eats whites,tabs, and newlines (at least one)
// There should be no consecutive spaces in the description.
// for each letter in descript, its reads the corresponding letter in the output
// the routine is case insensitive.


// Simple routine, enough for our purposes.
//  works with ASCII chars
inline char Upper(char ch) 
{
    /*if (('a'<=ch)&&(ch<='z'))
        return (ch-'a'+'A');
    else
        return ch;
    */
    return toupper(ch);
}

void Eat(std::istream& is,const char* descript)
{
    // eats whites before word
    char ch;
    char chdescr;
    ch=_EatSpace(is);   
    is.putback(ch);
    const char* p;
    p = descript;
    while ((*p)!=0) {
        chdescr = (char)Upper(*p);
        if (chdescr==' ') {
            int count=0;
            ch=_EatSpace(is,&count);
            is.putback(ch);
            if (count==0) {
                throw Error_BasicIO_Not_A_Space();
            }
        } else {
            ch=(char)is.get();
            if (chdescr!=Upper(ch)) {
               throw Error_BasicIO_Unexpected();
            }
        }
        p++;
    }
    
}



void EatWord(std::istream& is,const char* delim,char* storage,int maxsize)
{
    int ch;
    char* p;
    int size;
    // eat white before word
    ch=_EatSpace(is);
    p = storage;
    size=0;
    int count = 0;
    while ((count==0)&&(strchr(delim,ch)==NULL)) {
        *p = (char) toupper(ch);
        ++p;
        if (size==maxsize) {
           throw Error_BasicIO_ToBig();
        }
        _check_istream(is);
        ++size;
        //ch = is.get();
        ch =_EatSpace(is,&count);
    }
    *p=0;
    is.putback(ch);
}


}
