/**
 ** Items.cc - Names of items.
 **
 ** Written: 11/5/98 - JSF
 **/

/*
Copyright (C) 1998  Jeffrey S. Freedman
Copyright (C) 2000-2022  The Exult Team

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; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
*/

#ifdef HAVE_CONFIG_H
#	include <config.h>
#endif

// #include <iomanip>           /* Debugging */
#include "items.h"

#include "U7obj.h"
#include "endianio.h"
#include "exult_flx.h"
#include "fnames.h"
#include "msgfile.h"
#include "utils.h"

#include <fstream>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>

using std::cerr;
using std::endl;
using std::ifstream;
using std::istream;
using std::ofstream;
using std::ostream;
using std::string;
using std::stringstream;
using std::vector;

vector<string> item_names;    // Names of U7 items.
vector<string> text_msgs;     // Msgs. (0x400 - in text.flx).
vector<string>
		misc_names;    // Frames, etc (0x500 - 0x5ff/0x685 (BG/SI) in text.flx).

static inline int remap_index(bool remap, int index, bool sibeta) {
	if (!remap) {
		return index;
	}
	if (sibeta) {
		// Account for differences between SI Beta and SI Final when remapping
		// to SS indices.
		if (index >= 0x146) {
			return index + 17;
		} else if (index >= 0x135) {
			return index + 16;
		} else if (index >= 0x0f6) {
			return index + 15;
		} else if (index >= 0x0ea) {
			return index + 14;
		} else if (index >= 0x0e5) {
			return index + 13;
		} else if (index >= 0x0b1) {
			return index + 11;
		} else if (index >= 0x0ae) {
			return index + 10;
		} else if (index >= 0x0a2) {
			return index + 9;
		} else if (index >= 0x094) {
			return index + 8;
		} else if (index >= 0x08b) {
			return index + 7;
		} else if (index >= 0x07f) {
			return index + 2;
		} else {
			return index;
		}
	} else {
		if (index >= 0x0fa) {
			return index + 11;
		} else if (index >= 0x0b2) {
			return index + 10;
		} else if (index >= 0x0af) {
			return index + 9;
		} else if (index >= 0x094) {
			return index + 8;
		} else if (index >= 0x08b) {
			return index + 7;
		} else if (index >= 0x07f) {
			return index + 2;
		} else {
			return index;
		}
	}
}

static inline const char* get_text_internal(
		const vector<string>& src, unsigned num) {
	return src[num].c_str();
}

static inline void add_text_internal(
		vector<string>& src, unsigned num, const char* name) {
	if (num >= src.size()) {
		src.resize(num + 1);
	}
	src[num] = name;
}

/*
 *  Get how many item names there are.
 */

int get_num_item_names() {
	return item_names.size();
}

/*
 *  Get an item name.
 */
const char* get_item_name(unsigned num) {
	return get_text_internal(item_names, num);
}

/*
 *  Create an item name.
 */
void Set_item_name(unsigned num, const char* name) {
	add_text_internal(item_names, num, name);
}

/*
 *  Get how many text messages there are.
 */

int get_num_text_msgs() {
	return text_msgs.size();
}

/*
 *  Get a text message.
 */
const char* get_text_msg(unsigned num) {
	return get_text_internal(text_msgs, num);
}

/*
 *  Create a text message.
 */
void Set_text_msg(unsigned num, const char* msg) {
	add_text_internal(text_msgs, num, msg);
}

/*
 *  Get how many misc names there are.
 */

int get_num_misc_names() {
	return misc_names.size();
}

/*
 *  Get a misc name.
 */
const char* get_misc_name(unsigned num) {
	return get_text_internal(misc_names, num);
}

/*
 *  Create a misc name.
 */
void Set_misc_name(unsigned num, const char* name) {
	add_text_internal(misc_names, num, name);
}

/*
 *  Set up names of items.
 *
 *  Msg. names start at 0x400.
 *  Frame names start at entry 0x500 (reagents,medallions,food,etc.).
 */

static void Setup_item_names(
		istream& items, istream& msgs, bool si, bool expansion, bool sibeta) {
	vector<string> msglist;
	int            first_msg;    // First in exultmsg.txt.  Should
	//   follow those in text.flx.
	int total_msgs     = 0;
	int num_item_names = 0;
	int num_text_msgs  = 0;
	int num_misc_names = 0;

	items.seekg(0x54);
	int flxcnt = little_endian::Read4(items);
	first_msg = num_item_names = flxcnt;
	if (flxcnt > 0x400) {
		num_item_names = 0x400;
		num_text_msgs  = flxcnt - 0x400;
		if (flxcnt > 0x500) {
			num_text_msgs  = 0x100;
			num_misc_names = flxcnt - 0x500;
			int last_name;    // Discard all starting from this.
			if (si) {
				last_name = 0x686;
			} else {
				last_name = 0x600;
			}
			if (flxcnt > last_name) {
				num_misc_names = last_name - 0x500;
				flxcnt         = last_name;
			}
		}
		total_msgs = num_text_msgs;
	}
	if (msgs.good()) {
		// Exult msgs. too?
		first_msg = Read_text_msg_file(msgs, msglist);
		if (first_msg >= 0) {
			first_msg -= 0x400;
			if (first_msg < num_text_msgs) {
				cerr << "Exult msg. # " << first_msg
					 << " conflicts with 'text.flx'" << endl;
				first_msg = num_text_msgs;
			}
			total_msgs = static_cast<int>(msglist.size() - 0x400);
		} else {
			first_msg = num_text_msgs;
		}
	}
	item_names.resize(num_item_names);
	text_msgs.resize(total_msgs);
	misc_names.resize(num_misc_names);
	// Hack alert: move SI misc_names around to match those of SS.
	const bool doremap = si && (!expansion || sibeta);
	if (doremap) {
		flxcnt -= 17;    // Just to be safe.
	}
	int i;
	for (i = 0; i < flxcnt; i++) {
		items.seekg(0x80 + i * 8);
		const int itemoffs = little_endian::Read4(items);
		if (!itemoffs) {
			continue;
		}
		const int itemlen = little_endian::Read4(items);
		items.seekg(itemoffs);
		char* newitem = new char[itemlen];
		items.read(newitem, itemlen);
		if (i < num_item_names) {
			item_names[i] = newitem;
		} else if (i - num_item_names < num_text_msgs) {
			if (sibeta && (i - num_item_names) >= 0xd2) {
				text_msgs[i - num_item_names + 1] = newitem;
			} else {
				text_msgs[i - num_item_names] = newitem;
			}
		} else {
			misc_names[remap_index(
					doremap, i - num_item_names - num_text_msgs, sibeta)]
					= newitem;
		}
		delete[] newitem;
	}
	for (i = first_msg; i < total_msgs; i++) {
		text_msgs[i] = msglist[i + 0x400];
	}
}

