/* ------------------------------------------------------------------
 * Copyright (C) 1998-2009 PacketVideo
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
 * express or implied.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 * -------------------------------------------------------------------
 */
// -*- c++ -*-
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

//               O S C L_ S T R I N G   C L A S S

//    This is a simple string class without any multithread access
//    protection.

// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =

#ifndef UNIT_TEST_LOCAL_STRING_H
#define UNIT_TEST_LOCAL_STRING_H

// - - Inclusion - - - - - - - - - - - - - - - - - - - - - - - - - - - -

inline int _strlen(const char* s)
{
    for (int i = 0; ; i++)
    {
        if (s[i] == '\0')
            return i;
    }
}

inline char* _strcat(char* dest, const char* src)
{
    char* tmp = dest + _strlen(dest);
    for (uint32 i = 0; *src != '\0'; i++)
    {
        *tmp++ = *src++;
    }
    *tmp = '\0';
    return dest;
}

inline char* _strncpy(char* dest, const char* src, uint32 count)
{
    char* tmp = dest;
    uint32 ii;
    for (ii = 0; ii < count && *src != '\0'; ii++)
    {
        *tmp++ = *src++;
    }
    // pad with null chars upto length count
    for (; ii < count; ii++)
    {
        *tmp++ = '\0';
    }
    return dest;
}

inline int32 _strcmp(const char* str1, const char* str2)
{
    while ((*str1 == *str2) && (*str1 != '\0'))
    {
        str1++;
        str2++;
    }
    return (*str1 - *str2);
}

// **************************************************************

/** @name UnitTest_String is a simple string class
    which is compatible with regular character array
    strings as well as Unicode wchar_t array strings.

    The class uses a copy-on-write to minimize unnecessary
    copying when multiple instances of a string are created
    for reading.  Allocated memory is automatically freed by
    the class destructor when the last string referencing the
    memory is destroyed.  The class HAS NO thread synchronization
    built-in, so it is NOT MT-SAFE.  External locks should be used
    if the class is to be shared across threads.
*/

class UnitTest_String_Srep : public UnitTest_HeapBase
{
    public:

        char *buffer; // holds actual string value
        int32 size; // number of elements;
        int32 refcnt; // reference count;

        UnitTest_String_Srep(uint32 nsz, const char *src)
        {
            refcnt = 1;
            size = nsz;
            buffer = (char*)unit_test_allocator::allocate(size + 1);          /* allocate enough space
                                      * including terminator
                                      */
            _strncpy(buffer, src, size);

            buffer[size] = '\0';

        }

        ~UnitTest_String_Srep()
        {
            unit_test_allocator::deallocate(buffer);
        }

        UnitTest_String_Srep* get_own_copy()
        {
            if (1 == refcnt)
            {
                // already a private copy so return
                return this;
            }


            --refcnt; // decrement reference

            UnitTest_String_Srep *tmp = new UnitTest_String_Srep(size, buffer);
            return tmp;
        }

        void assign(int32 nsz, const char *src)
        {

            if (size != nsz)
            {
                unit_test_allocator::deallocate(buffer);
                size = nsz;
                buffer = (char*)unit_test_allocator::allocate(size + 1);
            }

            _strncpy(buffer, src, size);
            buffer[size] = '\0';

        }

    private:
        UnitTest_String_Srep(const UnitTest_String_Srep&);
        UnitTest_String_Srep& operator=(const UnitTest_String_Srep&);

};

class UnitTest_String
{

    private:
// Not needed anymore!  struct Srep;   // Note this is a forward declaraton only, allocates no memory.
        typedef UnitTest_String_Srep Srep;
        Srep *rep;

    public:

        /// Default constructor -- simply creates an empty string
        UnitTest_String();

        /// Copy constructor from character array
        UnitTest_String(const char *cp);

        /// Copy constructor from character array, but allocates
        /// length according to the length parameter.
        UnitTest_String(const char *src, uint32 length);

        /// Copy constructor from another UnitTest_String
        UnitTest_String(const UnitTest_String& src);

        /// Assignment operator from a character array
        UnitTest_String& operator=(const char *);

        /// Assignment operator from another UnitTest_String
        UnitTest_String& operator=(const UnitTest_String &);


        friend int32 operator== (const UnitTest_String& a, const UnitTest_String& b);
        friend int32 operator!= (const UnitTest_String& a, const UnitTest_String& b);
        friend int32 operator< (const UnitTest_String& a, const UnitTest_String& b);
        friend int32 operator<= (const UnitTest_String& a, const UnitTest_String& b);
        friend int32 operator> (const UnitTest_String& a, const UnitTest_String& b);
        friend int32 operator>= (const UnitTest_String& a, const UnitTest_String& b);


