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

#include <string>

#include "base_line_provider.h"
#include "i_log_lines.h"
#include "run.h"
#include "zcat.h"

#include "contrib/izstream.hpp"

using namespace std;

/* a line provider based around a file. takes a file name and reads it, produces
 * its contents as lines to the LogLines. when it gets to the end of the file,
 * it signals eof() to loglines. it keeps tailing the file though and if new
 * lines come it unsets the eof() and keeps adding them */
class FileLineProvider : public BaseLineProvider {
public:
	FileLineProvider(ILogLines* ll, const string& filename)
		: BaseLineProvider(ll), _filename(filename) {}
	virtual ~FileLineProvider() {}

protected:
	void loader() override {
		/* the file being read */
		unique_ptr<ifstream> basefile;
		/* the stream being read in case we are decompressing the file
		 */
		unique_ptr<istream> fin;
		fin.reset(new ifstream(_filename));

		/* read the file line of the file. if it is a gzip file, reopen
		 * with gzip reader. otherwise reset to the top
		 */
		string line;
		getline(*fin.get(), line);
		if (ZCat::magic(line)) {
			basefile.reset(new ifstream(_filename));
			fin.reset(new zstream::igzstream(*basefile.get()));
		} else {
			fin->clear();
			fin->seekg(0, ios::beg);
		}

		bool first_run = true;
		// counter of number of times we didn't get any new lines
		int num_checks = 0;

		while (!exit()) {
			while (!exit() && getline(*fin.get(), line)) {
				queue_line(line);
				if (!first_run) unset_eof();
				num_checks = 0;
			}
			flush_lines();
			/* the first time we think we are EOF believe it. if we
			 * are wrong then continue to be skeptical */
			if (first_run) eof();
			first_run = false;

			if (num_checks == 100) {
				// 10s passed without updates. lets believe eof
				// again.
				eof();
				// turn of checking
				++num_checks;
			} else if (num_checks < 100) {
				++num_checks;
			}
			this_thread::sleep_for(chrono::milliseconds(100));
			fin->clear();
		}
	}

	/* the file we are serving */
	string _filename;
};

#endif // __FD_LINE_PROVIDER__H__
