//
// The developer of the original code and/or files is Tripwire, Inc.
// Portions created by Tripwire, Inc. are copyright (C) 2000 Tripwire,
// Inc. Tripwire is a registered trademark of Tripwire, Inc.  All rights
// reserved.
// 
// This program is free software.  The contents of this file are subject
// to the terms of the GNU General Public License as published by the
// Free Software Foundation; either version 2 of the License, or (at your
// option) any later version.  You may redistribute it and/or modify it
// only in compliance with the GNU General Public License.
// 
// This program is distributed in the hope that it will be useful.
// However, this program is distributed AS-IS WITHOUT ANY
// WARRANTY; INCLUDING THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS
// FOR A PARTICULAR PURPOSE.  Please see the GNU General Public License
// for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
// USA.
// 
// Nothing in the GNU General Public License or any other license to use
// the code or files shall permit you to use Tripwire's trademarks,
// service marks, or other intellectual property without Tripwire's
// prior written consent.
// 
// If you have any questions, please contact Tripwire, Inc. at either
// info@tripwire.org or www.tripwire.org.
//
///////////////////////////////////////////////////////////////////////////////
// wchar16.h
//
// Function and classes dealing with the WCHAR16 type
//
#include "stdcore.h"
#include "wchar16.h"
#include "debug.h"
#include <stdexcept> // for stl::out_of_range
#include <memory.h>

int wchar16len(const WCHAR16* s)
{
    int i;
    for (i = 0; *s != 0; ++s, ++i)
        ;

    return i;
}

//=============================================================================
// class wc16_string
//
// This clas implements enough of the std::basic_string interface so we 
// can use it like an STL string. 
//
// In an ideal world we this would be a std::basic_string instantiation, but
// UNIX doesn't seem to dealing 16 bit wide chars, it uses 32 bit chars.
//=============================================================================

class wc16_string_impl
{
public:
    int      length;
    WCHAR16* pString;
    int      refCount;

    wc16_string_impl();
    wc16_string_impl(const wc16_string_impl& rhs);

    void AddRef();
    void Release();
    void Resize(int newlen);
    void CopyString(const wc16_string_impl& rhs);
        // note: does not alter this->referenceCount, and ASSERTs that refcount == 1

    ~wc16_string_impl();    // call Release() to delete
private:
    void operator = (const wc16_string_impl& rhs) { return; }	// don't call
};

static WCHAR16 NULL_WCHAR16 = 0;

wc16_string::wc16_string()
:   mpData(0)
{
}

wc16_string::wc16_string(const wc16_string& rhs)
{
    mpData = rhs.mpData;
    if (mpData)
        mpData->AddRef();
}

wc16_string::wc16_string(const_iterator pStr)
{
    mpData = new wc16_string_impl;

    int len = wchar16len(pStr);
    mpData->Resize(len);
    memcpy(mpData->pString, pStr, len * sizeof(WCHAR16));
}

wc16_string::~wc16_string()
{
    if (mpData)
        mpData->Release();
}

void wc16_string::operator = (const wc16_string& rhs)
{
    if (mpData)
        mpData->Release();

    mpData = rhs.mpData;

	if( mpData )
	    mpData->AddRef();
}


int wc16_string::compare(const wc16_string& rhs) const
{
    if( length() > rhs.length() )
        return 1;
    else if ( length() < rhs.length() )
        return -1;
    else
        return memcmp( c_str(), rhs.c_str(), length()*sizeof( WCHAR16 ) );
}

wc16_string::size_type
wc16_string::length() const
{
    return mpData ? mpData->length : 0;
}

wc16_string::size_type
wc16_string::size() const
{
    return mpData ? mpData->length : 0;
}

wc16_string::const_iterator
wc16_string::c_str() const
{
    if (mpData == 0)
        return &NULL_WCHAR16;

    mpData->pString[mpData->length] = 0;

    return mpData->pString;
}

wc16_string::const_iterator
wc16_string::begin() const
{
    return (const_cast<wc16_string*>(this))->begin();
}

