using std::cout;  using std::cerr;  using std::endl;

#ifndef __CPP__INCLUDED__RPerl__DataType__String_cpp
#define __CPP__INCLUDED__RPerl__DataType__String_cpp 0.013_100

// [[[ INCLUDES ]]]
#include <RPerl/DataType/String.h>		// -> NULL (relies on native C type)
#include <RPerl/DataType/Boolean.cpp>  // -> Boolean.h
#include <RPerl/DataType/UnsignedInteger.cpp>  // -> UnsignedInteger.h
#include <RPerl/DataType/Integer.cpp>  // -> Integer.h
#include <RPerl/DataType/Number.cpp>  // -> Number.h
#include <RPerl/DataType/Character.cpp>  // -> Character.h

// [[[ GENERAL FUNCTIONS ]]]

// DEV NOTE, CORRELATION #rp070: "logical and" && operator behaves differently in Perl vs C++, accepts strings so must use double-negation trick to convert to boolean, returns operand on right if operator evaluates to true; use ANDl instead of && in C++
bool operator!(const string& s) {

    // START HERE: should this be returning s.empty() or string_to_boolean()?  need write tests for negation of strings in PERLOPS_PERLTYPES vs CPPOPS_CPPTYPES?
    // START HERE: should this be returning s.empty() or string_to_boolean()?  need write tests for negation of strings in PERLOPS_PERLTYPES vs CPPOPS_CPPTYPES?
    // START HERE: should this be returning s.empty() or string_to_boolean()?  need write tests for negation of strings in PERLOPS_PERLTYPES vs CPPOPS_CPPTYPES?

    return s.empty();
}

// [[[ TYPE CHECKING ]]]
// [[[ TYPE CHECKING ]]]
// [[[ TYPE CHECKING ]]]

// NEED ANSWER: does this change the original string via move semantics, or make a copy?
// escape all back-slash \ and single-quote ' characters with a back-slash \ character
string escape_backslash_singlequote(string unescaped_string) {
    string escaped_string = unescaped_string;
    size_t escaped_string_pos = 0;
    while((escaped_string_pos = escaped_string.find("\\", escaped_string_pos)) != string::npos)
    {
        escaped_string.replace(escaped_string_pos, 1, "\\\\");
        escaped_string_pos += 2;
    }
    escaped_string_pos = 0;
    while((escaped_string_pos = escaped_string.find("'", escaped_string_pos)) != string::npos)
    {
        escaped_string.replace(escaped_string_pos, 1, "\\'");
        escaped_string_pos += 2;
    }
    return escaped_string;
}

// TYPE-CHECKING SUBROUTINES DEPRECATED IN FAVOR OF EQUIVALENT MACROS
/*
void string_CHECK(SV* possible_string) {
    if (not(SvOK(possible_string))) {
    	croak("\nERROR EPV00, TYPE-CHECKING MISMATCH, CPPOPS_PERLTYPES & CPPOPS_CPPTYPES:\nstring value expected but undefined/null value found,\ncroaking");
    }
	if (not(SvPOKp(possible_string))) {
    	croak("\nERROR EPV01, TYPE-CHECKING MISMATCH, CPPOPS_PERLTYPES & CPPOPS_CPPTYPES:\nstring value expected but non-string value found,\ncroaking");
    }
};
void string_CHECKTRACE(SV* possible_string, const char* variable_name, const char* subroutine_name) {
    if (not(SvOK(possible_string))) {
    	croak("\nERROR EPV00, TYPE-CHECKING MISMATCH, CPPOPS_PERLTYPES & CPPOPS_CPPTYPES:\nstring value expected but undefined/null value found,\nin variable %s from subroutine %s,\ncroaking",
    			variable_name, subroutine_name);
    }
	if (not(SvPOKp(possible_string))) {
    	croak("\nERROR EPV01, TYPE-CHECKING MISMATCH, CPPOPS_PERLTYPES & CPPOPS_CPPTYPES:\nstring value expected but non-string value found,\nin variable %s from subroutine %s,\ncroaking",
    			variable_name, subroutine_name);
    }
};
*/