        ~UnitTest_String();

        /// Access functions for the string size
        int32 get_size() const;
        int32 size() const
        {
            return get_size();
        };

        /// Access function for the C-style string
        const char * get_cstr() const;
        const char * c_str() const
        {
            return get_cstr();
        };

        /// Append a c-style string
        UnitTest_String& operator+=(const char* src);

        /// Append another UnitTest_String to this UnitTest_String
        UnitTest_String& operator+=(const UnitTest_String& src);

        /// Append a single character
        UnitTest_String& operator+=(const char c);

        char operator[](int32 index) const;


};

inline const char * UnitTest_String::get_cstr() const
{
    return rep->buffer;
}

inline int32 UnitTest_String::get_size() const
{
    return rep->size;
}

inline int32 operator==(const UnitTest_String& a, const UnitTest_String& b)
{
    return (!_strcmp(a.rep->buffer, b.rep->buffer));
}

inline int32 operator!=(const UnitTest_String& a, const UnitTest_String& b)
{
    return (_strcmp(a.rep->buffer, b.rep->buffer) != 0);
}

inline int32 operator>(const UnitTest_String& a, const UnitTest_String& b)
{
    return (_strcmp(a.rep->buffer, b.rep->buffer) > 0);
}

inline int32 operator>=(const UnitTest_String& a, const UnitTest_String& b)
{
    return (_strcmp(a.rep->buffer, b.rep->buffer) >= 0);
}

inline int32 operator<=(const UnitTest_String& a, const UnitTest_String& b)
{
    return (_strcmp(a.rep->buffer, b.rep->buffer) <= 0);
}

inline int32 operator<(const UnitTest_String& a, const UnitTest_String& b)
{
    return (_strcmp(a.rep->buffer, b.rep->buffer) < 0);
}

// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
inline UnitTest_String::UnitTest_String()
{
    char *ptr = NULL;
    rep = new Srep(0, ptr);
}

inline UnitTest_String::UnitTest_String(const char *cp)
{
    if (cp)
        rep = new Srep(_strlen(cp), cp);
    else
        rep = new Srep(0, cp);
}

inline UnitTest_String::UnitTest_String(const char *cp, uint32 length)
{
    rep = new Srep(length, cp);
}

inline UnitTest_String::UnitTest_String(const UnitTest_String& src)
{
    src.rep->refcnt++;
    rep = src.rep;
}

inline UnitTest_String::~UnitTest_String()
{
    if (--rep->refcnt == 0) delete rep;
}

inline UnitTest_String& UnitTest_String::operator=(const UnitTest_String & src)
{

    if (rep == src.rep)
    {
        return *this;  // protect against "str = str"
    }
    src.rep->refcnt++;
    if (--rep->refcnt == 0)
    {
        delete rep;
    }

    rep = src.rep;
    return *this;
}

inline UnitTest_String& UnitTest_String::operator=(const char * cp)
{
    if (--rep->refcnt == 0)
    {
        delete rep;
    }

    if (cp == NULL)
    {
        rep = new Srep(0, cp);
    }
    else
    {
        rep = new Srep(_strlen(cp), cp);
    }
    return *this;
}

inline UnitTest_String& UnitTest_String::operator+=(const char * src)
{
    Srep *new_rep;
    int32 new_size = rep->size + _strlen(src);
    new_rep = new Srep(new_size, rep->buffer);
    _strcat(new_rep->buffer, src);
    if (--rep->refcnt == 0)
    {
        delete rep;
    }
    rep = new_rep;
    return *this;
}

inline UnitTest_String& UnitTest_String::operator+=(const UnitTest_String & src)
{
    Srep *new_rep;
    int32 new_size = rep->size + src.rep->size;
    new_rep = new Srep(new_size, rep->buffer);
    _strcat(new_rep->buffer, src.rep->buffer);
    if (--rep->refcnt == 0)
    {
        delete rep;
    }
    rep = new_rep;
    return *this;
}

inline UnitTest_String& UnitTest_String::operator+=(const char c)
{
    char tmp_str[2];
    tmp_str[0] = c;
    tmp_str[1] = (char)'\0';

    return ((*this) += tmp_str);
}

inline char UnitTest_String::operator[](int32 index) const
{
    if (index < 0 || index >= rep->size)
        return '\0';
    return rep->buffer[index];
}



#endif //UNIT_TEST_LOCAL_STRING_H

