// MOStringModifying.m
//
// by Mike Ferris
// Part of MOKit
// Copyright 1993, all rights reserved.

// ABOUT MOKit
// by Mike Ferris (mike@lorax.com)
//
// MOKit is a collection of useful and general objects.  Permission is 
// granted by the author to use MOKit in your own programs in any way 
// you see fit.  All other rights pertaining to the kit are reserved by the 
// author including the right to sell these objects as objects,  as part 
// of a LIBRARY, or as SOURCE CODE.  In plain English, I wish to retain 
// rights to these objects as objects, but allow the use of the objects 
// as pieces in a fully functional program.  Permission is also granted to 
// redistribute the source code of MOKit for FREE as long as this copyright 
// notice is left intact and unchanged.  NO WARRANTY is expressed or implied.  
// The author will under no circumstances be held responsible for ANY 
// consequences from the use of these objects.  Since you don't have to pay 
// for them, and full source is provided, I think this is perfectly fair.

#import "MOString.h"

extern char *MOBuildStringFromFormatV(const char *formatStr, 
			va_list param_list);

@implementation MOString(Modifying)

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// =-=-=-=-=-=-=-=-=-=-=-=-=- Modifying MOStrings -=-=-=-=-=-=-=-=-=-=-=-=-=
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

- takeStringValueFrom:sender
{
	[self setStringValue:[sender stringValue]];
	return self;
}

- takeIntValueFrom:sender
{
	[self setIntValue:[sender intValue]];
	return self;
}

- takeFloatValueFrom:sender
{
	[self setFloatValue:[sender floatValue]];
	return self;
}

- takeDoubleValueFrom:sender
{
	[self setDoubleValue:[sender doubleValue]];
	return self;
}

- cat:stringObject
// Adds the contents of the given String object to the receiver's contents.
// Returns nil if stringObject is nil or empty, self if it succeeds.
// Unique strings CANNOT be modified.
{
	if (isUnique)  {
		return nil;
	}
	if (!stringObject)  {
		// nothing needs to be done
		return self;
	}
	return (([self strcat:[stringObject stringValue]]==NULL)?nil:self);
}

- catStringValue:(const char *)s
// Just a conceptual contiinuity cover for -strcat:
{
	if (isUnique)  {
		return nil;
	}
	return (([self strcat:s]==NULL)?nil:self);
}

- catFromFormat:(const char *)format, ...;
// Appends to the string from the printf style format string and arguments.
{
	va_list param_list;
	char *buf;
	
	if (isUnique)  {
		return nil;
	}
	
	va_start(param_list, format);
	buf = MOBuildStringFromFormatV(format, param_list);
	va_end(param_list);
	
	[self strcat:buf];
	
	NX_FREE(buf);
	
	return self;
}

- preCat:stringObject
// Prepends the contents of the given string object before the contents of
// the receiver
{
	id newStr;
	if (isUnique)  {
		return nil;
	}
	if (!stringObject || [stringObject isEmpty])  {
		// nothing needs to be done
		return self;
	}
	
	newStr = [[[self class] allocFromZone:[self zone]] init];
	
	[newStr setFromFormat:"%s%s", [stringObject stringValue], str];
	[self setStringValue:[newStr stringValue]];
	
	[newStr free];

	return self;
}

- preCatStringValue:(const char *)s
// Prepends the given c-string before the contents of
// the receiver
{
	id newStr;
	if (isUnique)  {
		return nil;
	}
	if (!s || !*s)  {
		// nothing needs to be done
		return self;
	}
	
	newStr = [[[self class] allocFromZone:[self zone]] init];
	
	[newStr setFromFormat:"%s%s", s, str];
	[self setStringValue:[newStr stringValue]];
	
	[newStr free];

	return self;
}

- preCatFromFormat:(const char *)format, ...
// Prepends the given format string after formatting before the contents
// of the receiver.
{
	id newStr;
	va_list param_list;
	char *buf;
	
	if (isUnique)  {
		return nil;
	}
	
	va_start(param_list, format);
	buf = MOBuildStringFromFormatV(format, param_list);
	va_end(param_list);
	
	
	newStr = [[[self class] allocFromZone:[self zone]] init];
	
	[newStr setFromFormat:"%s%s", buf, str];
	[self setStringValue:[newStr stringValue]];
	
	[newStr free];
	NX_FREE(buf);
	
	return self;
}

