// This may look like C code, but it is really -*- C++ -*-
 
//<copyright>
// 
// Copyright (c) 1995-1996
// Institute for Information Processing and Computer Supported New Media (IICM),
// Graz University of Technology, Austria.
// 
//</copyright>
 
//<file>
//
// Name:       nstr.C
//
// Purpose:    NString Class (RString Extension for Number Conversion)
//
// Created:    11 Oct 95    Georg Essl
//
// Modified:   27 Sep 96    Georg Essl
//
//
//
// Description:
//
// If a numbers are passed with in RStrings this numbers need to be converted
// into int, long, float etc. The NString class adds new method to the RString
// class to cope with this need.
//
//</file>

// ***** INCLUDES *****

#include "nstr.h"
#include <stdlib.h>
#include <stdio.h>
#include "assert.h"
#include <math.h>
#include <string.h>

Fieldsimplement (NSCharFieldBase, char) ;

static int nstring_debug = 0;

#ifndef trunc
double trunc(double x)
{
    if( x>=0.0)
        return floor(x);
    else
        return ceil(x);
}
#endif

// Class NSCharField

// ***** PUBLIC METHODS *****

NSCharField& NSCharField :: write_format (char ch, const RString& format)
{
    convtype_ = CHAR;
    argchar_ = ch;
    
    parseFormat(format);
    return *this;
}

NSCharField& NSCharField :: write_format (const RString& str, const RString& format)
{
    convtype_ = STRING;
    argstring_ = str;
    parseFormat(format);
    return *this;
}

NSCharField& NSCharField :: write (int i, const RString& format) 
{
    convtype_ = INT;
    arglong_ = i;
    parseFormat(format);
    return *this ;
}

NSCharField& NSCharField :: write (long l, const RString& format)
{
    
    convtype_ = INT;
    arglong_ = l;
    parseFormat(format);
    return *this ;
}

NSCharField& NSCharField :: write (unsigned int i, const RString& format) 
{
    convtype_ = INT;
    arglong_ = i;
    parseFormat(format);
    return *this ;
}

NSCharField& NSCharField :: write (unsigned long l, const RString& format)
{
    convtype_ = INT;
    arglong_ = l;
    parseFormat(format);
    return *this ;
}

NSCharField& NSCharField :: write (float f, const RString& format) 
{
    convtype_ = FLOAT;
    argdouble_ = f;
    parseFormat(format);
    return *this ;
}

NSCharField& NSCharField :: write (double d, const RString& format)
{
    convtype_ = FLOAT;
    argdouble_ = d;
    parseFormat(format);
    return *this ;
}

// ***** PROTECTED METHODS *****

/* References: man 3 printf
 */
enum ConvFormat { CF_ERROR, CF_PRESTRING, CF_FORCEBEHAVIOR, CF_WIDTH, CF_PRECISION, CF_INTSIZE, CF_CONVERSIONTYPE, CF_EOT };

