#ifndef ECODE_APPCONFIG_HPP
#define ECODE_APPCONFIG_HPP

#include <eepp/config.hpp>
#include <eepp/math/size.hpp>
#include <eepp/system/inifile.hpp>
#include <eepp/ui/css/stylesheetlength.hpp>
#include <eepp/ui/tools/uicodeeditorsplitter.hpp>
#include <eepp/ui/uicodeeditor.hpp>
#include <eepp/window/window.hpp>

#include <eterm/terminal/terminaltypes.hpp>
#include <eterm/ui/uiterminal.hpp>

#include <nlohmann/json_fwd.hpp>

using namespace EE;
using namespace EE::Math;
using namespace EE::UI;
using namespace EE::UI::CSS;
using namespace EE::UI::Tools;
using namespace EE::System;
using namespace EE::Window;
using namespace eterm::Terminal;
using namespace eterm::UI;

namespace ecode {
class App;
class PluginManager;

enum class PanelPosition { Left, Right };

struct UIConfig {
	StyleSheetLength fontSize{ 11, StyleSheetLength::Dp };
	StyleSheetLength panelFontSize{ 11, StyleSheetLength::Dp };
	bool showSidePanel{ true };
	bool showStatusBar{ true };
	bool showMenuBar{ false };
	bool welcomeScreen{ true };
	bool openFilesInNewWindow{ false };
	bool openProjectInNewWindow{ false };
	bool nativeFileDialogs{ false };
	bool imagesQuickPreview{ false };
	bool editorFontInInputFields{ true };
	PanelPosition panelPosition{ PanelPosition::Left };
	std::string sansSerifFont;
	std::string monospaceFont;
	std::string terminalFont;
	std::string fallbackFont;
	ColorSchemeExtPreference colorScheme{ ColorSchemeExtPreference::Dark };
	std::string theme;
	std::string language;
	FontHinting fontHinting{ FontHinting::Full };
	FontAntialiasing fontAntialiasing{ FontAntialiasing::Grayscale };
};

struct WindowStateConfig {
	Float pixelDensity{ 0 };
	Sizei size{ 1280, 720 };
	std::string winIcon;
	bool maximized{ false };
	std::string panelPartition;
	std::string statusBarPartition;
	int displayIndex{ 0 };
	Vector2i position{ -1, -1 };
	Uint32 lastRunVersion{ 0 };
	std::vector<std::string> sidePanelTabsOrder;
};

struct CodeEditorConfig {
	std::string colorScheme{ "ecode" };
	StyleSheetLength fontSize{ 11, StyleSheetLength::Dp };
	StyleSheetLength lineSpacing{ 0, StyleSheetLength::Dp };
	bool showLineNumbers{ true };
	bool showWhiteSpaces{ true };
	bool showLineEndings{ false };
	bool showIndentationGuides{ false };
	bool highlightMatchingBracket{ true };
	bool verticalScrollbar{ true };
	bool horizontalScrollbar{ true };
	bool highlightCurrentLine{ true };
	bool highlightSelectionMatch{ true };
	bool colorPickerSelection{ false };
	bool colorPreview{ false };
	bool inlineColorBoxes{ false };
	bool minimap{ true };
	bool showDocInfo{ true };
	bool hideTabBarOnSingleTab{ true };
	bool hideTabBar{ false };
	bool tabSwitcher{ false };
	bool openDocumentsInMainSplit{ false };
	UITabWidget::TabJumpMode tabJumpMode{ UITabWidget::TabJumpMode::Linear };