#define SHAPES_SECT "shapes"
#define MSGS_SECT   "msgs"
#define MISC_SECT   "miscnames"

/*
 *  This sets up item names and messages from Exult's new file,
 *  "textmsgs.txt".
 */

static void Setup_text(
		istream& txtfile,    // All text.
		istream& exultmsg) {
	// Start by reading from exultmsg
	vector<string> msglist;
	int            first_msg;
	first_msg                 = Read_text_msg_file(exultmsg, msglist);
	const unsigned total_msgs = static_cast<int>(msglist.size() - 0x400);
	if (first_msg >= 0) {
		first_msg -= 0x400;
	}
	text_msgs.resize(total_msgs);
	for (unsigned i = first_msg; i < total_msgs; i++) {
		text_msgs[i] = msglist[i + 0x400];
	}
	// Now read in textmsg.txt
	Read_text_msg_file(txtfile, item_names, SHAPES_SECT);
	Read_text_msg_file(txtfile, text_msgs, MSGS_SECT);
	Read_text_msg_file(txtfile, misc_names, MISC_SECT);
}

/*
 *  Setup item names and text messages.
 */

void Setup_text(bool si, bool expansion, bool sibeta) {
	Free_text();
	const bool is_patch = is_system_path_defined("<PATCH>");
	// Always read from exultmsg.txt
	// TODO: allow multilingual exultmsg.txt files.
	std::unique_ptr<istream> exultmsg;
	if (is_patch && U7exists(PATCH_EXULTMSG)) {
		exultmsg = U7open_in(PATCH_EXULTMSG, true);
	} else {
		auto           exultmsgbuf = std::make_unique<stringstream>();
		const char*    msgs        = BUNDLE_CHECK(BUNDLE_EXULT_FLX, EXULT_FLX);
		const U7object txtobj(msgs, EXULT_FLX_EXULTMSG_TXT);
		size_t         len;
		auto           txt = txtobj.retrieve(len);
		if (txt && len > 0) {
			exultmsgbuf->str(string(reinterpret_cast<char*>(txt.get()), len));
		}
		exultmsg = std::move(exultmsgbuf);
	}

	// Exult new-style messages?
	if (is_patch && U7exists(PATCH_TEXTMSGS)) {
		auto pTxtfile = U7open_in(PATCH_TEXTMSGS, true);
		if (!pTxtfile) {
			return;
		}
		auto& txtfile = *pTxtfile;
		Setup_text(txtfile, *exultmsg);
	} else if (U7exists(TEXTMSGS)) {
		auto pTxtfile = U7open_in(TEXTMSGS, true);
		if (!pTxtfile) {
			return;
		}
		auto& txtfile = *pTxtfile;
		Setup_text(txtfile, *exultmsg);
	} else {
		std::unique_ptr<istream> pTextflx;
		if (is_patch && U7exists(PATCH_TEXT)) {
			pTextflx = U7open_in(PATCH_TEXT);
		} else {
			pTextflx = U7open_in(TEXT_FLX);
		}
		if (!pTextflx) {
			return;
		}
		auto& textflx = *pTextflx;
		Setup_item_names(textflx, *exultmsg, si, expansion, sibeta);
	}
}

/*
 *  Free memory.
 */

static void Free_text_list(vector<string>& items) {
	items.clear();
}

void Free_text() {
	Free_text_list(item_names);
	Free_text_list(text_msgs);
	Free_text_list(misc_names);
}

/*
 *  Write out new-style Exult text file.
 */

void Write_text_file() {
	auto pOut = U7open_out(PATCH_TEXTMSGS, true);    // (It's a text file.)
	if (!pOut) {
		return;
	}
	auto& out = *pOut;
	out << "Exult " << VERSION << " text message file."
		<< "  Written by ExultStudio." << endl;
	Write_msg_file_section(out, SHAPES_SECT, item_names);
	Write_msg_file_section(out, MSGS_SECT, text_msgs);
	Write_msg_file_section(out, MISC_SECT, misc_names);
}
