// Copyright 2009 The Archiveopteryx Developers <info@aox.org>

#ifndef USTRING_H
#define USTRING_H

#include "global.h"

class EString;


class UStringData
    : public Garbage
{
private:
    UStringData(): str( 0 ), len( 0 ), max( 0 ) {
        setFirstNonPointer( &len );
    }
    UStringData( int );

    friend class UString;
    friend bool operator==( const class UString &, const class UString & );
    friend bool operator==( const UString &, const char * );
    void * operator new( size_t, uint );
    void * operator new( size_t s ) { return Garbage::operator new( s); }

    uint * str;
    uint len;
    uint max;
};


class UString
    : public Garbage
{
public:
    UString();
    UString( const UString & );
    ~UString();

    UString & operator=( const UString & );
    UString & operator+=( const UString & str );

    void operator delete( void * );

    // const, returns zero when used beyond the end
    uint operator[]( uint i ) const {
        if ( !d || i >= d->len )
            return 0;
        return d->str[i];
    }

    bool isEmpty() const { return !d || d->len == 0; }
    uint length() const { return d ? d->len : 0; }

    void append( const UString & );
    void append( const uint );
    void append( const char * );

    void reserve( uint );
    void truncate( uint = 0 );

    bool isAscii() const;
    EString ascii() const;
    EString utf8() const;

    int find( char, int=0 ) const;
    int find( const UString &, int=0 ) const;
    bool contains( const UString & ) const;
    bool contains( const char ) const;
    bool contains( const char * ) const;

    UString mid( uint, uint = UINT_MAX ) const;
    uint number( bool *, uint = 10 ) const;
    UString simplified() const;
    UString trimmed() const;

    inline const uint * data() const { return d ? (const uint*)d->str : 0; }

    UString titlecased() const;

    inline void detach() { if ( !modifiable() ) reserve( length() ); }

    bool modifiable() const { return d && d->max > 0; }

    bool operator<( const UString & ) const;
    bool operator>( const UString & ) const;
    bool operator<=( const UString & ) const;
    bool operator>=( const UString & ) const;

    int compare( const UString & ) const;

    bool startsWith( const EString & ) const;
    bool startsWith( const UString & ) const;
    bool startsWith( const char * ) const;
    bool endsWith( const EString & ) const;
    bool endsWith( const UString & ) const;
    bool endsWith( const char * ) const;

    static bool isDigit( uint );
    static bool isLetter( uint );
    static bool isSpace( uint );

private:
    void reserve2( uint );


private:
    class UStringData * d;
};


inline bool operator==( const UString & s1, const UString & s2 )
{
    if ( s1.length() != s2.length() )
        return false;
    uint i = 0;
    while ( i < s1.length() ) {
        if ( s1[i] != s2[i] )
            return false;
        i++;
    }
    return true;
}


inline bool operator!=( const UString & s1, const UString & s2 )
{
    return !( s1 == s2 );
}


inline bool operator==( const UString & s1, const char * s2 )
{
    if ( !s2 )
        return false;
    uint i = 0;
    while ( i < s1.length() ) {
        if ( s2[i] < 32 || s2[i] >= 127 || s1[i] != s2[i] )
            return false;
        i++;
    }
    if ( s2[i] )
        return false;
    return true;
}


inline bool operator!=( const UString & s1, const char * s2 )
{
    return !( s1 == s2 );
}


extern const UString operator+( const UString & a, const UString & b );
extern const UString operator+( const UString & a, const char * b );
extern const UString operator+( const char * a, const UString & b );
extern const UString operator+=( const UString & a, const UString & b );

inline UString us( const char * s )
{
    UString u;
    u.append( s );
    return u;
}


#endif