void NSCharField::parseFormat(const RString& format)
{
    
    ConvFormat state = CF_PRESTRING;
    long formatidx = 0;
    long precisionidx = 0;
    long widthidx = 0;
    
    char curchar=0;
    
    clearFormat();
    
    if(nstring_debug)
        cout << format << endl;
    
    while(state != CF_ERROR && state != CF_EOT)
    {
        if(nstring_debug)
            cout << "State: " << (int) state << ":" << format[(int)formatidx] << ":" << (int)formatidx << endl;
        
        if(formatidx >= format.length())
            state = CF_EOT;
        else
            curchar = format[(int)formatidx];
        
        switch(state)
        {
            case CF_PRESTRING:
                if(curchar == '%')
                {
                    formatidx ++;
                    state = CF_FORCEBEHAVIOR;
                    
                }
                else
                {
                    write( format.gSubstrIndex(formatidx,formatidx) );
                    formatidx ++;
                }
                
            break;
            case CF_FORCEBEHAVIOR:
                switch(curchar)
                {
                    case '%':
                        
                        write( format.gSubstrIndex(formatidx,formatidx) );
                                 
                        formatidx++;
                        state = CF_PRESTRING;
                    break;
                    case '-':
                        if(nstring_debug)
                            cout << "GULP" << endl;
                        forceleftalign_ = true;
                        formatidx ++;
                    break;
                    case '+':
                        forceshowsign_ = true;
                        formatidx ++;
                    break;
                    case '#':
                        forceprefixchars_ = true;
                        formatidx ++;
                    break;
                    case '0':
                        forceleadingzeros_ = true;
                        formatidx ++;
                    break;
                    case ' ':
                        forcespacepad_ = true;
                        formatidx ++;
                    break;
                    case '.':
                        formatidx ++;
                        precisionidx = formatidx;
                        state = CF_PRECISION;
                    break;
                    default:
                        if(curchar >= '1' && curchar <='9')
                        {
                            widthidx = formatidx;
                            formatidx ++;
                            state = CF_WIDTH;
                        }
                        else
                            state = CF_INTSIZE;
                }
            break;
            case CF_WIDTH:
                if(curchar >='0' && curchar <='9')
                {
                    formatidx ++;
                }
                else
                {
                    NString width = format.gSubstrIndex(widthidx, formatidx - 1);
                    
                    // MISS: convert to int. width_ = ...
                    width_ = width.toInt(10);
                    
                    if(curchar == '.')
                    {
                        formatidx ++;
                        precisionidx = formatidx;
                        state = CF_PRECISION;
                    }
                    else
                        state = CF_INTSIZE;
                }
                
            break;
            case CF_PRECISION:
                if(curchar >='0' && curchar <='9')
                {
                    formatidx ++;
                }
                else
                {
                    if(formatidx > precisionidx)
                    {
                        NString precision = format.gSubstrIndex(precisionidx, formatidx - 1);
                    
                        // MISS: convert to int. width_ = ...
                        precision_ = precision.toInt(10);
                    
                        state = CF_INTSIZE;
                    }
                }
            break;
            case CF_INTSIZE:
                switch(curchar)
                {
                    case 'h': 
                        ignoreshort_ = true;
                        formatidx ++;
                    break;
                    case 'l': 
                        ignorelong_ = true;
                        formatidx ++;
                    break;
                    default:
                        state = CF_CONVERSIONTYPE;
                }
            break;
            case CF_CONVERSIONTYPE:
                switch(curchar)
                {
                    case 'd': 
                    case 'i':
                        intformat_ = SIGNEDDECINT;
                    break;
                    case 'u':
                        intformat_ = UNSIGNEDDECINT;
                    break;
                    case 'o':
                        intformat_ = OCTALINT;
                    break;
                    case 'X':
                        capitals_ = true;
                    case 'x':
                        intformat_ = HEXINT;
                    break;
                    case 'f':
                        floatformat_ = PLAINFLOAT;
                    break;
                    case 'E':
                        capitals_ = true;
                    case 'e':
                        floatformat_ = EXPONENTFLOAT;
                    break;
                    case 'G':
                        capitals_ = true;
                    case 'g':
                        floatformat_ = PLAINFLOAT;
                        adaptivefloat_ = true;
                    break;
                    case 'c':
                        ignorechar_ = true;
                    break;
                    case 's':
                        ignorestring_ = true;
                    break;
                    default:
                    break;
                }
                
                convertValue();
                
                clearFormat();
                
                
                formatidx++;
                state = CF_PRESTRING;
            break;
            default:
            break;
        }
       
    }
}

void NSCharField::clearFormat()
{
                forceleftalign_ = false;
                forceshowsign_ = false;
                forceprefixchars_ = false;
                forceleadingzeros_ = false;
                forcespacepad_ = false;
                width_ = 0;
                if(convtype_ == FLOAT)
                    precision_ = 6;
                else
                    precision_ = 0;
                intformat_ = SIGNEDDECINT;
                floatformat_ = PLAINFLOAT;
                capitals_ = false;
                adaptivefloat_ = false;
}


