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

using namespace std;

#define FOOTNOTE_HEADER			"@footnote:"
#define FOOTNOTE_START			'['
#define FOOTNOTE_END			']'
#define FOOTNOTE_ALLOWEDCHARS	"0123456789"

// not very clean, but fast...
#define to_string(from, to, stringstream)	\
				stringstream.clear();		\
				stringstream.str("");		\
				stringstream << from;		\
				to = stringstream.str();
#define from_string(from, to, stringstream) \
				stringstream.clear();		\
				stringstream.str(from);		\
				stringstream >> to;

int main(int argc, char ** argv) {
	int					FOOTNOTE_HEADER_LEN			= strlen(FOOTNOTE_HEADER);
	int					FOOTNOTE_ALLOWEDCHARS_LEN	= strlen(FOOTNOTE_ALLOWEDCHARS);

	ifstream			input(argv[1]);
	string				line;				// ... for reading from file
	string				strold;				// old footnote as string
	string				strnew;				// new footnote as string
	string::size_type	pos1	= 0;
	string::size_type	pos2	= 0;
	bool				found	= false;	// indicates whether we've found the footnote header
	size_t				last	= 0;		// last used footnote
	size_t				old		= 0;		// old footnote as number
	std::vector<size_t>	mapping(1024, 0);	// mapping between old and new footnote numbers
	stringstream		ss;					// for conversions (string <-> size_t)
	
	// process input until footnote header has been found
	while (!found && getline(input, line)) {

		// check for footnote header
		if (line.compare(0, FOOTNOTE_HEADER_LEN, FOOTNOTE_HEADER) == 0) {
			found = true;
			cout << line << endl;
			continue;
		}

		// search footnotes
		pos1 = 0;
		while ( (pos1 = line.find_first_of(FOOTNOTE_START,	pos1)) != string::npos) {
			pos2 = line.find_first_not_of(FOOTNOTE_ALLOWEDCHARS, pos1 + 1, FOOTNOTE_ALLOWEDCHARS_LEN);

			// skip invalid footnote references
			if (pos2 == string::npos || pos2 < pos1 + 2 || line[pos2] != FOOTNOTE_END) {
				pos1 = pos2;
				continue;
			}

			// we found a valid footnote reference
			strold = line.substr(pos1 + 1, pos2 - pos1 - 1);
			from_string(strold, old, ss);

			// resize vector if it is to small
			if (old + 1 > mapping.size()) mapping.resize(old + 1, 0);

			if (mapping[old] != 0) {
				// known footnote -> use stored number
				to_string(mapping[old], strnew, ss);
			}
			else {
				// unknown footnote -> new number
				last++;
				mapping[old] = last;
				to_string(last, strnew, ss);
			}

			// replace footnote number
			line.replace(pos1 + 1, pos2 - pos1 - 1, strnew);
			pos1 = pos2 - strold.size() + strnew.size();
		} 
		cout << line << endl;
	}


	// process foot notes

	std::vector<std::string> storage(last, string());

	while (getline(input, line)) {
		
		// print empty lines directly
		if (line.empty()) { cout << endl; continue; }

		// skip lines without a footnote
		if (line[0] != FOOTNOTE_START) continue;
		pos1 = line.find_first_not_of(FOOTNOTE_ALLOWEDCHARS, 1, FOOTNOTE_ALLOWEDCHARS_LEN);
		if (pos1 == string::npos || line[pos1] != FOOTNOTE_END) continue;

		// store footnote
		strold = line.substr(1, pos1 - 1);
		from_string(strold, old, ss);
		if (old + 1> mapping.size() || mapping[old] == 0) {
			// skip unknown (i.e. unreferenced) footnotes
			cerr << "unreferenced footnote " << old << endl;
			continue;
		}
		storage[mapping[old] - 1] = line.substr(pos1 + 1);
	}

	// print sorted list of footnotes
	for (size_t i = 0; i < storage.size(); i++) {
		if (storage[i].empty()) cerr << "missing footnote " << i + 1 << endl;
		cout << FOOTNOTE_START << i + 1 << FOOTNOTE_END << storage[i] << endl;
	}
}
