#include "types.h"
#include "encoding.h"
#include "string.h"
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <stdio.h>

#ifndef DEBUG
#include "stringdata.inl"
#endif

#define DATA ((WStringData *)data)
#define ADATA ((AStringData *)adata)

// String class

static u32 alen( const char *a, const char *top = 0 )
{
    u32 res = 0;
    if ( a )
    {
        while ( *a && (!top || a < top) )
        {
            res++;
            a++;
        }
    }
    return res;
}

static u32 wlen( const wchar *w )
{
    u32 res = 0;
    if ( w )
    {
        while ( *w )
        {
            res++;
            w++;
        }
    }
    return res;
}

// returns true if string is empty
bool String::empty() const
{
    return !data || DATA->len == 0;
}

// returns string length in characters
u32 String::length() const
{
    return !data ? 0 : DATA->len;
}

String::String() : data(0), adata(0)
{
}

String::String( const char *o, const char *end ) : data(0), adata(0)
{
    if ( !end )
    {
        *this = o;
    }
    else
    {
        // extra handling!
        if ( o && *o )
        {
            // set from utf-8 string!
            u32 wlen = 0;
            const char *old = o, *top = end;

            // pass 1: get final length
            while ( o < top )
            {
                u32 size;
                unichar ch = charFromUTF8( o, size, top );
                if ( ch == -1 )
                {
                    size = 1;	// skip invalid char
                }
                wlen++;
                o += size;
            }
            data = WStringData::create( wlen );
            o = old;
            // pass 2: extract
            wlen = 0;
            while ( o < top )
            {
                u32 size;
                unichar ch = charFromUTF8( o, size, top );
                if ( ch == -1 )
                {
                    size = 1;		// skip invalid char
                    ch = 0xfffd;	// unicode_?
                }
                DATA->data[ wlen++ ] = (wchar)ch;
                o += size;
            }
        }
    }
}

String::String( const wchar *o, const wchar *end ) : data(0), adata(0)
{
    if ( !end )
    {
        *this = o;
    }
    else
    {
        // extra handling!
        if ( o && *o )
        {
            u32 len = end - o;
            data = WStringData::create( len );
            memcpy( DATA->data, o, len * sizeof(wchar) );
        }
    }
}

String::String( const String &o ) : data(0), adata(0)
{
    *this = o;
}

void String::changed() const
{
    if ( adata )
    {
        ADATA->release();
        adata = 0;
    }
}

void String::clear()
{
    if ( data )
    {
        DATA->release();
        data = 0;
    }
    if ( adata )
    {
        ADATA->release();
        adata = 0;
    }
}

// replace a given character
// out: number of characters replaced
u32 String::replaceChar( i32 what, i32 with )
{
    if ( !data )
        return 0;
    u32 res = 0;
    unique( true );
    wchar *w = DATA->data;
    u32 n = DATA->len;
    while ( n-- )
    {
        if ( *w == what )
        {
            *w = with;
            res++;
        }
        w++;
    }
    return res;
}

String &String::operator =( const char *o )
{
    clear();
    if ( o && *o )
    {
        // set from utf-8 string!
        u32 len = alen(o);
        u32 wlen = 0;
        const char *old = o, *top = o + len;

        // pass 1: get final length
        while ( o < top )
        {
            u32 size;
            unichar ch = charFromUTF8( o, size, top );
            if ( ch == -1 )
            {
                size = 1;	// skip invalid char
            }
            wlen++;
            o += size;
        }
        data = WStringData::create( wlen );
        o = old;
        // pass 2: extract
        wlen = 0;
        while ( o < top )
        {
            u32 size;
            unichar ch = charFromUTF8( o, size, top );
            if ( ch == -1 )
            {
                size = 1;		// skip invalid char
                ch = 0xfffd;	// unicode_?
            }
            DATA->data[ wlen++ ] = (wchar)ch;
            o += size;
        }
    }
    return *this;
}

String &String::operator =( const wchar *o )
{
    clear();
    if ( o && *o )
    {
        u32 len = wlen( o );
        data = WStringData::create( len );
        memcpy( DATA->data, o, len * sizeof(wchar) );
    }
    return *this;
}

String &String::operator =( const String &o )
{
    if ( data != o.data )
    {
        clear();
        data = o.data;
        adata = o.adata;
        if ( data )
            DATA->addref();
        if ( adata )
            ADATA->addref();
    }
    return *this;
}

