#ifndef DAEMON_H
#define DAEMON_H

#include "file_manager.h"
#include "pifiletransfer.h"
#include "pipeer.h"
#include "piscreentiles.h"
#include "pisystemmonitor.h"
#include "piterminal.h"
#include "terminal_tile.h"

extern PISystemMonitor sys_mon;

class Daemon: public PIPeer {
	PIOBJECT_SUBCLASS(Daemon, PIPeer)

public:
	Daemon();
	~Daemon();

	struct HostInfo {
		HostInfo() {
			processorsCount = ID = threads = priority = -1;
			physical_memsize = share_memsize = 0;
			cpu_load_system = cpu_load_user = 0.;
		}
		PIString execCommand;
		PIString hostname;
		PIString user;
		PIString OS_name;
		PIString OS_version;
		PIString architecture;
		PIDateTime execDateTime;
		int processorsCount;

		int ID;
		int threads;
		int priority;
		ulong physical_memsize;
		ulong share_memsize;
		float cpu_load_system;
		float cpu_load_user;
	};

	void showMainList() { showTile(list_daemons, "Select daemon"); }
	void showActionList() { showTile(list_actions, "Select action"); }
	void showLocalFilemanager() {
		mode = rmLocalFileManager;
		showTile(tile_fm, "File manager");
	}

	PIStringList availableDaemons();
	void connectToDaemon(const PIString & dn);
	void disconnect();

	PIString connectedDaemon() const;
	PIString thisDaemonName() const { return selfInfo().name.mid(6); }

	PIScreenTile * tile() const;

	FileManager fm;

private:
	enum PacketType {
		RequestHostInfo = 10,
		RequestChangeDir,

		ReplyHostInfo = 20,
		ReplyChangeDir,
		CopyFiles,
		RemoveFiles,
		MkDir,
		CryptFiles,

		FileTransfer = 30,

		ShellOpen    = 40,
		ShellClose,
		ShellContent,
		ShellResizeRequest,
		ShellKeyEvent
	};

	enum RemoteMode {
		rmNone,
		rmLocalFileManager,
		rmSelectMode,
		rmInformation,
		rmFileManager,
		rmShell
	};

	class Remote: public PIThread {
		PIOBJECT_SUBCLASS(Remote, PIThread)

	public:
		explicit Remote(const PIString & n = PIString());
		~Remote();
		void sendFiles(const PIString & dir, const PIStringList & fl) { startAction(Daemon::CopyFiles, dir, fl); }
		void removeFiles(const PIStringList & fl) { startAction(Daemon::RemoveFiles, PIString(), fl); }
		void updateDir() { startAction(Daemon::RequestChangeDir, PIString(), PIStringList()); }
		void makeDir(const PIString & dir) { startAction(Daemon::MkDir, dir, PIStringList() << ""); }
		void cryptFiles(const PIStringList & fl) { startAction(Daemon::CryptFiles, PIString(), fl); }
		void shellOpen();
		void shellClose();
		void shellResize(int w, int h);
		void shellKeySend(PIKbdListener::KeyEvent k);
		EVENT_HANDLER1(void, ftSendRequest, PIByteArray &, data) {
			PIByteArray h;
			h << int(FileTransfer);
			data.insert(0, h);
			sendRequest(name(), data);
		}
		EVENT_HANDLER1(void, ftReceived, bool, ok) { receiveFinished(name(), ok); }
		EVENT_HANDLER1(void, ftSended, bool, ok) { sendFinished(name(), ok); }

		EVENT2(sendRequest, const PIString &, p_name, const PIByteArray &, data)
		EVENT2(receiveFinished, const PIString &, p_name, bool, ok)
		EVENT2(sendFinished, const PIString &, p_name, bool, ok)
		EVENT2(changeDirFinished, const PIString &, p_name, const PIString &, dir)
		EVENT_HANDLER1(void, received, PIByteArray &, data) { ft.received(data); }
		EVENT_HANDLER(void, termTimerTick);

		void startAction(PacketType a, const PIString & dir, const PIStringList & fl);
		void run() override;

		PIDir dir_my;
		PIVector<PIFile::FileInfo> my_filelist;
		PIVector<PIVector<PIScreenTypes::Cell>> pcells;
		PIFileTransfer ft;
		PIStringList _fl;
		PacketType action;
		PITimer term_timer;
		PITerminal * term;