	bool singleClickNavigation{ false };
	bool syncProjectTreeWithEditor{ true };
	bool autoCloseXMLTags{ true };
	bool linesRelativePosition{ false };
	bool autoReloadOnDiskChange{ false };
	bool codeFoldingEnabled{ true };
	bool codeFoldingAlwaysVisible{ false };
	LineWrapMode wrapMode{ LineWrapMode::NoWrap };
	LineWrapType wrapType{ LineWrapType::Viewport };
	bool wrapKeepIndentation{ true };
	std::string autoCloseBrackets{ "" };
	Time cursorBlinkingTime{ Seconds( 0.5f ) };
	Time codeFoldingRefreshFreq{ Seconds( 2.f ) };
	std::string tabIndentCharacter{ "" };
	CharacterAlignment tabIndentAlignment{ CharacterAlignment::Center };
};

struct DocumentConfig {
	bool trimTrailingWhitespaces{ false };
	bool forceNewLineAtEndOfFile{ false };
	bool autoDetectIndentType{ true };
	bool writeUnicodeBOM{ false };
	bool indentSpaces{ false };
	bool tabStops{ true };
	TextFormat::LineEnding lineEndings{ TextFormat::LineEnding::LF };
	TextDocument::AutoIndentConfig autoIndent{ TextDocument::AutoIndentConfig::Smart };
	int indentWidth{ 4 };
	int tabWidth{ 4 };
	int lineBreakingColumn{ 100 };
};

struct SearchBarConfig {
	bool caseSensitive{ false };
	bool regex{ false };
	bool luaPattern{ false };
	bool wholeWord{ false };
	bool escapeSequence{ false };
};

struct GlobalSearchBarConfig {
	bool caseSensitive{ false };
	bool regex{ false };
	bool luaPattern{ false };
	bool wholeWord{ false };
	bool escapeSequence{ false };
	bool bufferOnlyMode{ false };
};

struct LanguagesExtensions {
	std::map<std::string, std::string> priorities;
};

struct ProjectConfig {
	bool useGlobalSettings{ true };
	HExtLanguageType hExtLanguageType{ HExtLanguageType::AutoDetect };
	DocumentConfig doc;
	LanguagesExtensions languagesExtensions;
	ProjectConfig() {}
	ProjectConfig( const DocumentConfig& doc ) { this->doc = doc; }
};

struct ProjectBuildConfiguration {
	std::string buildName;
	std::string buildType;
	std::string runName;
};

class NewTerminalOrientation {
  public:
	enum Orientation { Same, Vertical, Horizontal, StatusBarPanel };

	static NewTerminalOrientation::Orientation fromString( const std::string& orientation ) {
		if ( "same" == orientation )
			return Orientation::Same;
		if ( "horizontal" == orientation )
			return Orientation::Horizontal;
		if ( "statusbar_panel" == orientation )
			return Orientation::StatusBarPanel;
		return Orientation::Vertical;
	}

	static std::string toString( const Orientation& orientation ) {
		switch ( orientation ) {
			case Orientation::Vertical:
				return "vertical";
			case Orientation::Horizontal:
				return "horizontal";
			case Orientation::StatusBarPanel:
				return "statusbar_panel";
			case Orientation::Same:
			default:
				return "same";
		}
	}
};

class TerminalWorkingDir {
  public:
	enum Mode { ProjectRoot, CurrentFileDir, UserHome, Other };

	static Mode fromString( const std::string& mode ) {
		if ( "current" == mode )
			return Mode::CurrentFileDir;
		if ( "home" == mode )
			return Mode::UserHome;
		if ( "other" == mode )
			return Mode::Other;
		return Mode::ProjectRoot;
	}

