//-----------------------------------------------------------------------------
//
//  $Logfile:: /Quake 2 Engine/Sin/code/game/str.h                            $
// $Revision:: 6                                                              $
//   $Author:: Jimdose                                                        $
//     $Date:: 10/25/98 11:58p                                                $
//
// Copyright (C) 1997 by Ritual Entertainment, Inc.
// All rights reserved.
//
// This source is may not be distributed and/or modified without
// expressly written permission by Ritual Entertainment, Inc.
//
// $Log:: /Quake 2 Engine/Sin/code/game/str.h                                 $
//
// 6     10/25/98 11:58p Jimdose
// working on getting rid of exports
//
// 5     10/08/98 12:04a Jimdose
// made = keep the old string around until it was copied in case the source was
// inside the old string
//
// 4     6/09/98 4:25p Jimdose
// added substring copy constructor
//
// 3     5/13/98 4:53p Jimdose
// Moved defintion of functions to header to make them inline
//
// 2     4/30/98 9:23p Jimdose
// Created file
//
// DESCRIPTION:
// Simple, DLL portable string class
//

#ifndef __STR_H__
#define __STR_H__

#include <assert.h>
#include <string.h>

#include "q_shared.h"

class str
{
protected:
	int len;
	char*                   data;

public:
	~str();
	str();
	str( const char* text );
	str( const str& string );
	str( const str string, int start, int end );

	int     length( void ) const;
	const char*   c_str( void ) const;

	void        append( const char* text );
	void        append( const str& text );

	char operator[]( int index ) const;
	char&       operator[]( int index );

	void operator=( const str& text );
	void operator=( const char* text );

	friend  str operator+( const str& a, const str& b );
	friend  str operator+( const str& a, const char* b );
	friend  str operator+( const char* a, const str& b );

	str&        operator+=( const str& a );
	str&        operator+=( const char* a );

	friend  int operator==( const str& a, const str& b );
	friend  int operator==( const str& a, const char* b );
	friend  int operator==( const char* a, const str& b );

	friend  int operator!=( const str& a, const str& b );
	friend  int operator!=( const str& a, const char* b );
	friend  int operator!=( const char* a, const str& b );
};

inline str::~str()
{
	assert( data );
	if( data )
	{
		delete [] data;
		data = NULL;
	}
}

inline str::str()
{
	data = new char[ 1 ];
	data[ 0 ] = 0;
	len = 0;
}

inline str::str( const char* text )
{
	assert( text );

	if( text )
	{
		len = (int)strlen( text );
		data = new char[ len + 1 ];
		strcpy( data, text );
	}
	else
	{
		data = new char[ 1 ];
		data[ 0 ] = 0;
		len = 0;
	}
}

inline str::str( const str& text )
{
	len = text.length();
	data = new char[ len + 1 ];
	strcpy( data, text.c_str() );
}

inline str::str( const str text, int start, int end )
{
	int i;

	if( end > text.length() )
	{
		end = text.length();
	}

	if( start > text.length() )
	{
		start = text.length();
	}

	len = end - start;
	if( len < 0 )
	{
		len = 0;
	}

	data = new char[ len + 1 ];
	for( i = 0; i < len; i++ )
	{
		data[ i ] = text[ start + i ];
	}

	data[ len ] = 0;
}

inline int str::length( void ) const
{
	return len;
}

inline const char* str::c_str( void ) const
{
	assert( data );

	return data;
}

inline void str::append( const char* text )
{
	char* olddata;

	assert( text );
	assert( data );

	if( text )
	{
		olddata = data;

		len = (int)strlen( data ) + (int)strlen( text );
		data = new char[ len + 1 ];

		strcpy( data, olddata );
		strcat( data, text );

		if( olddata )
		{
			delete [] olddata;
			olddata = NULL;
		}
	}
}

inline void str::append( const str& text )
{
	assert( data );

	append( text.c_str() );
}

inline char str::operator[]( int index ) const
{
	assert( data );

	// don't include the '/0' in the test, because technically, it's out of bounds
	assert( ( index >= 0 ) && ( index < len ) );

	// In release mode, give them a null character
	// don't include the '/0' in the test, because technically, it's out of bounds
	if( ( index < 0 ) || ( index >= len ) )
	{
		return 0;
	}

	return data[ index ];
}

inline char& str::operator[]( int index )
{
	// Used for result for invalid indices
	static char dummy = 0;

	assert( data );

	// don't include the '/0' in the test, because technically, it's out of bounds
	assert( ( index >= 0 ) && ( index < len ) );

	// In release mode, let them change a safe variable
	// don't include the '/0' in the test, because technically, it's out of bounds
	if( ( index < 0 ) || ( index >= len ) )
	{
		return dummy;
	}

	return data[ index ];
}

inline void str::operator=( const str& text )
{
	char* temp;

	//this assert() fires in djgpp dxe3 builds upon quit!
	//assert( data );

	len = text.length();
	temp = new char[ len + 1 ];
	strcpy( temp, text.c_str() );

	// we don't destroy the old data until we've allocated the new one
	// in case we are assigning the string from data inside the old string.
	if( data )
	{
		delete [] data;
	}

	data = temp;
}

inline void str::operator=( const char* text )
{
	char* temp;

	assert( data );
	assert( text );

	if( !text )
	{
		// safe behaviour if NULL
		len = 0;
		temp = new char[ 1 ];
		temp[ 0 ] = 0;
	}
	else
	{
		len = (int)strlen( text );
		temp = new char[ len + 1 ];
		strcpy( temp, text );
	}

	// we don't destroy the old data until we've allocated the new one
	// in case we are assigning the string from data inside the old string.
	if( data )
	{
		delete [] data;
	}

	data = temp;
}

inline str operator+( const str& a, const str& b )
{
	str result( a );

	result.append( b );

	return result;
}

inline str operator+( const str& a, const char* b )
{
	str result( a );

	result.append( b );

	return result;
}

inline str operator+( const char* a, const str& b )
{
	str result( a );

	result.append( b );

	return result;
}

inline str& str::operator+=( const str& a )
{
	assert( data );

	append( a );
	return *this;
}

inline str& str::operator+=( const char* a )
{
	assert( data );

	append( a );
	return *this;
}

inline int operator==( const str& a, const str& b )
{
	return ( !strcmp( a.c_str(), b.c_str() ) );
}

inline int operator==( const str& a, const char* b )
{
	assert( b );

	if( !b )
	{
		return false;
	}
	return ( !strcmp( a.c_str(), b ) );
}

inline int operator==( const char* a, const str& b )
{
	assert( a );

	if( !a )
	{
		return false;
	}
	return ( !strcmp( a, b.c_str() ) );
}

inline int operator!=( const str& a, const str& b )
{
	return !( a == b );
}

inline int operator!=( const str& a, const char* b )
{
	return !( a == b );
}

inline int operator!=( const char* a, const str& b )
{
	return !( a == b );
}

// Test function
void TestStringClass( void );

#endif
