1/*
2 * Copyright 2010, Haiku.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Clemens Zeidler <haiku@clemens-zeidler.de>
7 */
8
9#include "CatchUpManager.h"
10
11#include <vector>
12
13#include <Debug.h>
14#include <Query.h>
15
16#include "IndexServer.h"
17
18
19const uint32 kCatchUp = '&CaU';
20const uint32 kCatchUpDone = '&CUD';
21
22const bigtime_t kSecond = 1000000;
23
24
25CatchUpAnalyser::CatchUpAnalyser(const BVolume& volume, time_t start,
26	time_t end, BHandler* manager)
27	:
28	AnalyserDispatcher("CatchUpAnalyser"),
29	fVolume(volume),
30	fStart(start),
31	fEnd(end),
32	fCatchUpManager(manager)
33{
34
35}
36
37
38void
39CatchUpAnalyser::MessageReceived(BMessage *message)
40{
41	switch (message->what) {
42		case kCatchUp:
43			_CatchUp();
44		break;
45
46		default:
47			BLooper::MessageReceived(message);
48	}
49}
50
51
52void
53CatchUpAnalyser::StartAnalysing()
54{
55	PostMessage(kCatchUp);
56	Run();
57}
58
59
60void
61CatchUpAnalyser::AnalyseEntry(const entry_ref& ref)
62{
63	for (int i = 0; i < fFileAnalyserList.CountItems(); i++) {
64		FileAnalyser* analyser = fFileAnalyserList.ItemAt(i);
65		const analyser_settings& settings = analyser->CachedSettings();
66		if (settings.syncPosition / kSecond >= fStart
67			&& settings.watchingStart / kSecond <= fEnd)
68			analyser->AnalyseEntry(ref);
69	}
70}
71
72
73void
74CatchUpAnalyser::_CatchUp()
75{
76	STRACE("_CatchUp start %i, end %i\n", (int)fStart, (int)fEnd);
77	for (int i = 0; i < fFileAnalyserList.CountItems(); i++)
78		STRACE("- Analyser %s\n", fFileAnalyserList.ItemAt(i)->Name().String());
79
80	BQuery query;
81	query.SetVolume(&fVolume);
82	query.PushAttr("last_modified");
83	query.PushInt32(fStart);
84	query.PushOp(B_GE);
85	query.PushAttr("last_modified");
86	query.PushInt32(fEnd);
87	query.PushOp(B_LE);
88	query.PushOp(B_AND);
89
90	query.Fetch();
91
92	std::vector<entry_ref> entryList;
93	entry_ref ref;
94	while (query.GetNextRef(&ref) == B_OK)
95		entryList.push_back(ref);
96
97	printf("CatchUpAnalyser:: entryList.size() %i\n", (int)entryList.size());
98
99	if (entryList.size() == 0)
100		return;
101
102	for (uint32 i = 0; i < entryList.size(); i++) {
103		if (Stopped())
104			return;
105		if (i % 100 == 0)
106			printf("Catch up: %i/%i\n", (int)i,(int)entryList.size());
107		AnalyseEntry(entryList[i]);
108	}
109	LastEntry();
110
111	_WriteSyncSatus(fEnd * kSecond);
112	printf("Catched up.\n");
113
114	BMessenger managerMessenger(fCatchUpManager);
115	BMessage msg(kCatchUpDone);
116	msg.AddPointer("Analyser", this);
117	managerMessenger.SendMessage(&msg);
118}
119
120
121void
122CatchUpAnalyser::_WriteSyncSatus(bigtime_t syncTime)
123{
124	for (int i = 0; i < fFileAnalyserList.CountItems(); i++) {
125		AnalyserSettings* settings = fFileAnalyserList.ItemAt(i)->Settings();
126		ASSERT(settings);
127		settings->SetSyncPosition(syncTime);
128		settings->WriteSettings();
129	}
130
131}
132
133
134CatchUpManager::CatchUpManager(const BVolume& volume)
135	:
136	fVolume(volume)
137{
138
139}
140
141
142CatchUpManager::~CatchUpManager()
143{
144	Stop();
145
146	for (int i = 0; i < fFileAnalyserQueue.CountItems(); i++)
147		delete fFileAnalyserQueue.ItemAt(i);
148}
149
150
151void
152CatchUpManager::MessageReceived(BMessage *message)
153{
154	CatchUpAnalyser* analyser;
155	switch (message->what) {
156		case kCatchUpDone:
157			message->GetPointer("Analyser", &analyser);
158			fCatchUpAnalyserList.RemoveItem(analyser);
159			analyser->PostMessage(B_QUIT_REQUESTED);
160		break;
161
162		default:
163			BHandler::MessageReceived(message);
164	}
165}
166
167
168bool
169CatchUpManager::AddAnalyser(const FileAnalyser* analyserOrg)
170{
171	IndexServer* server = (IndexServer*)be_app;
172	FileAnalyser* analyser = server->CreateFileAnalyser(analyserOrg->Name(),
173		fVolume);
174	if (!analyser)
175		return false;
176	ASSERT(analyserOrg->Settings());
177	analyser->SetSettings(analyserOrg->Settings());
178
179	bool status = fFileAnalyserQueue.AddItem(analyser);
180	if (!status)
181		delete analyser;
182	return status;
183}
184
185
186void
187CatchUpManager::RemoveAnalyser(const BString& name)
188{
189	for (int i = 0; i < fFileAnalyserQueue.CountItems(); i++) {
190		FileAnalyser* analyser = fFileAnalyserQueue.ItemAt(i);
191		if (analyser->Name() == name) {
192			fFileAnalyserQueue.RemoveItem(analyser);
193			delete analyser;
194		}
195	}
196
197	for (int i = 0; i < fCatchUpAnalyserList.CountItems(); i++)
198		fCatchUpAnalyserList.ItemAt(i)->RemoveAnalyser(name);
199}
200
201
202bool
203CatchUpManager::CatchUp()
204{
205	STRACE("CatchUpManager::CatchUp()\n");
206	bigtime_t startBig = real_time_clock_usecs();
207	bigtime_t endBig = 0;
208	for (int i = 0; i < fFileAnalyserQueue.CountItems(); i++) {
209		FileAnalyser* analyser = fFileAnalyserQueue.ItemAt(i);
210 		analyser->UpdateSettingsCache();
211		const analyser_settings& settings = analyser->CachedSettings();
212		STRACE("%s, %i, %i\n", analyser->Name().String(),
213			  (int)settings.syncPosition, (int)settings.watchingStart);
214		if (settings.syncPosition < startBig)
215			startBig = settings.syncPosition;
216		if (settings.watchingStart > endBig)
217			endBig = settings.watchingStart;
218	}
219
220	CatchUpAnalyser* catchUpAnalyser = new CatchUpAnalyser(fVolume,
221		startBig / kSecond, endBig / kSecond, this);
222	if (!catchUpAnalyser)
223		return false;
224	if (!fCatchUpAnalyserList.AddItem(catchUpAnalyser)) {
225		delete catchUpAnalyser;
226		return false;
227	}
228
229	for (int i = 0; i < fFileAnalyserQueue.CountItems(); i++) {
230		FileAnalyser* analyser = fFileAnalyserQueue.ItemAt(i);
231		// if AddAnalyser fails at least don't leak
232		if (!catchUpAnalyser->AddAnalyser(analyser))
233			delete analyser;
234
235	}
236	fFileAnalyserQueue.MakeEmpty();
237
238	catchUpAnalyser->StartAnalysing();
239	return true;
240}
241
242
243void
244CatchUpManager::Stop()
245{
246	for (int i = 0; i < fCatchUpAnalyserList.CountItems(); i++) {
247		CatchUpAnalyser* catchUpAnalyser = fCatchUpAnalyserList.ItemAt(i);
248		catchUpAnalyser->Stop();
249		catchUpAnalyser->PostMessage(B_QUIT_REQUESTED);
250	}
251	fCatchUpAnalyserList.MakeEmpty();
252}
253