/*
 * 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 __DIRECTORY_LINE_PROVIDER__H__
#define __DIRECTORY_LINE_PROVIDER__H__

#include <filesystem>
#include <fstream>
#include <optional>
#include <string>
#include <unordered_map>

#include "base_line_provider.h"
#include "i_log_lines.h"

using namespace std;

/* directory line provider is used to display media files in a directory to
 * allow logserver to function like a media server
 */
class DirectoryLineProvider : public BaseLineProvider {
public:
	DirectoryLineProvider(ILogLines* ll, const string& dir)
		: BaseLineProvider(ll), _dir(dir) {}
	virtual ~DirectoryLineProvider() {}

	virtual string get_line(size_t pos) override {
		if (pos >= _files.size()) return "";
		return _files.at(pos);
	}

protected:
	/* load the files in the passed directory recursively and put an 'x'
	 * next to the ones that have an entry in the mediaserver file */
	void loader() override {
		set<string> sorted;
		for (const auto& entry : filesystem::recursive_directory_iterator(_dir)) {
			sorted.insert(entry.path());
		}
		for (const auto& x : sorted) {
			process(x);
		}
		ifstream fin(_dir + "/.mediaserver");
		string s;
		while (getline(fin, s)) {
			if (_file_to_index.count(s)) {
				_display[_file_to_index[s]][0] = 'x';
			}
		}
		commit();
	}

	void commit() {
		for (const auto& x : _display) {
			add_line(x);
		}
	}

	void process(const string& file) {
		// TODO: only in mediaserver mode
		if (!is_media(file)) return;

		_file_to_index[file] = _files.size();
		_files.emplace_back(file);
		string line = tidy(file);
		_display.emplace_back(tidy(file));
	}

	static string tidy(const string& file) {
		string ret = "  ";
		bool off = false;
		optional<char> last;
		for (size_t i = 0; i < file.size(); ++i) {
			if (file[i] == '[') off = true;
			else if (file[i] == ']') off = false;
			else if (!off) {
				if (last && (*last == '/' || punctuation(*last))
				    && file[i] == ' '); //nop
				else if (punctuation(file[i])) ret += ' ';
				else ret += file[i];
				last = file[i];
			}
		}

		return ret;
	}

	static bool punctuation(char c) {
		static set<char> chars = {'.', '-', '_', ' '};
		return chars.count(c);
	}

	static bool is_media(const string& file) {
		size_t n = file.size();
		if (n < 4) return false;
		if (file.substr(n - 4) == ".mkv") return true;
		if (file.substr(n - 4) == ".avi") return true;
		if (file.substr(n - 4) == ".mp4") return true;
		return false;
	}

	string _dir;

	vector<string> _files;
	vector<string> _display;
	unordered_map<string, size_t> _file_to_index;
};

#endif // __DIRECTORY_LINE_PROVIDER__H__
