/*
rkhtmlwindow - This file is part of the RKWard project. Created: Wed Oct 12 2005
SPDX-FileCopyrightText: 2005-2022 by Thomas Friedrichsmeier <thomas.friedrichsmeier@kdemail.net>
SPDX-FileContributor: The RKWard Team <rkward-devel@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/

#ifndef RKHTMLWINDOW_H
#define RKHTMLWINDOW_H

#include <QUrl>
#include <kparts/part.h>
#include <kio/jobclasses.h>

#include <QDomElement>
#include <QNetworkRequest>

#include "../windows/rkmdiwindow.h"

class KActionCollection;
class KRecentFilesAction;
class QAction;
class RKComponentHandle;
class XMLHelper;
class RKHTMLWindowPart;
class QTemporaryFile;
class RKHTMLWindow;
class RKFindBar;
class RCommandChain;
class RKWebPage;
class RKWebView;
class RKOutputDirectory;

/**
	\brief Show html files.

Provide a window for viewing HTML pages.

It is used as a base for several purposes: Display R-help (in HTML format), display RKWard help pages, display generic HTML, display RKWard output.

@author Pierre Ecochard
*/
class RKHTMLWindow : public RKMDIWindow {
	Q_OBJECT
public:
	enum WindowMode {
		Undefined,
		HTMLHelpWindow,
		HTMLOutputWindow
	};

/** constructor. 
@param parent parent QWidget, usually RKGlobals::rkApp () or similar */
	explicit RKHTMLWindow (QWidget *parent, WindowMode mode=HTMLHelpWindow);
/** destructor */
	~RKHTMLWindow ();
/** open given URL. Returns false, if the URL is not an existing local file. Loading a non-local URL may succeed, even if this returns false! */
	bool openURL (const QUrl &url);
/** takes care of special handling, if the url is an rkward://-url. Does nothing and returns false, otherwise.
 *  If window is not 0, and the url is a help window, open it, there (otherwise in a new window).
 *  TODO: move to RKWorkplace? As this can really open a bunch of different things, although generally _from_ an html window.
 */
	static bool handleRKWardURL (const QUrl &url, RKHTMLWindow *window=0);
	void openRKHPage (const QUrl &url);

	bool isModified () override;
/** Return current url */
	QUrl url () const { return current_url; };
/** Return current url in a restorable way, i.e. for help pages, abstract the session specific part of the path */
	QUrl restorableUrl ();
/** Return the RKOutpuDirectory shown in this view (if any) */
	RKOutputDirectory *outputDirectory() const { return dir; };

	WindowMode mode () { return window_mode; };
public slots:
	void slotPrint();
	void slotExport();
	void slotSave();
	void slotSaveAs();
	void slotRevert();
	void slotActivate();
	void slotForward ();
	void slotBack ();
	void selectionChanged ();
	void runSelection ();
/** flush current output. */
	void flushOutput ();
/** Reload current page.*/
	void refresh ();
	void zoomIn ();
	void zoomOut ();
	void setTextEncoding (QTextCodec* encoding);
	void updateState();
private slots:
	void scrollToBottom ();
	void mimeTypeDetermined (KIO::Job*, const QString& type);
	void mimeTypeJobFail (KJob*);
	void mimeTypeJobFail2 (KJob*);
	void internalNavigation (const QUrl& new_url);
	void makeContextMenu (const QPoint& pos);
	void findRequest (const QString& text, bool backwards, const RKFindBar *findbar, bool* found);
private:
friend class RKHTMLWindowPart;
	RKWebView* view;
	RKWebPage* page;
	RKFindBar* findbar;
	bool have_highlight;
/** In case the part is a khtmlpart: A ready-cast pointer to that. 0 otherwise (if a webkit part is in use) */
	RKHTMLWindowPart *part;
/** update caption according to given URL */
	virtual void updateCaption (const QUrl &url);
/** called from openURL. Takes care of updating caption, and updating back/forward actions, if available */
	void changeURL (const QUrl &url);

