/*
Opis:
Narzędzie służące do tagowania tokenów (zawierających tag WSD) w korpusie tekstu pojęciami sumo.
Jeśli do tokenu przypisany jest tag np. <prop key="sense:wsd_zmiana">zmiana-1</prop> oraz owy
synset znajduje się w pliku podawanym na wejściu, zawierającym synsety zmapowane na pojęcia sumo,
to zostanie on otagowany pojęciem sumo np. <prop key="plwn2sumo:map">subsumed:Process</prop>.

Przykład uruchomienia:
./swa \
    ~/Korpusy/kpwr-1.1/index_wsd.txt \
    ~/Korpusy/kpwr-1.1 \
    ~/Sources/plwn_pwn_sumo.csv

1 parametr: ścieżka do pliku ze spisem wszystkich plików tekstu w korpusie
2 parametr: ścieżka do katalogu z korpusem tekstu
3 parametr: ścieżka do pliku z synsetami zmapowanymi na pojęcia sumo
*/


#include <libcorpus2/tagsetmanager.h>
#include <libcorpus2/io/xcesreader.h>
#include <libcorpus2/tagging.h>
#include <libcorpus2/io/cclwriter.h>
#include <libcorpus2/sentence.h>

#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/algorithm/string/classification.hpp>

#include <string>

#include <iostream>
#include <fstream>

#include <map>
#include <vector>

#include <mysql/mysql.h>

using namespace std;


void show_usage(char **argv)
{
	cout << "Uzycie: \n";
	cout << argv[0] << " INPUT_FILE INPUT_DIR INPUT_SUMO_FILE\n\n";

	cout << "Przyklad uzycia: \n";
	cout << argv[0] << "\\ \n";
	cout << "    ~/Korpusy/kpwr-1.1/index_wsd.txt \\ \n";
	cout << "    ~/Korpusy/kpwr-1.1/ \\ \n";
	cout << "    ~/Sources/plwn_pwn_sumo.csv \n\n";

	cout << "Error: Wszystkie argumenty sa wymagane. \n";
}


map<string, vector<string> > make_annotation_map()
{
	map<string, vector<string> > annMap;

	MYSQL *connection;
	MYSQL_RES *result;
	MYSQL_ROW row;

	connection = mysql_init(NULL);

	mysql_options(connection, MYSQL_SET_CHARSET_NAME, "utf8");
	mysql_options(connection, MYSQL_INIT_COMMAND, "SET NAMES utf8");

	mysql_real_connect(connection, "156.17.10.92", "wordnet", "VfazYsAc9ER8Qjrr",
		"wordnet_work", 3306, NULL, 0);

	mysql_query(connection,
		"SELECT wordnet_work.synset.comment, wordnet_work.synset.ID " \
		"FROM wordnet_work.synset " \
		"WHERE wordnet_work.synset.comment LIKE '%wsd%';");
	result = mysql_store_result(connection);


	while(row = mysql_fetch_row(result)) {
		vector<string> spl_key_row0;
		boost::split(spl_key_row0, row[0], boost::algorithm::is_any_of(";"));

		vector<string> spl_key;
		boost::split(spl_key, spl_key_row0[0], boost::algorithm::is_any_of(","));

		for(int i=0; i<spl_key.size(); i++) {
			boost::replace_all(spl_key[i], "WSD", "");
			boost::replace_all(spl_key[i], " ", "");
			boost::replace_all(spl_key[i], "#", "-");

			if(annMap.find(spl_key[i]) != annMap.end()) {
				annMap[spl_key[i]].push_back(row[1]);
			}
			else {
				vector<string> newValue;
				newValue.push_back(row[1]);
				annMap[spl_key[i]] = newValue;
			}
		}
	}

	return annMap;
}