	private:
		void updateDirEntries();
	};

	class TileFileProgress: public PIScreenTile {
		PIOBJECT_SUBCLASS(TileFileProgress, PIScreenTile)

	public:
		TileFileProgress();
		void show(PIFileTransfer * f);
		void close(bool ok = true);
		TileSimple *label_file, *label_speed, *label_cnt;
		TileProgress *prog_file, *prog_all;
		TileButtons * buttons;
		PIFileTransfer * ft;
		PITimeMeasurer tm, tme;
		void resizeEvent(int w, int h) override;
		void sizeHint(int & w, int & h) const override;
		void drawEvent(PIScreenDrawer * d) override;
		bool rec;
		PIString conn_name;
		EVENT_HANDLER2(void, tileEvent, PIScreenTile *, t, PIScreenTypes::TileEvent, e);
	};

	EVENT_HANDLER2(void, tileEvent, PIScreenTile *, t, PIScreenTypes::TileEvent, e);
	EVENT_HANDLER1(void, keyEvent, PIKbdListener::KeyEvent, key);
	EVENT_HANDLER2(void, sendRequest, const PIString &, p_name, const PIByteArray &, data) { send(p_name, data); }
	EVENT_HANDLER1(void, fmKeyEvent, PIKbdListener::KeyEvent, key);
	EVENT_HANDLER3(void, fmActionRequest, bool, remote_tile, FileManager::Action, type, PIVariant, data);
	EVENT_HANDLER(void, shResizeRequest);
	EVENT_HANDLER1(void, shKeyEvent, PIKbdListener::KeyEvent, k);
	EVENT_HANDLER1(void, timerEvent, int, delim);
	EVENT_HANDLER2(void, filesReceived, const PIString &, p_name, bool, ok);
	EVENT_HANDLER2(void, filesSended, const PIString &, p_name, bool, ok);
	EVENT_HANDLER2(void, dirChanged, const PIString &, p_name, const PIString &, dir);
	EVENT_HANDLER2(void, closeFileDialog, const PIString &, p_name, bool, ok);
	EVENT_HANDLER2(void, localSend, const PIString &, p_name, const PIByteArray &, data);
	EVENT_HANDLER(void, escPressed);
	EVENT_HANDLER(void, requestOpenShell);
	EVENT_HANDLER(void, requestCloseShell);
	EVENT(menuRequest)
	void hideAll();
	void showTile(PIScreenTile * t, const PIString & header = PIString());
	void fillInfoTile(const HostInfo & hi);
	void peerConnected(const PIString & p_name) override;
	void peerDisconnected(const PIString & p_name) override;
	void dataReceived(const PIString & from, const PIByteArray & data) override;
	void makeMyHostInfo();
	void makeOtherHostInfo();

	void requestChDir(const PIString & d);
	void sendDirToRemote(Remote * r);

	mutable PIStringList available_daemons;
	PITimer dtimer;
	PIString conn_name;
	PIMutex remote_mutex;
	PIMap<PIString, Remote *> remotes;
	PISystemMonitor sys_mon_other;
	HostInfo info_my, info_other;
	PIScreenTile *tile_root, *tile_fm;
	TileSimple *tile_info, *tile_header;
	TileList *list_daemons, *list_actions;
	TileFileProgress * tile_file_progress;
	TileTerminal * tile_shell;
	PIFileTransfer localft;
	Remote * _self;
	RemoteMode mode;
	int offset, cur, height;
};

BINARY_STREAM_WRITE(Daemon::HostInfo) {
	s << v.execCommand << v.hostname << v.user << v.OS_name << v.OS_version << v.architecture << v.execDateTime << v.processorsCount << v.ID
	  << v.threads << v.priority << v.physical_memsize << v.share_memsize << v.cpu_load_system << v.cpu_load_user;
	return s;
}
BINARY_STREAM_READ(Daemon::HostInfo) {
	s >> v.execCommand >> v.hostname >> v.user >> v.OS_name >> v.OS_version >> v.architecture >> v.execDateTime >> v.processorsCount >>
		v.ID >> v.threads >> v.priority >> v.physical_memsize >> v.share_memsize >> v.cpu_load_system >> v.cpu_load_user;
	return s;
}

#endif // DAEMON_H