String operator +( const char *s, const String &o )
{
    String res(s);
    res += o;
    return res;
}

String operator +( const wchar *s, const String &o )
{
    String res(s);
    res += o;
    return res;
}

String operator +( const String &s, const char *o )
{
    String res(s);
    res += o;
    return res;
}

String operator +( const String &s, const wchar *o )
{
    String res(s);
    res += o;
    return res;
}

String operator +( const String &s, const String &o )
{
    String res(s);
    res += o;
    return res;
}

// string concatenation
String &String::operator +=( const char *o )
{
    if ( empty() )
        return *this = o;
    if ( o && *o )
    {
        // TODO: faster?
        String tmp = o;
        return *this += tmp;
    }
    return *this;
}

String &String::operator +=( const wchar *o )
{
    if ( empty() )
        return *this = o;
    if ( o && *o )
    {
        // TODO: faster
        const wchar *t = o;
        u32 osz = 0;
        while ( *t )
        {
            osz++;
            t++;
        }
        u32 nlen;
        StringData<wchar> *ndata = DATA->reserve( nlen = DATA->len + osz );

        memcpy( ndata->data + DATA->len, o, (t - o + 1)*sizeof(wchar) );
        if ( ndata != data )
        {
            DATA->release();
            data = ndata;
        }
        ndata->len = nlen;
        changed();
        return *this;
    }
    return *this;
}

String &String::operator +=( const String &o )
{
    if ( empty() )
        return *this = o;
    if ( !o.empty() )
    {
        u32 nlen;
        StringData<wchar> *ndata = DATA->reserve( nlen = DATA->len + o.length() );
        memcpy( ndata->data + DATA->len, ((StringData<wchar> *)o.data)->data, (o.length() + 1)*sizeof(wchar) );
        if ( ndata != data )
        {
            DATA->release();
            data = ndata;
        }
        ndata->len = nlen;
        changed();
        return *this;
    }
    return *this;
}

String::~String()
{
    clear();
}

// make string unique
void String::unique( bool clearAnsi )
{
    if ( data && DATA->refs )
    {
        // make unique
        data = DATA->clone();
        if ( adata && ADATA->refs )
        {
            if ( clearAnsi )
            {
                ADATA->release();
                adata = 0;
            }
            else
            {
                adata = ADATA->clone();
            }
        }
    }
}

String::operator const char *() const
{
    return ansi();
}

String::operator char *()
{
    if ( !data )
        return 0;
    unique();
    return (char *)ansi();
}

String::operator const wchar *() const
{
    return wide();
}

String::operator wchar *()
{
    if ( !data )
        return 0;
    unique();
    return (wchar *)wide();
}

// return ansi version (ut8-encoded)
// returns "" if empty
const char *String::utf8() const
{
    return ansi();
}

const char *String::ansi() const
{
    if ( adata )
    {
        return (const char *)ADATA->data;	// no need to convert
    }
    if ( data && DATA->len )
    {
        // convert to UTF-8
        changed();
        u32 len = DATA->len;
        u32 i;
        u32 bytes = 0;
        for (i=0; i<len; i++)
        {
            u32 tmp = charToUTF8( DATA->data[i], 0 );
            if ( tmp == 0 )
                tmp = 1;
            bytes += tmp;
        }
        adata = AStringData::create( bytes );
        ADATA->enc = (i16)encUTF8;
        u8 *u = (u8 *)ADATA->data;
        for (i=0; i<len; i++)
        {
            u32 tmp = charToUTF8( DATA->data[i], u );
            if ( tmp == 0 )
            {
                *u = '?';
                tmp = 1;
            }
            u += tmp;
        }
        return (const char *)ADATA->data;
    }
    return "";
}

const wchar *String::wide() const
{
    return data ? DATA->data : L"";
}

// string formatting. %s is ansi, ut8-encoded (ALWAYS!)
void String::format( const char *fmt, ... )
{
    char buf[2048];
    va_list ap;
    va_start( ap, fmt );
    vsnprintf( buf, sizeof(buf), fmt, ap );
    buf[ sizeof(buf) - 1 ] = 0;
    va_end( ap );
    *this = buf;
}

String format( const char *fmt, ... )
{
    char buf[2048];
    va_list ap;
    va_start( ap, fmt );
    vsnprintf( buf, sizeof(buf), fmt, ap );
    buf[ sizeof(buf) - 1 ] = 0;
    va_end( ap );
    return String( buf );
}

// relational operators