void NSCharField::convertValue()
{
    
    char padchar = ' ';
    
    if(forceleadingzeros_)
        padchar = '0';
    
    switch(convtype_)
    {
        case INT:
            switch(intformat_)
            {
                case OCTALINT:
                    long2String(arglong_, 8);
                break;
                case HEXINT:
                    long2String(arglong_,16);
                break;
                default:
                    long2String(arglong_,10);
                break;
            }
        break;
        case FLOAT:
            float2String(argdouble_);
        break;
        case CHAR:
            write( argchar_ );
            padChar(width_-1, count()-1);
        break;
        case STRING:
        {
            long pos = count();
            int printlen = precision_ > argstring_.length() ? argstring_.length() : precision_;
            int padwidth = width_ - printlen;
            
            if(padwidth < 0) padwidth = 0;
            
            write( argstring_.gSubstrIndex(0,printlen-1) );
            
            if(nstring_debug)
                cout << "ARG: " << argstring_.gSubstrIndex(0,printlen-1) << endl;
            
            padChar(padwidth, pos);
                
        }   
        break;
        default:
            hgassert(intformat_, "NSCharField::convertValue() unknown Type!! (SHOULD NEVER HAPPEN! SHIT!)");
            return; // Type not supported. Better HGASSERT here!!
        break;
    }
}

void NSCharField::float2String(double d)
{
    long pos = count();
    double absd = fabs(d);
    long precision = precision_;
    
    long len;
    
    if(d != 0)
    {
        len = (long)log10(absd);
    }
    else
        len = 0;
    
    if(adaptivefloat_ && (len < -4 || len > precision_))
    {
        floatformat_ = EXPONENTFLOAT;
    }
    
    if(floatformat_ == EXPONENTFLOAT)
    {
        d = d / pow((double) 10, (double)len);
    }
    
    double predot = trunc(d);
    double postdot = d - predot; // Exact??
    
    double i;
    double i2;
     
    i = predot;
    
    if(nstring_debug)
        cout << "PREDOT: " << i << endl;
    
    i2 = i < 0.0 ? -i : i;
    
    digits2String(i2, 1, 10);
    
    i = (postdot * pow((double) 10, (double)precision));
    
    if(nstring_debug)
        cout << "POSTDOT: " << i << endl;
    
    i2 = i < 0 ? -i : i;
    
    if(precision > 0)
    {
        write ( '.' );
        digits2String(i2, precision, 10);
    }
    
    if(floatformat_ == EXPONENTFLOAT)
    {
        if(capitals_)
            write('E');
        else
            write('e');
                
        if(len >= 0)
            write('+');
        else
            write('-');
                
        digits2String((unsigned long)(len<0 ? -len:len), 2, 10);
    }
    
    if(!forceleadingzeros_ || forceleftalign_)
    {
        if(d < 0)
        {
            insert(pos, '-');
        }
        else if(forceshowsign_)
        {
            insert(pos,'+');
        }
        else if(forcespacepad_ && forceleadingzeros_ && !forceleftalign_)
        {
            insert(pos,'0');
        }
        else if(forcespacepad_)
        {
            insert(pos,' ');
        }
    }
    
    int printlen = count();
    int padwidth = width_ - printlen;
    
    if(padwidth < 0) padwidth = 0;
    
    if(nstring_debug)
        cout << "ARG: " << *this << endl;
            
    padChar(padwidth, pos);
    
    
    if(!(!forceleadingzeros_ || forceleftalign_))
    {
        if(d < 0)
        {
            insert(pos, '-');
        }
        else if(forceshowsign_)
        {
            insert(pos, '+');
        }
        else if(forcespacepad_ && forceleadingzeros_ && !forceleftalign_)
        {
            insert(pos, '0');
        }
        else if(forcespacepad_)
        {
            insert(pos, ' ');
        }
    }
}

void NSCharField::digits2String(unsigned long i2, long precision, int base)
{
    int remainder;
    char digit;
    long pos = count();
    
    do
    {
        remainder = i2 % base;
        if(remainder > 9)
        {
            if(capitals_)
                digit = 'A' + remainder - 10;
            else
                digit = 'a' + remainder - 10;
        }
        else
        {
            digit = '0' + remainder;
        }
        insert(pos, digit );
        i2 = i2/base;
        precision --;
    }
    while(i2 != 0 || precision > 0);

}

void NSCharField::digits2String(double i2, long precision, int base)
{
    int remainder;
    char digit;
    long pos = count();
    
    do
    {
        remainder = (int)fmod (i2,(double)  base);
        if(remainder > 9)
        {
            if(capitals_)
                digit = 'A' + remainder - 10;
            else
                digit = 'a' + remainder - 10;
        }
        else
        {
            digit = '0' + remainder;
        }
        insert(pos, digit );
        i2 = i2/base;
        precision --;
    }
    while(trunc(i2) != 0.0 || precision > 0);
}

