// given a valid XSTR() tag piece of text, extract the string portion, return it in out, nonzero on success
int lcl_ext_get_text(const SCP_string &xstr, SCP_string &out)
{
	size_t open_quote_pos, close_quote_pos;

	// this is some crazy wack-ass code.
	// look for the open quote
	open_quote_pos = xstr.find('\"');
	if (open_quote_pos == SCP_string::npos) {
		error_display(0, "Error parsing XSTR() tag %s\n", xstr.c_str());
		return 0;
	}

	// look for the close quote
	close_quote_pos = xstr.find('\"', open_quote_pos+1);
	if (close_quote_pos == SCP_string::npos) {
		error_display(0, "Error parsing XSTR() tag %s\n", xstr.c_str());
		return 0;
	}

	// now that we know the boundaries of the actual string in the XSTR() tag, copy it
	out.assign(xstr, open_quote_pos + 1, close_quote_pos - open_quote_pos - 1);

	// success
	return 1;
}
static void handle_includes_impl(SCP_vector<SCP_string>& include_stack,
								 SCP_stringstream& output,
								 int& include_counter,
								 const SCP_string& filename,
								 const SCP_string& original) {
	include_stack.emplace_back(filename);
	auto current_source_number = include_counter + 1;

	const char* INCLUDE_STRING = "#include";
	SCP_stringstream input(original);

	int line_num = 1;
	for (SCP_string line; std::getline(input, line);) {
		auto include_start = line.find(INCLUDE_STRING);
		if (include_start != SCP_string::npos) {
			auto first_quote = line.find('"', include_start + strlen(INCLUDE_STRING));
			auto second_quote = line.find('"', first_quote + 1);

			if (first_quote == SCP_string::npos || second_quote == SCP_string::npos) {
				Error(LOCATION,
					  "Shader %s:%d: Malformed include line. Could not find both quote charaters.",
					  filename.c_str(),
					  line_num);
			}

			auto file_name = line.substr(first_quote + 1, second_quote - first_quote - 1);
			auto existing_name = std::find_if(include_stack.begin(), include_stack.end(), [&file_name](const SCP_string& str) {
				return str == file_name;
			});
			if (existing_name != include_stack.end()) {
				SCP_stringstream stack_string;
				for (auto& name : include_stack) {
					stack_string << "\t" << name << "\n";
				}

				Error(LOCATION,
					  "Shader %s:%d: Detected cyclic include! Previous includes (top level file first):\n%s",
					  filename.c_str(),
					  line_num,
					  stack_string.str().c_str());
			}

			++include_counter;
			// The second parameter defines which source string we are currently working with. We keep track of how many
			// excludes have been in the file so far to specify this
			output << "#line 1 " << include_counter + 1 << "\n";

			handle_includes_impl(include_stack,
								 output,
								 include_counter,
								 file_name,
								 opengl_load_shader(file_name.c_str()));

			// We are done with the include file so now we can return to the original file
			output << "#line " << line_num + 1 << " " << current_source_number << "\n";
		} else {
			output << line << "\n";
		}

		++line_num;
	}

	include_stack.pop_back();
}
// given a valid XSTR() tag piece of text, extract the id# portion, return the value in out, nonzero on success
int lcl_ext_get_id(const SCP_string &xstr, int *out)
{
	char id_buf[10];
	size_t p, pnext;

	// find the first quote
	p = xstr.find('\"');
	if (p == SCP_string::npos) {
		error_display(0, "Error parsing id# in XSTR() tag %s\n", xstr.c_str());
		return 0;
	}
	p++;

	// continue searching until we find the close quote
	while(1) {
		pnext = xstr.find('\"', p);
		if (pnext == SCP_string::npos) {
			error_display(0, "Error parsing id# in XSTR() tag %s\n", xstr.c_str());
			return 0;
		}

		// if the previous char is a \, we know its not the "end-of-string" quote
		if (xstr[pnext - 1] != '\\') {
			p = pnext;
			break;
		}

		// continue
		p = pnext;
	}

	// search until we find a ,	
	pnext = xstr.find(',', p);
	if (pnext == SCP_string::npos) {
		error_display(0, "Error parsing id# in XSTR() tag %s\n", xstr.c_str());
		return 0;
	}
	pnext++;

	// find the close parenthesis
	p = pnext;
	pnext = xstr.find(')', p);
	if (pnext == SCP_string::npos) {
		error_display(0, "Error parsing id# in XSTR() tag %s\n", xstr.c_str());
		return 0;
	}
	pnext--;

	// get only the number
	while (is_white_space(xstr[p]) && p <= pnext)
		p++;
	while (is_white_space(xstr[pnext]) && p <= pnext)
		pnext--;
	if (p > pnext) {
		error_display(0, "Error parsing id# in XSTR() tag %s\n", xstr.c_str());
		return 0;
	}

	// now get the id string
	if ((pnext - p + 1) > 9) {
		error_display(0, "Error parsing id# in XSTR() tag %s\n", xstr.c_str());
		return 0;
	}
	memset(id_buf, 0, 10);
	xstr.copy(id_buf, pnext - p + 1, p);

	// get the value and we're done
	*out = atoi(id_buf);

	// success
	return 1;
}