	struct VisitedLocation {
		QUrl url;
		QPointF scroll_position;
	};
	QList<VisitedLocation> url_history;
	void openLocationFromHistory (VisitedLocation &loc);
	int current_history_position;
	bool url_change_is_from_history;	// dirty!!!

	QUrl current_url;
	void startNewCacheFile ();
	QTemporaryFile *current_cache_file;

	WindowMode window_mode;
	void useMode (WindowMode);

	void fileDoesNotExistMessage ();

	void saveBrowserState (VisitedLocation *state);
/** the RKOutpuDirectory viewed in this window (if any) */
	RKOutputDirectory *dir;
friend class RKWebPage;
	static RKWebPage *new_window;
};

class RKHTMLWindowPart : public KParts::Part {
	Q_OBJECT
public:
	explicit RKHTMLWindowPart (RKHTMLWindow *window);
	~RKHTMLWindowPart () {};

	void setOutputDirectoryActionsEnabled(bool enable);
	void setOutputWindowSkin();
	void setHelpWindowSkin();
	void initActions();
private:
friend class RKHTMLWindow;
	RKHTMLWindow *window;

	// general actions
	QAction* run_selection;
	QAction* print;
	// actions in output window mode
	QAction* outputFlush;
	QAction* outputRefresh;
	// actions in help window mode
	QAction* back;
	QAction* forward;
	QAction* export_page;
	QAction* revert;
	QAction* activate;
};

/**
	\brief Renders RKWard help pages.

@author Thomas Friedrichsmeier
*/
class RKHelpRenderer {
public:
/** ctor */
	explicit RKHelpRenderer (QIODevice *_device) { device = _device; help_xml = 0; component_xml = 0; };
/** destructor */
	~RKHelpRenderer () {};
/** render an rkward://[page|component]-page to the device given in the ctor.
 * @param container : Should be page contain dynamic elements, connections will be set up to call refresh(), on the container, as appropriate. May be nullptr. */
	bool renderRKHelp(const QUrl &url, RKHTMLWindow* container);
private:
	XMLHelper *help_xml;
	XMLHelper *component_xml;
	QDomElement help_doc_element;
	QDomElement component_doc_element;

	QString renderHelpFragment (QDomElement &fragment);
	QString resolveLabel (const QString &id) const;
	QString prepareHelpLink (const QString &href, const QString &text);
	QString componentPathToId (const QString &path);
	RKComponentHandle *componentPathToHandle (const QString &path);
	QString startSection(const QString &name, const QString &title, const QString &shorttitle, QStringList *anchors, QStringList *anchor_names);
	QString endSection();

	QIODevice *device;
	void writeHTML (const QString &string);
};

#include <QMultiHash>

#include <kdirwatch.h>

/** Takes care of showing / refreshing output windows as needed.
 *
 *  For historical reasons, not all output windows refer to RKOutpuDirectories, which is why this separate class takes care of the mapping (for now). */
class RKOutputWindowManager : public QObject {
Q_OBJECT
public:
	static RKOutputWindowManager *self ();

	void registerWindow (RKHTMLWindow *window);
/** R may produce output while no output window is active. This allows to set the file that should be monitored for such changes (called from within rk.set.html.output.file()). */
	void setCurrentOutputPath (const QString &path);
	QString currentOutputPath() const { return current_default_path; };
/** returns a list (possibly empty) of pointers to existing output windows for the given path. */
	QList<RKHTMLWindow*> existingOutputWindows (const QString &path) const;
private:
	RKOutputWindowManager ();
	~RKOutputWindowManager ();
	static RKOutputWindowManager *_self;

	QString current_default_path;
	KDirWatch *file_watcher;
	QMultiHash<QString, RKHTMLWindow *> windows;
private slots:
	void fileChanged (const QString &path);
	void windowDestroyed (QObject *window);
	void rewatchOutput ();
};

#endif
