#pragma once

#ifdef DEBUG
// make debugging string easier
#include "stringdata.inl"
#endif

class Stream;

// UCS-2 encoded string (unicode) (microcore version)
class String
{
private:

#ifdef DEBUG
    WStringData *data;			// string data (unicode)
    mutable AStringData *adata;	// string data (ansi)
#else
    void *data;					// string data (unicode)
    mutable void *adata;		// string data (ansi)
#endif

    // changed callback
    void changed() const;
public:

    // make string unique
    // clear ansi: if true, ansi cache buffer is released. note that this should be used when passing
    // strings between threads (since String is not thread-safe)
    void unique( bool clearAnsi = false );

    String();
    String( const String &o );
    String( const char *o, const char *end = 0 );
    String( const wchar *o, const wchar *end = 0 );

    String &operator =( const char *o );
    String &operator =( const wchar *o );
    String &operator =( const String &o );

    String &operator +=( const char *o );
    String &operator +=( const wchar *o );
    String &operator +=( const String &o );

    // returns "" if empty
    operator const char *() const;
    // returns 0 if empty
    operator char *();
    // returns L"" if empty
    operator const wchar *() const;
    // returns 0 if empty
    operator wchar *();

    // return ansi version (ut8-encoded)
    // note that as a side effect this also switches current ansi encoding to utf8
    // returns "" if empty
    const char *utf8() const;
    // return ansi version (optionally encode as anything else but utf-8)
    // returns "" if empty
    const char *ansi() const;
    // return wide-char (ucs-2) version
    // returns L"" if empty
    const wchar *wide() const;

    // string concatenation
    friend String operator +( const char *s, const String &o );
    friend String operator +( const wchar *s, const String &o );
    friend String operator +( const String &s, const char *o );
    friend String operator +( const String &s, const wchar *o );
    friend String operator +( const String &s, const String &o );

    // simply clears string
    void clear();

    // replace a given character
    // out: number of characters replaced
    u32 replaceChar( i32 what, i32 with );

    ~String();

    // returns true if string is empty
    bool empty() const;

    // returns string length in characters
    uint32 length() const;

    operator bool() const;
    bool operator !() const;

    // -1 if less, 0 if equal, 1 if greater than
    int compare( const String &o ) const;
    int compare( const wchar *w ) const;

    friend int compare( const String &a, const String &b );
    friend int compare( const String &a, const wchar *b );
    friend int compare( const wchar *a, const String &b );

    // relational operators
    bool operator ==( const String &o ) const;
    bool operator ==( const wchar *w ) const;
    bool operator ==( const char *c ) const;
    bool operator !=( const String &o ) const;
    bool operator !=( const wchar *w ) const;
    bool operator !=( const char *c ) const;
    bool operator <( const String &o ) const;
    bool operator <( const wchar *w ) const;
    bool operator <=( const String &o ) const;
    bool operator <=( const wchar *w ) const;
    bool operator >( const String &o ) const;
    bool operator >( const wchar *w ) const;
    bool operator >=( const String &o ) const;
    bool operator >=( const wchar *w ) const;

    // find (forward)
    // whole: whole words only
    // return: -1 if not found, otherwise position
    i32 find( const String &sub, i32 start = 0, bool whole = false ) const;

    // inplace modification

    // string formatting. %s is ansi, ut8-encoded (ALWAYS!)
////	void format( const String &fmt, ... );	// FIXME: doesn't work, for some reason
    void format( const char *fmt, ... );

    // string formatting. %s is ansi, ut8-encoded (ALWAYS!)
    friend String format( const char *fmt, ...);
};
