#ifndef DAEMON_H
#define DAEMON_H

#include "piscreentiles.h"
#include "pipeer.h"
#include "pisystemmonitor.h"
#include "pidatatransfer.h"
#include "pifiletransfer.h"
#include "file_manager.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");}
	
	PIStringList availableDaemons();
	void connectToDaemon(const PIString & dn);
	void disconnect();
	
	PIString connectedDaemon() const;
	PIString thisDaemonName() const {return selfInfo().name.mid(6);}
	
	PIScreenTile * tile() const;
	
	bool lockedEth() const {return eth_mutex.isLocked();}
	bool lockedPeers() const {return peers_mutex.isLocked();}
	
private:
	enum PacketType {
		RequestHostInfo = 10,
		RequestChangeDir,
		
		ReplyHostInfo = 20,
		ReplyChangeDir,
		CopyFiles,
		RemoveFiles,
		MkDir,
		
		FileTransfer = 30
		
	};
	
	class Remote: public PIThread {
		PIOBJECT_SUBCLASS(Remote, PIThread)
	public:
		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);}
		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 & , name, const PIByteArray &, data)
		EVENT2(receiveFinished, const PIString & , name, bool, ok)
		EVENT2(sendFinished, const PIString & , name, bool, ok)
		EVENT2(removeFinished, const PIString & , name, const PIString & , dir)
		EVENT_HANDLER1(void, received, const PIByteArray & , data) {ft.received(data);}
		
		void startAction(PacketType a, const PIString & dir, const PIStringList & fl);
		void run();
		
		PIDir dir_my, dir_remote;
		PIFileTransfer ft;
		PIStringList _fl;
		PacketType action;
	};
	
	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);
		void sizeHint(int & w, int & h) const;
		void drawEvent(PIScreenDrawer * d);
		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 & , name, const PIByteArray & , data) {send(name, data);}
	EVENT_HANDLER1(void, fmKeyEvent, PIKbdListener::KeyEvent, key);
	EVENT_HANDLER3(void, fmActionRequest, bool, remote_tile, FileManager::Action, type, PIVariant, data);
	EVENT_HANDLER2(void, timerEvent, void * , _d, int, delim);
	EVENT_HANDLER2(void, filesReceived, const PIString & , name, bool, ok);
	EVENT_HANDLER2(void, filesRemoved, const PIString & , name, const PIString & , dir);
	EVENT_HANDLER2(void, closeFileDialog, const PIString & , name, bool, ok);
	EVENT(menuRequest)
	void hideAll();
	void showTile(PIScreenTile * t, const PIString & header = PIString());
	void fillInfoTile(const HostInfo & hi);
	void peerConnected(const PIString & name);
	void peerDisconnected(const PIString & name);
	void dataReceived(const PIString & from, const PIByteArray & data);
	void makeMyHostInfo();
	void makeOtherHostInfo();
	
	void requestChDir(const PIString & d);
	void sendDirToRemote(Remote * r);
	
	mutable PIStringList available_daemons;
	PITimer timer;
	PIString conn_name;
	PIMutex remote_mutex;
	PIMap<int, PIString> dnames;
	PIMap<PIString, Remote*> remotes;
	PISystemMonitor sys_mon_other;
	HostInfo info_my, info_other;
	FileManager fm;
	PIScreenTile * tile_root, * tile_fm;
	TileSimple * tile_info, * tile_header;
	TileList * list_daemons, * list_actions;
	TileFileProgress * tile_file_progress;
	int mode, offset, cur, height;
	
	
};

inline PIByteArray & operator <<(PIByteArray & b, const Daemon::HostInfo & v) {
	b << 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 b;
}
inline PIByteArray & operator >>(PIByteArray & b, Daemon::HostInfo & v) {
	b >> 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 b;
}

#endif // DAEMON_H
