summaryrefslogtreecommitdiff
path: root/plugins/HistoryStats/src/statistic.h
blob: 9c387af84625fb58c7d08c1f92fd33d4d65e7401 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
#if !defined(HISTORYSTATS_GUARD_STATISTIC_H)
#define HISTORYSTATS_GUARD_STATISTIC_H

#include "stdafx.h"
#include "_consts.h"

#include <vector>
#include <map>
#include <set>
#include <list>

#include "settings.h"
#include "message.h"

class Contact; // forward declaration instead of #include "contact.h"

class Statistic
	: private pattern::NotCopyable<Statistic>
{
public:
	enum InvocationSource {
		fromOptions,
		fromStartup,
		fromMenu,
	};

	enum DBEventTypeRaw {
		EVENTTYPE_AVATARCHANGE        =  9003, // from pescuma
		EVENTTYPE_WAT_REQUEST         =  9601, // from WATrack
		EVENTTYPE_WAT_ANSWER          =  9602, // from WATrack
		EVENTTYPE_WAT_ERROR           =  9603, // from WATrack
		EVENTTYPE_STATUSCHANGE        = 25368, // from NewXStatusNotify
	};

	enum DBEventType {
		// messages
		etMessage             = EVENTTYPE_MESSAGE,
		// other events
		etFile                = EVENTTYPE_FILE,

		// authorization and contacts
		etAuthRequest         = EVENTTYPE_AUTHREQUEST,
		etAdded               = EVENTTYPE_ADDED,
		etContacts            = EVENTTYPE_CONTACTS,
		// status/avatar/nick/... changes
		etStatusChange        = EVENTTYPE_STATUSCHANGE,
		etAvatarChange        = EVENTTYPE_AVATARCHANGE,
		// WaTrack events
		etWATRequest          = EVENTTYPE_WAT_REQUEST,
		etWATAnswer           = EVENTTYPE_WAT_ANSWER,
		etWATError            = EVENTTYPE_WAT_ERROR,
	};

	typedef std::vector<Contact*> ContactList;
	typedef std::vector<const Contact*> ContactListC;
	typedef std::pair<ext::string, ext::string> ConflictingFile; // (desired, temp)
	typedef std::list<ConflictingFile> ConflictingFiles;
	typedef std::map<Canvas::Digest, ext::string> ImageMap;

private:
	static bool m_bRunning;

private:
	// settings and the like
	Settings m_Settings;
	Settings::CharMapper m_CharMapper;

	// handles for windows and synchronisation
	HINSTANCE m_hInst;
	HWND m_hWndProgress;
	HANDLE m_hThreadPushEvent;
	HANDLE m_hCancelEvent;
	InvocationSource m_InvokedFrom;

	// list of contacts
	ContactList m_Contacts;

	// special 'contacts': omitted, totals
	Contact* m_pTotals;
	Contact* m_pOmitted;

	bool
		m_bActuallyOmitted,  // did we really omit something
		m_bResult;           // result of calculations

	// start time for statistics
	DWORD m_TimeStarted;
	DWORD m_MSecStarted;

	// minimum/maximum date/time to include
	DWORD m_TimeMin;
	DWORD m_TimeMax;

	// error processing and the like
	ext::string m_ErrorText;

	// column management
	int m_nNextSlot;
	std::vector<Column*> m_ActiveCols;
	std::vector<Column*> m_AcquireCols;
	std::vector<Column*> m_TransformCols;

	// file management
	ext::string m_TempPath;
	ext::string m_OutputPath;
	ext::string m_OutputFile;
	ext::string m_OutputPrefix;
	ConflictingFiles m_ConflictingFiles;
	int m_nLastFileNr;

	// PNG management
	ImageMap m_Images;

	// first/last message
	bool m_bHistoryTimeAvailable;
	DWORD m_nFirstTime;
	DWORD m_nLastTime;

	// misc data
	DWORD m_AverageMinTime;

private:
	// contact handling
	void prepareColumns();
	void prepareContactData(Contact& contact);
	void freeContactData(Contact& contact);
	void mergeContactData(Contact& contact, const Contact& include);
	void transformContactData(Contact& contact);
	Contact& addContact(const ext::string& nick, const ext::string& protoDisplayName, const ext::string& groupName, int nSources);

	// misc routines
	DWORD getTimeStarted() { return m_TimeStarted; }
	bool shouldTerminate() { return (WaitForSingleObject(m_hCancelEvent, 0) == WAIT_OBJECT_0) || bool_(Miranda_IsTerminated()); }
	void handleAddMessage(Contact& contact, Message& msg);
	void handleAddChat(Contact& contact, bool bOutgoing, DWORD localTimestampStarted, DWORD duration);

	// progress dialog handling
	static INT_PTR CALLBACK staticProgressProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam);
	void setProgressMax(bool bSub, int max);
	void setProgressLabel(bool bSub, const ext::string& label);
	void stepProgress(bool bSub, int step = 1);
	static INT_PTR CALLBACK staticConflictProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);

	// statistics creation steps
	bool stepInit();
	bool stepReadDB();
	bool stepRemoveContacts();
	bool stepSortContacts();
	bool stepPreOmitContacts();
	bool stepOmitContacts();
	bool stepCalcTotals();
	bool stepPostOmitContacts();
	bool stepTransformData();
	bool stepWriteHTML();

	// private constructor & main statistic creation routine
	explicit Statistic(const Settings& settings, InvocationSource invokedFrom, HINSTANCE hInst);
	bool createStatistics();
	void createStatisticsSteps();
	static void __cdecl threadProc(Statistic *pStats);
	static void __cdecl threadProcSteps(Statistic *pStats);

public:
	~Statistic();

public:
	static void run(const Settings& settings, InvocationSource invokedFrom, HINSTANCE hInst, HWND hWndParent = nullptr);

	int countContacts() const { return m_Contacts.size(); }
	const Contact& getContact(int index) const;
	const Contact& getTotals() const { assert(m_pTotals); return *m_pTotals; }
	const Contact& getOmitted() const { assert(m_pOmitted); return *m_pOmitted; }
	bool hasTotals() const { return (m_pTotals != nullptr) && m_Settings.m_CalcTotals; } // MEMO: only makes sense after 'calc totals'-step
	bool hasOmitted() const { return (m_pOmitted != nullptr) && m_Settings.m_OmitContacts && m_Settings.m_OmittedInExtraRow && m_bActuallyOmitted; } // MEMO: only makes sense after 'omit'-step
	DWORD getFirstTime(); // MEMO: only makes sense after 'calc totals'-step
	DWORD getLastTime(); // MEMO: only makes sense after 'calc totals'-step
	DWORD getHistoryTime(); // MEMO: only makes sense after 'calc totals'-step
	DWORD getAverageMinTime() { return m_AverageMinTime; }

	// file management
	ext::string createFile(const ext::string& desiredName);
	bool newFile(const wchar_t* fileExt, ext::string& writeFile, ext::string& finalURL);
	bool newFilePNG(ext::string& writeFile, ext::string& finalURL) { return newFile(L".png", writeFile, finalURL); }
	bool newFilePNG(Canvas& canvas, ext::string& finalURL);
};

#endif // HISTORYSTATS_GUARD_STATISTIC_H