/*
 * Logserver
 * Copyright (C) 2017-2025 Joel Reardon
 *
 * 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 3 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, see <https://www.gnu.org/licenses/>.
 */

#ifndef __XREF__H__
#define __XREF__H__

#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>

#include <cassert>
#include <iostream>
#include <filesystem>
#include <fstream>
#include <map>
#include <memory>
#include <string>

using namespace std;

/* reads lines to see if they are actionable links */
class XRef {
public:
	static bool grep_line(const string_view& line,
			      string* file,
			      size_t* lineno) {
		int matched = Tokenizer::extract("%:%:%", line, file, lineno,
						 nullptr);
		if (*lineno == 0) return false;

		// grep counts from one
		--*lineno;
		if (matched < 2) return false;
		if (filesystem::exists(*file)) return true;
		return false;
	}

	static bool storytime_line(const string_view& line,
				   string* file,
				   size_t* lineno) {
		if (!line.length()) return false;
		if (line[0] != '@') return false;
		size_t last = line.find_last_of(':');
		if (last == string::npos || last == 1) {
			return false;
		}
		try {
			*lineno = stoi(string(line.substr(last + 1)));
		} catch (...) {
			return false;
		}

		*file = line.substr(1, last - 1);
		if (filesystem::exists(*file)) return true;
		return false;
	}

	static bool link_line(const string_view& line, string* file, size_t* lineno) {
		int matched = Tokenizer::extract("%@%:% %", line, nullptr,
						 file, lineno, nullptr);
		return matched >= 3;
	}

	static bool ctags_line(const string& filename,
			       const string_view& line, string* file,
			       size_t* lineno) {
		string item, target, ex;
		int ret = Tokenizer::extract("%\t%\t%", line, &item, &target, &ex);
		if (ret < 3) return false;
		if (item.starts_with("!_TAG_")) return false;

		filesystem::path p{filename};
		filesystem::path dir = p.parent_path();

		if (!filesystem::exists(dir / target)) return false;
		*file = dir / target;
		if (!ex.starts_with("/^") || ex.find("$/;\"") == string::npos) {
			// TODO handle these if ever relevant
			return false;
		}
		ex = ex.substr(2, ex.find("$/;\"") - 2);
		ifstream fin(*file);
		string curline;
		size_t i = 0;
		while (getline(fin, curline)) {
			if (curline == ex) {
				*lineno = i;
				break;
			}
			++i;
		}
		return true;
	}
};

#endif  // __XREF_CONTEXT__H__