void NSCharField::long2String(long i, int base)
{
    long pos = count();
    long precision = precision_;
    
    unsigned long i2;
    
    if(intformat_ != SIGNEDDECINT)
    {
        i2 = i;
    }
    else
    {
        i2 = i < 0 ? -i : i;
    }
    
    if(forceprefixchars_)
    {
        if(intformat_ == HEXINT)
        {
            write('0');
            write(capitals_ ? 'X' : 'x');
        }
        if(intformat_ == OCTALINT)
        {
            write('0');
        }
    }
    
    digits2String(i2, precision, base);
        
    if(intformat_ == SIGNEDDECINT)
    {
        if(i < 0)
        {
            insert(pos, '-' );
        }
        else if(forceshowsign_)
        {
            insert(pos, '+' );
        }
        else if(forcespacepad_ && forceleadingzeros_ && !forceleftalign_)
        {
            insert(pos, '0' );
        }
        else if(forcespacepad_)
        {
            insert(pos, ' ' );
        }
    }
    
    if(nstring_debug)
        cout << "ARG: " << *this << endl;
    
    int printlen = count();
    int padwidth = width_ - printlen;
    
    if(padwidth < 0) padwidth = 0;
    
    padChar(padwidth, pos);
    
}



void NSCharField::padChar(int padwidth, long pos)
{
    char padch = ' ';
    
    if(convtype_!= STRING && convtype_!=CHAR && forceleadingzeros_ && !forceleftalign_)
    {
        padch = '0';
    }
    
    if(nstring_debug)
        cout << "PAD: " << padch << ":" << padwidth << endl;
    
    if(!forceleftalign_)
        for(int i = 0 ; i < padwidth; i++)
        {
            insert(pos, padch );
        }
    else
        for(int i = 0 ; i < padwidth; i++)
        {
            write( padch );
        }
}


// Class NString

// ***** PUBLIC METHODS ******

int NString::toInt( int base )
{
    return( toLong( base ) ); // WRONG! Change
}

long NString::toLong( int base )
{
    if(base < 0 || base > 36)
        hgassert(base, "NString::toInt(base): Invalid base value specified. Must be 0-36)");
        
    return( strtol( this->string(), (char**)NULL, base));
}

unsigned long NString::toUnsignedLong( int base )
{
    if(base < 0 || base > 36)
        hgassert(base, "NString::toInt(base): Invalid base value specified. Must be 0-36)");
        
    return( strtoul( this->string(), (char**)NULL, base));
}

double NString::toDouble( void )
{
    return( strtod( this->string(), (char**)NULL) ); 
}


NString& NString::loadLong(const long l, const RString& format)
{
    nscharfield_.write(l, format);
    *this = nscharfield_.getRString();
    nscharfield_.free();
    return *this;
}

NString& NString::loadChar(const char ch, const RString& format)
{
    nscharfield_.write_format(ch, format);
    *this = nscharfield_.getRString();
    nscharfield_.free();
    return *this;
}

NString& NString::loadString(const RString& str, const RString& format)
{
    nscharfield_.write_format(str, format);
    *this = nscharfield_.getRString();
    nscharfield_.free();
    return *this;
}

NString& NString::loadDouble(const double d, const RString& format)
{
    nscharfield_.write(d, format);
    *this = nscharfield_.getRString();
    nscharfield_.free();
    return *this;
}

NString& NString::appendLong(const long l, const RString& format)
{
    nscharfield_.write(l, format);
    *this += nscharfield_.getRString();
    nscharfield_.free();
    return *this;
}

NString& NString::appendChar(const char ch, const RString& format)
{
    nscharfield_.write_format(ch, format);
    *this += nscharfield_.getRString();
    nscharfield_.free();
    return *this;
}

NString& NString::appendString(const RString& str, const RString& format)
{
    nscharfield_.write_format(str, format);
    *this += nscharfield_.getRString();
    nscharfield_.free();
    return *this;
}

NString& NString::appendDouble(const double d, const RString& format)
{
    nscharfield_.write(d, format);
    *this += nscharfield_.getRString();
    nscharfield_.free();
    return *this;
}

NSCharField NString::nscharfield_;