wc16_string::iterator
wc16_string::begin()
{
    return mpData ? mpData->pString : &NULL_WCHAR16;
}

wc16_string::const_iterator
wc16_string::end() const
{
    return 
        const_cast< iterator >(
            mpData 
                ? mpData->pString + mpData->length
                : &NULL_WCHAR16 );
}

wc16_string::iterator
wc16_string::end()
{
    return 
        mpData 
            ? mpData->pString + mpData->length
            : &NULL_WCHAR16;
}

wc16_string::const_iterator wc16_string::data() const
{
    return begin();
}

const wc16_string::value_type& wc16_string::operator [] (int i) const
{
    ASSERT(i >= 0);

    if (mpData == 0 || i >= mpData->length || i < 0)
    {
        ASSERT(false);
        throw std::out_of_range("wc16_string[]");
    }

    return mpData->pString[i];
}

wc16_string::value_type& wc16_string::operator [] (int i)
{
    ASSERT(i >= 0);

    if (mpData == 0 || i >= mpData->length || i < 0)
    {
        ASSERT(false);
        throw std::out_of_range("wc16_string[]");
    }

    if (mpData->refCount > 1)
    {
        wc16_string_impl* newImpl = new wc16_string_impl(*mpData);
        mpData->Release();
        mpData = newImpl;
    }
    
    return mpData->pString[i];
}

void wc16_string::resize( size_type nCount )
{
    if ( mpData == 0 )
    {
        mpData = new wc16_string_impl;
    }
    else if ( mpData->refCount > 1 )
    {
        wc16_string_impl* newImpl = new wc16_string_impl( *mpData );
        mpData->Release();
        mpData = newImpl;
    }

    mpData->Resize( nCount );
}

// useful to convert to network byte order
void wc16_string::swapbytes()
{
    if (mpData)
    {
        if (mpData->refCount > 1)
        {
            wc16_string_impl* newImpl = new wc16_string_impl(*mpData);
            mpData->Release();
            mpData = newImpl;
        }

        int i;
        for (i = 0; i < mpData->length; ++i)
        {
            mpData->pString[i] = SWAPBYTES16(mpData->pString[i]);
        }
    }
}

wc16_string_impl::wc16_string_impl()
{
    length = 0;
    pString = new WCHAR16[1];
    refCount = 1;
}

wc16_string_impl::wc16_string_impl(const wc16_string_impl& rhs)
{
    int newlen = rhs.length;
    pString = new WCHAR16[newlen + 1];
    length = newlen;
    refCount = 1;

    memcpy(pString, rhs.pString, newlen * sizeof(WCHAR16));
}

wc16_string_impl::~wc16_string_impl()
{
    delete [] pString;
}

void wc16_string_impl::AddRef()
{
    ++refCount;
}

void wc16_string_impl::Release()
{
    ASSERT(refCount > 0);

    if (--refCount == 0)
    {
        delete this;
    }
}

void wc16_string_impl::Resize(int newlen)
{
    ASSERT(newlen >= 0);
    if (newlen == length)
        return;

    WCHAR16* newString = new WCHAR16[newlen + 1];
    
    if (newlen < length)
    {
        memcpy(newString, pString, newlen * sizeof(WCHAR16));
        delete [] pString;
        pString = newString;
        length = newlen;
    }
    else
    {
        memcpy(newString, pString, length * sizeof(WCHAR16));
        memset(newString + length, 0, (newlen - length) * sizeof(WCHAR16));
        delete [] pString;
        pString = newString;
        length = newlen;
    }
}

void wc16_string_impl::CopyString(const wc16_string_impl& rhs)
{
    ASSERT(refCount == 1);

    int newlen = rhs.length;

    if (rhs.length != this->length)
    {
        delete [] pString;
        pString = new WCHAR16[newlen + 1];
        this->length = newlen;
    }

    memcpy(this->pString, rhs.pString, newlen * sizeof(WCHAR16));
}