bool String::operator ==( const String &o ) const
{
    u32 len = length(), olen = o.length();
    if ( len != olen )
        return false;
    return len ? memcmp( DATA->data, ((WStringData *)o.data)->data, len*sizeof(wchar) ) == 0 : true;
}

bool String::operator ==( const char *c ) const
{
    return *this == String(c);
}

bool String::operator ==( const wchar *w ) const
{
    u32 len = length();
    if ( !len )
    {
        return !w || !*w;
    }
    // assume len != 0
    const wchar *cw = (const wchar *)DATA->data;
    while ( len-- && *w )
    {
        if ( *w != *cw )
            return false;
        w++;
        cw++;
    }
    return true;
}

bool String::operator !=( const String &o ) const
{
    return !(*this == o);
}

bool String::operator !=( const wchar *w ) const
{
    return !(*this == w);
}

bool String::operator !=( const char *c ) const
{
    return *this != String(c);
}

bool String::operator <( const String &o ) const
{
    return compare( o ) < 0;
}

bool String::operator <( const wchar *w ) const
{
    return compare( w ) < 0;
}

bool String::operator <=( const String &o ) const
{
    return compare( o ) <= 0;
}

bool String::operator <=( const wchar *w ) const
{
    return compare( w ) <= 0;
}

bool String::operator >( const String &o ) const
{
    return compare( o ) > 0;
}

bool String::operator >( const wchar *w ) const
{
    return compare( w ) > 0;
}

bool String::operator >=( const String &o ) const
{
    return compare( o ) >= 0;
}

bool String::operator >=( const wchar *w ) const
{
    return compare( w ) >= 0;
}

int String::compare( const String &o ) const
{
    return compare( o.data ? ((WStringData *)o.data)->data : 0 );
}

int String::compare( const wchar *w ) const
{
    if ( !data || !DATA->len )
    {
        return ( !w || !*w ) ? 0 : -1;
    }
    if ( !w || !*w )
        return 1;
    const wchar *w0 = DATA->data;
    while ( *w0 )
    {
        if ( *w0 != *w )
        {
            return ( *w0 > *w ) ? 1 : ( *w0 == *w ? 0 : -1 );
        }
        w0++;
        w++;
    }
    return *w ? -1 : 0;
}

int compare( const String &a, const String &b )
{
    return a.compare( b );
}

int compare( const String &a, const wchar *b )
{
    return a.compare( b );
}

int compare( const wchar *a, const String &b )
{
    if ( !b.data || !((WStringData *)b.data)->len )
    {
        return ( !a || !*a ) ? 0 : 1;
    }
    const wchar *w = ((WStringData *)b.data)->data;
    while ( *a )
    {
        if ( *a != *w )
        {
            return ( *a > *w ) ? 1 : ( *a == *w ? 0 : -1 );
        }
        a++;
        w++;
    }
    return 0;
}

String::operator bool() const
{
    return !empty();
}

bool String::operator !() const
{
    return empty();
}

static i32 isSeparator( i32 ch )
{
    if ( ch <= 32 )
        return true;
    if ( ch >= '0' && ch <= '9' )
        return false;
    if ( ch < 'A' )
        return true;
    return false;
}

#ifndef _MSC_VER
static const wchar *wcsstr( const wchar *w0, const wchar *w1 )
{
	while (*w0)
	{
		const wchar *t0 = w0;
		const wchar *t1 = w1;
		while ( *t0 && *t1 )
		{
			if ( *t0 != *t1 )
				break;
			t0++;
			t1++;			

		}
		if ( !*t1 )
			return w0;
		w0++;
	}
	return 0;
}
#endif

// find (forward)
// return: -1 if not found, otherwise position
i32 String::find( const String &sub, i32 start, bool whole ) const
{
    if ( start < 0 || start >= length() || sub.length() > length() )
        return -1;
    const wchar *s1 = wide();
    const wchar *s2 = sub.wide();
    const wchar *res = wcsstr( s1 + start, s2 );
    if ( !res )
        return -1;
    if ( res != 0 && whole )
    {
        i32 sr = res - s1;
        bool bad = false;
        const wchar *src = wide();
        if ( sr != 0 && !isSeparator(src[ sr - 1 ]) )
            bad = true;
        else if ( !isSeparator(src[ sr + sub.length() ]) )
            bad = true;
        if ( bad )
        {
            // FIXME: is_recursion_ok?
            return find( sub, sr + 1, whole );
        }
    }
    return res - s1;
}
