/*
 * Copyright (C) 1992 by Software Research Associates, Inc.
 *	Author:	Y. Kawabe <kawabe@sra.co.jp>
 *
 * Permission to use, copy, modify, and distribute, and sell this software
 * and its documentation for any purpose and without fee is hereby granted,
 * provided that the above copyright notice appear in all copies and that
 * both that copyright notice and this permission notice appear in supporting
 * documentation, and that the name of Software Research Associates not be
 * used in advertising or publicity pertaining to distribution of the software
 * without specific, written prior permission.  Software Research Associates
 * makes no representations about the suitability of this software for any
 * purpose.  It is provided "as is" without express or implied warranty.
 *
 */

#include <NLS/charset.h>
#include <NLS/codeset.h>
#include <NLS/locale.h>
#include <NLS/wchar.h>
#include <NLS/wstring.h>
#include <hyperg/OS/list.h>
#include <hyperg/OS/string.h>

static int strlen (const WChar* s) {
    if (s == nil) return 0;
    int i;
    for (i = 0; s[i] != '\0'; i++);
    return i;
}

/*
 * Class WString
 */

WString::WString() {
    data_   = 0;
    length_ = 0;
    allocated_ = false;
    null_terminated_ = false;
}

WString::WString(const WChar *w) {
    data_ = w;
    length_ = strlen(w);
    allocated_ = false;
    null_terminated_ = true;
}

WString::WString(const WChar *w, int len) {
    data_ = w;
    length_ = len;
    allocated_ = false;
    null_terminated_ = false;
}

WString::WString(const Locale& locale, const char* s) {
    data_ = CodeSet::Convert(locale, s);
    length_ = strlen(data_);
    allocated_ = true;
    null_terminated_ = true;
}

WString::WString(const Locale& locale, const char* s, int len) {
    data_ = CodeSet::Convert(locale, s, len);
    length_ = strlen(data_);
    allocated_ = true;
    null_terminated_ = true;
}

WString::WString(const Locale& locale, const String& s) {
    data_ = CodeSet::Convert(locale, s.string(), s.length());
    length_ = strlen(data_);
    allocated_ = true;
    null_terminated_ = true;
}

WString::WString(const char* s) {
    data_ = CodeSet::Convert(Locale::setlocale(), s);
    length_ = strlen(data_);
    allocated_ = true;
    null_terminated_ = true;
}

WString::WString(const char* s, int len) {
    data_ = CodeSet::Convert(Locale::setlocale(), s, len);
    length_ = strlen(data_);
    allocated_ = true;
    null_terminated_ = true;
}

WString::WString(const String& s) {
    data_ = CodeSet::Convert(Locale::setlocale(), s.string(), s.length());
    length_ = strlen(data_);
    allocated_ = true;
    null_terminated_ = true;
}

WString::~WString() {
    if (allocated_) {
	delete [] (WChar *) data_;
    }
}

/*
 * A negative value for start initializes the position at the
 * end of the string before indexing.  Any negative length makes
 * the substring extend to the end of the string.
 */

WString WString::substr(int start, int length) const {
    if (start >= length_ || start < -length_) {
        return *this;
    }
    int pos = (start >= 0) ? start : (length_ + start);
    if (pos + length > length_) {
        return *this;
    }
    int len = (length >= 0) ? length : (length_ - pos);
    return WString(data_ + pos, len);
}

void WString::set_to_substr(int start, int length) {
    if (start > length_ || start < -length_) {
        return;
    }
    int pos = (start >= 0) ? start : (length_ + start);
    if (pos + length > length_) {
        return;
    }
    int len = (length >= 0) ? length : (length_ - pos);
    data_ += pos;
    length_ = len;
}

/*
 * A negative value for start initializes the position to the
 * end of the string before indexing and searches right-to-left.
 */

int WString::search(int start, const WChar& c) const {
    if (start >= length_ || start < -length_) {
	return -1;
    }
    if (start >= 0) {
	for (int i = start; i < length_; i++) {
	    if (data_[i] == c) {
		return i;
	    }
	}
    } else {
	for (int i = length_ + start; i > 0; i--) {
	    if (data_[i] == c) {
		return i;
	    }
	}
    }
    return -1;
}

void WString::reverse() {
    for (int i = 0, j = length_ - 1; i < j ; i++, j--) {
        register int tmp = data_[i].value();
	*((WChar*)(data_+ i)) = data_[j];
	*((WChar*)(data_+ j)) = tmp;
    }
}

WString& WString::operator = (const WString& ws) {
    data_ = ws.data_;
    length_ = ws.length_;
    null_terminated_ = ws.null_terminated_;
    allocated_ = false;
    return *this;
}

boolean WString::operator == (const WString& ws) const {
    if (length_ != ws.length_) {
	return false;
    }
    for (int i = 0; i < length_; i++) {
	if (data_[i] != ws.data_[i])
	    return false;
    }
    return true;
}

boolean WString::operator != (const WString& ws) const {
    if (length_ != ws.length_) {
	return true;
    }
    for (int i = 0; i < length_; i++) {
	if (data_[i] != ws.data_[i])
	    return true;
    }
    return false;
}

/*
 * return string with Locale
 */

char* WString::string(const Locale& locale) const {
    return CodeSet::Convert(locale, data_, length_);
}

/*
 * class CopyWString
 */

CopyWString::CopyWString() : WString() { }

CopyWString::CopyWString(const WString &s) : WString() {
    copy (s.string(), s.length());
}

CopyWString::CopyWString(const WChar* s) : WString () {
    copy (s, strlen(s));
}

CopyWString::CopyWString(const WChar* s, int len) : WString () {
    copy (s, len);
}

void CopyWString::copy(const WChar* str, int len) {
    WChar* w = new WChar[len + 1];
    int i;
    for (i = 0;i < len; i++) {
	w[i] = str[i];
    }
    w[i] = '\0';

    data_ = w;
    length_ = len;
    null_terminated_ = true;
    allocated_ = true;
}

/*
 * class UniqueWStringPool
 */

declarePtrList(UniqueWStringPool, WString);
implementPtrList(UniqueWStringPool, WString);

/*
 * class UniqueWString
 */

UniqueWStringPool* UniqueWString::pool_;

UniqueWString::UniqueWString () {
    data_   = 0;
    length_ = 0;
    allocated_ = false;
    null_terminated_ = false;
}

UniqueWString::UniqueWString (const WString& str) {
    WString *v = nil;
    
    if (!pool_) { pool_ = new UniqueWStringPool (); }
    for (ListItr(UniqueWStringPool) i(*pool_); i.more(); i.next()) {
	if (*(i.cur()) == str) {
	    v = i.cur();
	    break;
	}
    }
    if (!v) {
	v = new CopyWString(str);
	pool_->append(v);
    }
    
    data_ = v->string();
    length_ = v->length();
    allocated_ = false;
    null_terminated_ = true;
}

boolean UniqueWString::operator == (const WString& s) const {
    return (string() == s.string()) && (length() == s.length());
}

boolean UniqueWString::operator != (const WString& s) const {
    return (string() != s.string()) || (length() != s.length());
}
