/***************************************************************************
* Char.h                                                                   *
*                                                                          *
* Convenient constants, macros, and inline functions for manipulation of   *
* characters and strings.                                                  *
*                                                                          *
*   HISTORY                                                                *
*      Name    Date        Description                                     *
*                                                                          *
*      arvo    07/01/93    Initial coding.                                 *
*                                                                          *
*--------------------------------------------------------------------------*
* Copyright (C) 1999, James Arvo                                           *
*                                                                          *
* This program is free software; you can redistribute it and/or modify it  *
* under the terms of the GNU General Public License as published by the    *
* Free Software Foundation.  See http://www.fsf.org/copyleft/gpl.html      *
*                                                                          *
* This program is distributed in the hope that it will be useful, but      *
* WITHOUT EXPRESS OR IMPLIED WARRANTY of merchantability or fitness for    *
* any particular purpose.  See the GNU General Public License for more     *
* details.                                                                 *
*                                                                          *
***************************************************************************/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "Char.h"

namespace ArvoMath {

	typedef char *charPtr;

	// Treat "str" as a file name, and return just the directory
	// portion -- i.e. strip off the name of the leaf object (but
	// leave the final "/".
	const char *getPath( const char *str, char *buff )
	{
		int k;
		for( k = strlen( str ) - 1; k >= 0; k-- )
		{
			if( str[k] == Slash ) break;
		}
		for( int i = 0; i <= k; i++ ) buff[i] = str[i];
		buff[k+1] = NullChar;
		return buff;
	}

	// Treat "str" as a file name, and return just the file name
	// portion -- i.e. strip off everything up to and including
	// the final "/".
	const char *getFile( const char *str, char *buff )
	{
		int k;
		int len = strlen( str );
		for( k = len - 1; k >= 0; k-- )
		{
			if( str[k] == Slash ) break;
		}
		for( int i = 0; i < len - k; i++ ) buff[i] = str[ i + k + 1 ];
		return buff;
	}

	int getPrefix( const char *str, char *buff )
	{
		int len = 0;
		while( *str != NullChar && *str != Period ) 
		{
			*buff++ = *str++;
			len++;
		}
		*buff = NullChar;
		return len;
	}

	int getSuffix( const char *str, char *buff )
	{
		int n = strlen( str );
		int k = n - 1;
		while( k >= 0 && str[k] != Period ) k--;
		for( int i = k + 1; i < n; i++ ) *buff++ = str[i];
		*buff = NullChar;    
		return n - k - 1;
	}

	const char* toString( int number, char *buff )
	{
		static char local_buff[32];
		char *str = ( buff == NULL ) ? local_buff : buff;
		sprintf( str, "%d", number );
		return str;
	}

	const char* toString( float number, char *buff )
	{
		static char local_buff[32];
		char *str = ( buff == NULL ) ? local_buff : buff;
		sprintf( str, "%g", number );
		return str;
	}

	int isInteger( const char *str )
	{
		int n = strlen( str );
		for( int i = 0; i < n; i++ )
		{
			char c = str[i];
			if( isDigit(c) ) continue;
			if( c == Plus || c == Minus ) continue;
			if( c == Space ) continue;
			return 0;
		}
		return 1;
	}

	// Test to see if a string has a given suffix.
	int hasSuffix( const char *string, const char *suffix )
	{
		if( suffix == NULL ) return 1; // The null suffix always matches.
		if( string == NULL ) return 0; // The null string can only have a null suffix.
		int m = strlen( string );
		int k = strlen( suffix );
		if( k <= 0    ) return 1; // Empty suffix always matches.
		if( m < k + 1 ) return 0; // String is too short to have this suffix.

		// See if the file has the given suffix.
		int s = m - k;  // Beginning of suffix (if it matches).
		for( int i = 0; i < k; i++ )
			if( string[ s + i ] != suffix[ i ] ) return 0;
		return s;  // Always > 0.
	}

	// Test to see if a string has a given prefix.
	int hasPrefix( const char *string, const char *prefix )
	{
		if( prefix == NULL ) return 1; // The null prefix always matches.
		if( string == NULL ) return 0; // The null string can only have a null suffix.
		while( *prefix )
		{
			if( *prefix++ != *string++ ) return 0;
		}
		return 1;
	}

	// Test to see if the string contains the given character.
	int inString( char c, const char *str )
	{
		if( str == NULL || str[0] == NullChar ) return 0;
		while( *str != '\0' ) 
			if( *str++ == c ) return 1;
		return 0;
	}

	int nullString( const char *str )
	{
		return str == NULL || str[0] == NullChar;
	}

	const char *stripSuffix( const char *string, const char *suffix, char *buff )
	{
		static char local_buff[256];
		if( buff == NULL ) buff = local_buff;
		buff[0] = NullChar;
		if( !hasSuffix( string, suffix ) ) return NULL;
		int s = strlen( string ) - strlen( suffix );
		for( int i = 0; i < s; i++ )
		{
			buff[i] = string[i];
		}
		buff[s] = NullChar;
		return buff;
	}

	int getIndex( const char *pat, const char *str )
	{
		int p_len = strlen( pat );
		int s_len = strlen( str );
		if( p_len == 0 || s_len == 0 ) return -1;
		for( int i = 0; i <= s_len - p_len; i++ )
		{
			int match = 1;
			for( int j = 0; j < p_len; j++ )
			{
				if( str[ i + j ] != pat[ j ] ) { match = 0; break; }
			}
			if( match ) return i;
		}
		return -1;
	}