// [[[ TYPEMAP PACK/UNPACK FOR __CPP__TYPES ]]]
// [[[ TYPEMAP PACK/UNPACK FOR __CPP__TYPES ]]]
// [[[ TYPEMAP PACK/UNPACK FOR __CPP__TYPES ]]]

// DEV NOTE, CORRELATION #rp010: the pack/unpack subs (below) are called by number_to_string_CPPTYPES(), moved outside #ifdef blocks
//# ifdef __CPP__TYPES

// convert from (Perl SV containing string) to (C++ std::string)
string XS_unpack_string(SV* input_sv)
{
//fprintf(stderr, "in CPPOPS_CPPTYPES XS_unpack_string(), top of subroutine\n");
//	string_CHECK(input_sv);
	string_CHECKTRACE(input_sv, "input_sv", "XS_unpack_string()");
//	string output_string;
//	output_string = SvPV_nolen(input_sv);
//fprintf(stderr, "in CPPOPS_CPPTYPES XS_unpack_string(), bottom of subroutine\n");
	return((string)SvPV_nolen(input_sv));
//	return(output_string);
}

// convert from (C++ std::string) to (Perl SV containing string)
void XS_pack_string(SV* output_sv, string input_string) {
//fprintf(stderr, "in CPPOPS_CPPTYPES XS_pack_string(), top of subroutine\n");
//fprintf(stderr, "in CPPOPS_CPPTYPES XS_pack_string(), received input_string = '%s'\n", input_string.c_str());

	sv_setsv(output_sv, sv_2mortal(newSVpv(input_string.data(), input_string.size())));

//fprintf(stderr, "in CPPOPS_CPPTYPES XS_pack_string(), have output_sv = '%s'\n", SvPV_nolen(output_sv));
//fprintf(stderr, "in CPPOPS_CPPTYPES XS_pack_string(), bottom of subroutine\n");
}

//# endif

// [[[ SEARCH & REPLACE ]]]
// [[[ SEARCH & REPLACE ]]]
// [[[ SEARCH & REPLACE ]]]

# ifdef __PERL__TYPES

/* DISABLE UNTIL COMPLETE, TO AVOID C++ COMPILER WARNINGS
void string_substitute_global(SV* input_string, const string& find_string, const string& replace_string) {
//  string_CHECK(input_string);
    string_CHECKTRACE(input_string, "input_string", "string_substitute_global()");
    // NEED ADD CODE
}
*/

# elif defined __CPP__TYPES

void string_substitute_global(string& input_string, const string& find_string, const string& replace_string) {
    size_t found_position = 0;
    while ((found_position = input_string.find(find_string, found_position)) != string::npos) {
         input_string.replace(found_position, find_string.length(), replace_string);
         found_position += replace_string.length();
    }
}

# endif

// [[[ BOOLEANIFY ]]]
// [[[ BOOLEANIFY ]]]
// [[[ BOOLEANIFY ]]]

# ifdef __PERL__TYPES

SV* string_to_boolean(SV* input_string) {
//  string_CHECK(input_string);
    string_CHECKTRACE(input_string, "input_string", "string_to_boolean()");
    string_substitute_global(input_string, "_", "");  // remove underscores to allow them in input_string
    if (atoi(SvPV_nolen(input_string)) == 0) { return newSViv(0); }
    else { return newSViv(1); }
}

# elif defined __CPP__TYPES

boolean string_to_boolean(string input_string) {
    string_substitute_global(input_string, "_", "");  // remove underscores to allow them in input_string
    if (atoi(input_string.c_str()) == 0) { return 0; }
    else { return 1; }
}

# endif

// [[[ UNSIGNED INTEGERIFY ]]]
// [[[ UNSIGNED INTEGERIFY ]]]
// [[[ UNSIGNED INTEGERIFY ]]]

# ifdef __PERL__TYPES