	static std::string toString( const Mode& mode ) {
		switch ( mode ) {
			case Mode::CurrentFileDir:
				return "current";
			case Mode::UserHome:
				return "home";
			case Mode::Other:
				return "other";
			case Mode::ProjectRoot:
			default:
				return "project";
		}
	}
};

struct TerminalConfig {
	std::string shell;
	std::string shellArgs;
	std::string colorScheme{ "eterm" };
	StyleSheetLength fontSize{ 11, StyleSheetLength::Dp };
	NewTerminalOrientation::Orientation newTerminalOrientation{
		NewTerminalOrientation::Horizontal };
	TerminalWorkingDir::Mode workingDir{ TerminalWorkingDir::ProjectRoot };
	std::string workingDirOther;
	Uint64 scrollback{ 10000 };
	bool unsupportedOSWarnDisabled{ false };
	bool closeTerminalTabOnExit{ false };
	bool warnBeforeClosingTab{ true };
	bool exclusiveMode{ false };
	TerminalCursorMode cursorStyle{ TerminalCursorMode::SteadyUnderline };
	ScrollViewType scrollBarType{ ScrollViewType::Overlay };
	ScrollBarMode scrollBarMode{ ScrollBarMode::Auto };
};

struct WorkspaceConfig {
	bool restoreLastSession{ false };
	bool checkForUpdatesAtStartup{ true };
	bool sessionSnapshot{ true };
};

struct SessionSnapshotFile {
	std::string cachePath;
	std::string fspath;
	Int64 fsmtime{ 0 };
	std::string fshash;
	std::string name;
	std::string selection;
};

struct TabWidgetData {
	UIWidget* widget{ nullptr };
	Drawable* icon{ nullptr };
	std::string title;
};

struct TabWidgetCbs {
	std::function<nlohmann::json( UIWidget* )> onSave;
	std::function<TabWidgetData( const nlohmann::json& )> onLoad;
};

class AppConfig {
  public:
	WindowStateConfig windowState;
	ContextSettings context;
	CodeEditorConfig editor;
	DocumentConfig doc;
	TerminalConfig term;
	UIConfig ui;
	IniFile ini;
	IniFile iniState;
	FileInfo iniInfo;
	SearchBarConfig searchBarConfig;
	GlobalSearchBarConfig globalSearchBarConfig;
	WorkspaceConfig workspace;
	LanguagesExtensions languagesExtensions;

	bool isNewVersion() const;

	void load( const std::string& confPath, std::string& keybindingsPath,
			   std::string& initColorScheme, std::vector<std::string>& recentFiles,
			   std::vector<std::string>& recentFolders, const std::string& resPath,
			   PluginManager* pluginManager, const Sizei& displaySize, bool sync );

	void save( const std::vector<std::string>& recentFiles,
			   const std::vector<std::string>& recentFolders, const std::string& panelPartition,
			   const std::string& statusBarPartition, EE::Window::Window* win,
			   const std::string& colorSchemeName, const SearchBarConfig& searchBarConfig,
			   const GlobalSearchBarConfig& globalSearchBarConfig, PluginManager* pluginManager,
			   bool terminalMode );

	void saveProject( std::string projectFolder, UICodeEditorSplitter* editorSplitter,
					  const std::string& configPath, const ProjectConfig& docConfig,
					  const ProjectBuildConfiguration& buildConfig, bool onlyIfNeeded,
					  bool sessionSnapshot, PluginManager* );

	void loadProject( std::string projectFolder, UICodeEditorSplitter* editorSplitter,
					  const std::string& configPath, ProjectConfig& docConfig,
					  ecode::App* app, bool sessionSnapshot, PluginManager* pluginManager );

	void addTabWidgetType( const std::string& type, TabWidgetCbs tabWidget ) {
		Lock l( tabWidgetTypesMutex );
		tabWidgetTypes[type] = tabWidget;
	}

	void removeTabWidgetType( const std::string& type ) {
		Lock l( tabWidgetTypesMutex );
		tabWidgetTypes.erase( type );
	}

  protected:
	Int64 editorsToLoad{ 0 };

	void loadDocuments( UICodeEditorSplitter* editorSplitter, nlohmann::json j,
						UITabWidget* curTabWidget, ecode::App* app,
						const std::vector<SessionSnapshotFile>& sessionSnapshotFiles );

	void editorLoadedCounter( ecode::App* app );

	void loadFileAssociations( const std::string& projectFolder );

	nlohmann::json saveNode( Node* node );

	Mutex tabWidgetTypesMutex;
	std::unordered_map<std::string, TabWidgetCbs> tabWidgetTypes;
};

} // namespace ecode

#endif // ECODE_APPCONFIG_HPP