- insert:stringObject at:(int)position
// Inserts the contents of the given string into the contents of the receiver
// such that the first character of the inserted string is at position.
// position must be in the range 0..length of receiver's contents.
{
	id subStr1 = nil, subStr2 = nil;
	
	if (isUnique)  {
		return nil;
	}
	if ((position < 0) || (position > len))  {
		return nil;
	}
	if (!stringObject || [stringObject isEmpty])  {
		// nothing needs to be done
		return self;
	}
	
	if (position > 0)  {
		subStr1 = [self substringFrom:0 to:position-1];
	}
	if (position < len)  {
		subStr2 = [self substringFrom:position to:len];
	}
	[self setNull];
	if (subStr1)  {
		[self cat:subStr1];
	}
	[self cat:stringObject];
	if (subStr2)  {
		[self cat:subStr2];
	}
	
	[subStr1 free];
	[subStr2 free];
	
	return self;
}

- insertStringValue:(const char *)s at:(int)position
// Inserts the the given c-string into the contents of the receiver
// such that the first character of the inserted string is at position.
// position must be in the range 0..length of receiver's contents.
{
	id subStr1 = nil, subStr2 = nil;
	
	if (isUnique)  {
		return nil;
	}
	if ((position < 0) || (position > len))  {
		return nil;
	}
	if (!s || !*s)  {
		// nothing needs to be done
		return self;
	}
	
	if (position > 0)  {
		subStr1 = [self substringFrom:0 to:position-1];
	}
	if (position < len)  {
		subStr2 = [self substringFrom:position to:len];
	}
	[self setNull];
	if (subStr1)  {
		[self cat:subStr1];
	}
	[self strcat:s];
	if (subStr2)  {
		[self cat:subStr2];
	}
	
	[subStr1 free];
	[subStr2 free];
	
	return self;
}

- (char)replaceCharAt:(int)index with:(char)newChar
// Replaces the character at index with newChar.  Returns the old char.
// Returns '\0' if str is NULL or index is beyond the end of str.
// Unique strings CANNOT be modified.
{
	char oldChar;
	
	if (isUnique)  {
		return '\0';
	}
	if ((index<0) || (!str) || (index>=len))  {
		return '\0';
	}
	oldChar = str[index];
	str[index]=newChar;
	if (newChar == '\0')  {
		[self recalcLength];
	}
	return oldChar;	
}

- (int)replaceAllOccurrencesOfChar:(char)oldChar with:(char)newChar
// Replaces all occurrences of oldChar with newChar.  /0 is not an 
// acceptable value for either argument.  Returns the number of
// occurrences replaced or -1 if the string is unique or either argument is \0.
{
	int i, count = 0;
	
	if (isUnique || (oldChar == '\0') || (newChar == '\0'))  {
		return -1;
	}
	for (i=0; i<len; i++)  {
		if (str[i]==oldChar)  {
			str[i]=newChar;
			count++;
		}
	}
	return count;
}

- (size_t)recalcLength
// Recalculate and return the length of the string by calling strlen().  This 
// is used by replaceCharAt:with: if the character is '\0'.  It can be used if 
// you suspect that the cached length is incorrect.  (But it would only be
// incorrect if you were going behind the MOString's back and modifying
// buffers or if you had multiple MOStrings with the same buffer and you
// modified the buffer through one of the several MOStrings.)
{
	if ((isUnique?uStr:str) == NULL)  {
		len = 0;
	}  else  {
		len = strlen(isUnique?uStr:str);
	}
	return len;
}

- convertToLower
// Converts the string to lower case.  Non alpha characters are unchanged.
// Unique strings CANNOT be modified.
{
	char *index;
	
	if ((isUnique) || (!str))  {
		return nil;
	}
	
	index = str;
	while (*index)  {
		*index = NXToLower(*index);
		index++;
	}
	
	return self;
}

- convertToUpper
// Converts the string to upper case.  Non alpha characters are unchanged.
// Unique strings CANNOT be modified.
{
	char *index;
	
	if ((isUnique) || (!str))  {
		return nil;
	}
	
	index = str;
	while (*index)  {
		*index = NXToUpper(*index);
		index++;
	}
	
	return self;
}

@end