SV* string_to_unsigned_integer(SV* input_string) {
//  string_CHECK(input_string);
    string_CHECKTRACE(input_string, "input_string", "string_to_unsigned_integer()");
    string_substitute_global(input_string, "_", "");  // remove underscores to allow them in input_string
    if (atoi(SvPV_nolen(input_string)) < 0) { return newSViv(atoi(SvPV_nolen(input_string)) * -1); }
    else { return newSViv(atoi(SvPV_nolen(input_string))); }
}

# elif defined __CPP__TYPES

unsigned_integer string_to_unsigned_integer(string input_string) {
    string_substitute_global(input_string, "_", "");  // remove underscores to allow them in input_string
    if (atoi(input_string.c_str()) < 0) { return (unsigned_integer) (atoi(input_string.c_str()) * -1); }
    else { return (unsigned_integer) atoi(input_string.c_str()); }
}

# endif

// [[[ INTEGERIFY ]]]
// [[[ INTEGERIFY ]]]
// [[[ INTEGERIFY ]]]

# ifdef __PERL__TYPES

SV* string_to_integer(SV* input_string) {
//  string_CHECK(input_string);
    string_CHECKTRACE(input_string, "input_string", "string_to_integer()");
    string_substitute_global(input_string, "_", "");  // remove underscores to allow them in input_string
    return newSViv(atoi(SvPV_nolen(input_string)));
}

# elif defined __CPP__TYPES

integer string_to_integer(string input_string) {
    string_substitute_global(input_string, "_", "");  // remove underscores to allow them in input_string
    return (integer) atoi(input_string.c_str());
}

# endif

// [[[ NUMBERIFY ]]]
// [[[ NUMBERIFY ]]]
// [[[ NUMBERIFY ]]]

# ifdef __PERL__TYPES

SV* string_to_number(SV* input_string) {
//  string_CHECK(input_string);
    string_CHECKTRACE(input_string, "input_string", "string_to_number()");
    string_substitute_global(input_string, "_", "");  // remove underscores to allow them in input_string
    return newSVnv(atof(SvPV_nolen(input_string)));
}

# elif defined __CPP__TYPES

number string_to_number(string input_string) {
    string_substitute_global(input_string, "_", "");  // remove underscores to allow them in input_string
    return (number) atof(input_string.c_str());
}

# endif

// [[[ CHARACTERIFY ]]]
// [[[ CHARACTERIFY ]]]
// [[[ CHARACTERIFY ]]]

# ifdef __PERL__TYPES

/* DISABLE UNTIL COMPLETE, TO AVOID C++ COMPILER WARNINGS
SV* string_to_character(SV* input_string) {
//  string_CHECK(input_string);
    string_CHECKTRACE(input_string, "input_string", "string_to_character()");
    // NEED ADD CODE
}
*/

# elif defined __CPP__TYPES

character string_to_character(string input_string) {
    return (character) input_string.at(0);
}

# endif

// [[[ STRINGIFY ]]]
// [[[ STRINGIFY ]]]
// [[[ STRINGIFY ]]]

# ifdef __PERL__TYPES

SV* string_to_string(SV* input_sv)
{
    string input_sv_string;
    size_t input_sv_string_pos;

//	string_CHECK(input_sv);
	string_CHECKTRACE(input_sv, "input_sv", "string_to_string()");

	input_sv_string = SvPV_nolen(input_sv);

fprintf(stderr, "in CPPOPS_PERLTYPES string_to_string(), received input_sv_string =\n%s\n\n", input_sv_string.c_str());
//fprintf(stderr, "in CPPOPS_PERLTYPES string_to_string()...\n");

	// escape all back-slash \ and single-quote ' characters with a back-slash \ character
	input_sv_string = string(SvPV_nolen(input_sv));
	input_sv_string_pos = 0;
	while((input_sv_string_pos = input_sv_string.find("\\", input_sv_string_pos)) != string::npos)
	{
		input_sv_string.replace(input_sv_string_pos, 1, "\\\\");
		input_sv_string_pos += 2;
	}
	input_sv_string_pos = 0;
	while((input_sv_string_pos = input_sv_string.find("'", input_sv_string_pos)) != string::npos)
	{
		input_sv_string.replace(input_sv_string_pos, 1, "\\'");
		input_sv_string_pos += 2;
	}

	input_sv_string = "'" + input_sv_string + "'";

//fprintf(stderr, "in CPPOPS_PERLTYPES string_to_string(), bottom of subroutine, returning possibly-modified input_sv_string =\n%s\n\n", input_sv_string.c_str());

	return(newSVpvf("%s", input_sv_string.c_str()));
}

