// Emacs style mode select   -*- C++ -*-
//-----------------------------------------------------------------------------
//
// $Id: 4a7ed23193d1113ea5c4b04021367e6218bf0ce4 $
//
// Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 2006-2026 by The Odamex Team.
//
// 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 2
// 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.
//
// DESCRIPTION:
//		Default Config File.
//		1/5/12: JSON File Functions
//
//-----------------------------------------------------------------------------


#include "odamex.h"


#include "c_dispatch.h"
#include "m_argv.h"
#include "m_fileio.h"
#include "m_misc.h"
#include "i_system.h"
#include "z_zone.h"
#include "sv_main.h"
#include "sv_master.h"


// Used to identify the version of the game that saved
// a config file to compensate for new features that get
// put into newer configfiles.
static CVAR (configver, CONFIGVERSIONSTR, "", CVARTYPE_STRING, CVAR_ARCHIVE | CVAR_NOENABLEDISABLE)

/**
 * Get configuration file path.  This file contains commands to set all
 * archived cvars, bind commands to keys, and set other general game
 * information.
 *
 * @author Randy Heit
 * @return The filename of the configuration file path.
 */
std::string M_GetConfigPath(void)
{
	const char *p = Args.CheckValue("-config");

	if (p)
		return p;

	return M_GetUserFileName("odasrv.cfg");
}

// [RH] Don't write a config file if M_LoadDefaults hasn't been called.
bool DefaultsLoaded;

/**
 * Save a configuration file.
 *
 * @author Randy Heit
 * @param Optional: The filename to save the current configuration to.
 */
void STACK_ARGS M_SaveDefaults(std::string filename)
{
	if (!DefaultsLoaded)
		return;

	std::string configfile;
	if (!filename.empty())
	{
		M_AppendExtension(filename, ".cfg", true);
		configfile = filename;
	}
	else
	{
		configfile = M_GetConfigPath();
	}

	// Make sure the user hasn't changed configver
	configver.Set(CONFIGVERSIONSTR);

	if (auto f = uqFile(fopen(configfile.c_str(), "w")))
	{
		fmt::print(f.get(), "// Generated by Odasrv " DOTVERSIONSTR "\n\n");

		// Archive all cvars marked as CVAR_ARCHIVE
		fmt::print(f.get(), "// --- Console variables ---\n\n");
		cvar_t::C_ArchiveCVars(f.get());

		// Archive all aliases
		fmt::print(f.get(), "\n// --- Aliases ---\n\n");
		DConsoleAlias::C_ArchiveAliases(f.get());

		PrintFmt(PRINT_HIGH, "Configuration saved to {}.\n", configfile);
	}
}

BEGIN_COMMAND (savecfg)
{
	if (argc > 1)
		M_SaveDefaults(argv[1]);
	else
		M_SaveDefaults();
}
END_COMMAND (savecfg)

extern int cvar_defflags;
EXTERN_CVAR (dimamount)

/**
 * Load a configuration file from the default configuration file.
 *
 * @author Randy Heit
 */
void M_LoadDefaults(void)
{
	std::string cmd = "exec " + C_QuoteString(M_GetConfigPath());

	cvar_defflags = CVAR_ARCHIVE;
	AddCommandString(cmd.c_str());
	cvar_defflags = 0;

	if (configver < 118.0f)
	{
		AddCommandString("alias idclev map ");
		AddCommandString("alias changemap map ");
		AddCommandString("alias ? help ");
	}

	DefaultsLoaded = true;
}

// JSON Utility Functions (based on those from EECS)

// Reads a file in JSON format
bool M_ReadJSON(Json::Value &json, const std::string& filename)
{
	byte* buffer = NULL;
	std::string data;
	Json::Reader reader;
	uint64_t length;

	if (!(M_FileExists(filename)))
		return false;

	length = M_ReadFile(filename, &buffer);
	if (length > 0 && buffer)
	{
		const char* start = reinterpret_cast<const char*>(buffer);
		const char* end = reinterpret_cast<const char*>(&buffer[length]);

		bool res = reader.parse(start, end, json);

		Z_Free(buffer);

		if (res == false)
		{
			PrintFmt(PRINT_HIGH,"M_ReadJSONFromFile: Error parsing JSON: {}.\n",
			         reader.getFormattedErrorMessages());
			return false;
		}

		return true;
	}

	return false;
}

// Writes a file in JSON format.  Third param is true if the output
// should be pretty-printed.
bool M_WriteJSON(const char* filename, Json::Value &value, bool styled)
{
	std::ofstream out_file;
	Json::FastWriter fast_writer;
	Json::StyledWriter styled_writer;

	out_file.open(filename);

	if (styled)
	{
		out_file << styled_writer.write(value);
	}
	else
	{
		out_file << fast_writer.write(value);
	}

	out_file.close();

	if (out_file.fail())
	{
		return false;
	}
	return true;
}

VERSION_CONTROL (m_misc_cpp, "$Id: 4a7ed23193d1113ea5c4b04021367e6218bf0ce4 $")