map <int, vector<string> > make_sumo_map(string sumoFileName) {
	map<int, vector<string> > sumoMap;
	sumoMap.clear();

	ifstream fileSumo;
	fileSumo.open(sumoFileName.c_str());
	string line;
	while(getline(fileSumo, line)) {
		vector<string> lineVector;
		boost::split(lineVector, line, boost::algorithm::is_any_of(";"));

		int synsetID = atoi(lineVector[2].c_str());
		string relationANDconcept = lineVector.back() + ":" + lineVector[8];

		if(sumoMap.find(synsetID) != sumoMap.end()) {
			bool isNew = true;
			for(int i=0; i<sumoMap[synsetID].size(); i++) {
				if(sumoMap[synsetID][i].compare(relationANDconcept) == 0) {
					isNew = false;
					break;
				}
			}
			if(isNew)
				sumoMap[synsetID].push_back(relationANDconcept);
		}
		else {
			vector<string> newValue;
			newValue.push_back(relationANDconcept);
			sumoMap[synsetID] = newValue;
		}
	}

	return sumoMap;
}


int main(int argc, char *argv[])
{
	if(argc < 4) {
		show_usage(argv);
		return -1;
	}



	const Corpus2::Tagset tagset = Corpus2::get_named_tagset("nkjp");

	//stworzenie mapy, która mapuje synsety z komentarzy
	cout << "Making annotation dictionary...\n";
	map<string, vector<string> > annMap = make_annotation_map();

	//stworzenie mapy synsetów i odpowiadających im pojęć sumo
	//bazując na pliku wejściowym zawierającym pojęcia sumo
	string sumoFileName = argv[3];
	cout << "Making sumo dictionary...\n";
	map<int, vector<string> > sumoMap = make_sumo_map(sumoFileName);



	std::cout << "Adding tags...\n";
	ifstream fileIndex;
	fileIndex.open(argv[1]);

	string line = "";
	while(getline(fileIndex, line)) {
		string pathToFile = argv[2];
		pathToFile.append(line);

		boost::shared_ptr<Corpus2::TokenReader> reader =
			Corpus2::TokenReader::create_path_reader
			("ccl,disamb_only", tagset, pathToFile);

		boost::shared_ptr<Corpus2::TokenWriter> writer =
			Corpus2::TokenWriter::create_path_writer
			("ccl", pathToFile.append(".sumo"), tagset);

		boost::shared_ptr<Corpus2::Chunk> chunk;

		while(chunk = reader->get_next_chunk()) {
			const std::vector<boost::shared_ptr
				<Corpus2::Sentence> > &sentencesInChunk =
				chunk->sentences();

			for(unsigned s=0; s<sentencesInChunk.size(); s++) {
				boost::shared_ptr<Corpus2::Sentence> sentence =
					sentencesInChunk[s];
				const std::vector<Corpus2::Token *> &tokensInSentence =
					sentence->tokens();

				for(unsigned tkn=0; tkn<tokensInSentence.size(); tkn++) {
					Corpus2::Token *token = tokensInSentence[tkn];

					boost::shared_ptr<Corpus2::TokenMetaData> metaData =
						token->get_metadata();

					if(metaData) {
						const std::map<std::string, std::string> &attrMap =
							token->get_metadata()->attributes();

						for(std::map<std::string, std::string>::const_iterator
							attrIt = attrMap.begin();
							attrIt != attrMap.end();
							++attrIt
						) {
							if(0 == attrIt->first.find("sense:wsd_")) {
								if(annMap.find(attrIt->second) == annMap.end()) {
									cout << "Lack of key ";
									cout << attrIt->second;
									cout << " in dictionary!\n";
									continue;
								}

								int senseID =
									atoi(annMap.find(attrIt->second)->second[0].c_str());

								if(sumoMap.find(senseID) == sumoMap.end()) {
									cout << "Lack of sumo concept!\n";
									continue;
								}

								string s = "";
								for(int i=0; i<sumoMap.find(senseID)->second.size(); i++)
									s.append(sumoMap.find(senseID)->second[i]).append(";");
								string sumo = s.substr(0, s.size()-1);

								token->get_metadata()->set_attribute("sumo_map", sumo);
							}
						}
					}
				}
			}
			writer->write_chunk(*chunk);
		}
	}

	std::cout << "Done...\n";
	return 0;
}