# elif defined __CPP__TYPES

string to_string(string input_string) {
    fprintf(stderr, "in CPPOPS_CPPTYPES to_string(), about to call string_to_string & return value...\n");
    return(string_to_string(input_string));
}

string string_to_string(string input_string)
{
fprintf(stderr, "in CPPOPS_CPPTYPES string_to_string(), top of subroutine, received input_string =\n%s\n\n", input_string.c_str());
//fprintf(stderr, "in CPPOPS_CPPTYPES string_to_string()...\n");

    size_t input_string_pos;

	// escape all back-slash \ and single-quote ' characters with a back-slash \ character
	input_string_pos = 0;
	while((input_string_pos = input_string.find("\\", input_string_pos)) != string::npos)
	{
		input_string.replace(input_string_pos, 1, "\\\\");
		input_string_pos += 2;
	}
	input_string_pos = 0;
	while((input_string_pos = input_string.find("'", input_string_pos)) != string::npos)
	{
		input_string.replace(input_string_pos, 1, "\\'");
		input_string_pos += 2;
	}

	input_string = "'" + input_string + "'";

//fprintf(stderr, "in CPPOPS_CPPTYPES string_to_string(), bottom of subroutine, returning possibly-modified input_string =\n%s\n\n", input_string.c_str());

	return(input_string);
}

# endif

// [[[ TYPE TESTING ]]]
// [[[ TYPE TESTING ]]]
// [[[ TYPE TESTING ]]]

# ifdef __PERL__TYPES

SV* string_typetest0() {
//fprintf(stderr, "in CPPOPS_PERLTYPES string_typetest0()\n");
	return(newSVpv("Spice CPPOPS_PERLTYPES", 0));
}

SV* string_typetest1(SV* lucky_string) {
//	string_CHECK(lucky_string);
	string_CHECKTRACE(lucky_string, "lucky_string", "string_typetest1()");
//cout << "in CPPOPS_PERLTYPES string_typetest1(), received lucky_string '" << SvPV_nolen(lucky_string) << "'" << '\n';  cout.flush();  // DEV NOTE: must flush buffer to avoid endl over-flushing and out-of-order printing
//cerr << "in CPPOPS_PERLTYPES string_typetest1(), received lucky_string '" << SvPV_nolen(lucky_string) << "'" << '\n';  // DEV NOTE: cerr doesn't have a buffer to flush
//fprintf(stderr, "in CPPOPS_PERLTYPES string_typetest1(), received lucky_string = '%s'\n", SvPV_nolen(lucky_string));
	return(newSVpvf("%s%s", SvPV_nolen(string_to_string(lucky_string)), " CPPOPS_PERLTYPES"));
}

# elif defined __CPP__TYPES

string string_typetest0() {
	string retval = "Spice CPPOPS_CPPTYPES";
//fprintf(stderr, "in CPPOPS_CPPTYPES string_typetest0(), have retval = '%s'\n", retval.c_str());
	return retval;
}

string string_typetest1(string lucky_string) {
//fprintf(stderr, "in CPPOPS_CPPTYPES string_typetest1(), received lucky_string = '%s'\n", lucky_string.c_str());
	return(string_to_string(lucky_string) + " CPPOPS_CPPTYPES");
}

# else

Purposefully_die_from_a_compile-time_error,_due_to_neither___PERL__TYPES_nor___CPP__TYPES_being_defined.__We_need_to_define_exactly_one!

# endif

#endif