	int getSubstringAfter( const char *pat, const char *str, char *buff )
	{
		int ind = getIndex( pat, str );
		if( ind < 0 ) return -1;
		int p_len = strlen( pat );
		int k = 0;
		for( int i = ind + p_len; ; i++ )
		{
			buff[ k++ ] = str[ i ];
			if( str[ i ] == NullChar ) break;
		}
		return k;
	}

	const char *SubstringAfter( const char *pat, const char *str, char *user_buff )
	{
		static char temp[128];
		char *buff = ( user_buff != NULL ) ? user_buff : temp;
		int k = getSubstringAfter( pat, str, buff );
		if( k > 0 ) return buff;
		return str;
	}

	const char *metaString( const char *str, char *user_buff )
	{
		static char temp[128];
		char *buff = ( user_buff != NULL ) ? user_buff : temp;
		sprintf( buff, "\"%s\"", str );
		return buff;
	}

	// This is the opposite of metaString.
	const char *stripQuotes( const char *str, char *user_buff )
	{
		static char temp[128];
		char *buff = ( user_buff != NULL ) ? user_buff : temp;
		char *b = buff;
		for(;;)
		{
			if( *str != DoubleQuote ) *b++ = *str;
			if( *str == NullChar ) break; 
			str++;
		}
		return buff;
	}

	int getIntFlag( const char *flags, const char *flag, int &value )
	{
		while( *flags )
		{
			if( hasPrefix( flags, flag ) )
			{
				int k = strlen( flag );
				if( flags[k] == '=' )
				{
					value = atoi( flags + k + 1 );
					return 1;
				}
			}
			flags++;
		}
		return 0;
	}

	int getFloatFlag( const char *flags, const char *flag, float &value )
	{
		while( *flags )
		{
			if( hasPrefix( flags, flag ) )
			{
				int k = strlen( flag );
				if( flags[k] == '=' )
				{
					value = atof( flags + k + 1 );
					return 1;
				}
			}
			flags++;
		}
		return 0;
	}

	SortedList::SortedList( sort_type type_, int ascend_ )
	{
		type         = type_;
		ascend       = ascend_;
		num_elements = 0;
		max_elements = 0;
		sorted       = 1;
		list         = NULL;
	}

	SortedList::~SortedList()
	{
		Clear();
		delete[] list;
	}

	void SortedList::Clear()
	{
		// Delete all the private copies of the strings and re-initialize the
		// list.  Reuse the same list, expanding it when necessary.
		for( int i = 0; i < num_elements; i++ ) 
		{
			delete list[i];
			list[i] = NULL;
		}
		num_elements = 0;
		sorted       = 1;
	}

	SortedList &SortedList::operator<<( const char *str )
	{
		// Add a new string to the end of the list, expanding the list if necessary.
		// Mark the list as unsorted, so that the next reference to an element will
		// cause the list to be sorted again.
		if( num_elements == max_elements ) Expand();
		list[ num_elements++ ] = strdup( str );
		sorted = 0;
		return *this;
	}

	const char *SortedList::operator()( int i )
	{
		// Return the i'th element of the list.  Sort first if necessary.
		static char *null = "";
		if( num_elements == 0 || i < 0 || i >= num_elements ) return null;
		if( !sorted ) Sort();
		return list[i];
	}

	void SortedList::Expand()
	{
		// Create a new list of twice the size and copy the old list into it.
		// This doubles "max_elements", but leaves "num_elements" unchanged.
		if( max_elements == 0 ) max_elements = 1;
		max_elements *= 2;
		charPtr *new_list = new charPtr[ max_elements ];
		for( int i = 0; i < max_elements; i++ ) 
			new_list[i] = ( i < num_elements ) ? list[i] : NULL;
		delete[] list;
		list = new_list;
	}

	void SortedList::Swap( int i, int j )
	{
		char *temp = list[i];
		list[i] = list[j];
		list[j] = temp;
	}

	int SortedList::inOrder( int p, int q ) const
	{
		int test;
		if( type == sort_alphabetic )
			test = ( strcmp( list[p], list[q] ) <= 0 );
		else
		{
			int len_p = strlen( list[p] );
			int len_q = strlen( list[q] );
			test = ( len_p <  len_q ) || 
				( len_p == len_q && strcmp( list[p], list[q] ) <= 0 );
		}
		if( ascend ) return test;
		return !test;
	}

	// This is an insertion sort that operates on subsets of the
	// input defined by the step length.
	void SortedList::InsertionSort( int start, int size, int step ) 
	{
		for( int i = 0; i + step < size; i += step )
		{
			for( int j = i; j >= 0; j -= step )
			{
				int p = start + j;
				int q = p + step;
				if( inOrder( p, q ) ) break;
				Swap( p, q );
			}
		}
	}

	// This is a Shell sort.
	void SortedList::Sort()
	{
		for( int step  = num_elements / 2; step > 1; step /= 2 )
			for( int start = 0; start < step; start++ )
				InsertionSort( start, num_elements  - start, step );
		InsertionSort( 0, num_elements, 1 );
		sorted = 1;
	}

	void SortedList::SetOrder( sort_type type_, int ascend_ )
	{
		if( type_ != type || ascend_ != ascend )
		{
			type   = type_;
			ascend = ascend_;
			sorted = 0;
		}
	}

	int getstring( std::istream &in, const char *str )
	{
		char ch;
		if( str == NULL ) return 1;
		while( *str != NullChar )
		{
			in >> ch;
			if( *str != ch ) return 0;
			str++;
		}
		return 1;
	}

	std::istream &skipWhite( std::istream &in )
	{
		char c;
		while( in.get(c) ) 
		{
			if( !isWhite( c ) ) 
			{
				in.putback(c);
				break;
			}
		}
		return in;
	}
};
