/*
    PIP - Platform Independent Primitives
    Single application
    Ivan Pelipenko peri4ko@yandex.ru

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "pisingleapplication.h"

#include "piliterals_bytes.h"
#include "piliterals_time.h"
#include "pisharedmemory.h"
#include "pitime.h"


//! \class PISingleApplication pisingleapplication.h
//! \~\details
//! \~english
//!
//!
//! \~russian
//! Этот класс позволяет отслеживать повторный запуск приложения
//! и передавать сообщение первому запущеному.
//!
//! Видят друг друга %PISingleApplication с одинаковыми "app_name",
//! задаваемым в конструкторе.
//!
//! Если проверка \a isFirst() успешна, значит этот экземпляр
//! запущен первым, и можно соединиться к событию \a messageReceived().
//!
//! Если проверка провалена, значит этот экземпляр не первый,
//! и можно послать ему сообщение с помощью \a sendMessage(),
//! а затем выйти.
//!
//! \~\code
//! int main(int argc, char * argv[]) {
//! 	PISingleApplication sapp("myapp");
//! 	if (sapp.isFirst()) {
//! 		piCout << "I`m first, wait for another";
//! 		CONNECTL(&sapp, messageReceived, [](PIByteArray msg){
//! 			piCout << "Msg from another:" << PIString(msg);
//! 		});
//! 	} else {
//! 		piCout << "I`m not first, send and exit";
//! 		sapp.sendMessage(PIString("Hello!").toByteArray());
//! 		return 0;
//! 	}
//! 	WAIT_FOREVER
//! 	return 0;
//! }
//! \endcode
//!


#define SHM_SIZE 32_KiB


PISingleApplication::PISingleApplication(const PIString & app_name): PIThread() {
	first   = true;
	started = false;
	sacnt   = 0;
	shm     = new PISharedMemory("sa_" + app_name, SHM_SIZE);
	start(10_Hz);
}


PISingleApplication::~PISingleApplication() {
	stop();
	if (!waitForFinish(5_s)) terminate();
	delete shm;
}


bool PISingleApplication::isFirst() const {
	waitFirst();
	return first;
}


void PISingleApplication::sendMessage(const PIByteArray & m) {
	waitFirst();
	PIByteArray ba;
	int lm[3] = {0, 0, 0};
	for (;;) {
		shm->read(lm, 12);
		if (lm[2] == 0) break;
		piMSleep(10);
	}
	ba << sacnt << sacnt << int(1) << m;
	shm->write(ba);
}


void PISingleApplication::begin() {
	int cnt[2] = {0, 0};
	int tcnt   = 0;
	shm->read(cnt, 8);
	for (int i = 0; i < 5; ++i) {
		tcnt = cnt[0];
		shm->read(cnt, 8);
		if (cnt[0] == cnt[1] && cnt[0] != tcnt) {
			first = false;
			break;
		}
		piMSleep(100);
	}
	// piCoutObj << "started" << first << shm->size();
	readed.reserve(shm->size());
	started = true;
}


void PISingleApplication::run() {
	if (!first) return;
	++sacnt;
	int st_[2] = {sacnt, sacnt};
	shm->write(st_, 8);
	// piCoutObj << "write" << sacnt;
	const int hdr_sz = sizeof(int) * 3;
	PIByteArray all(shm->size());
	shm->read(all.data(), all.size());
	int ri[3] = {0, 0, 0};
	memcpy(ri, all.data(), hdr_sz);
	if (ri[2] != 0 && ri[0] == ri[1]) {
		readed.resize(all.size() - hdr_sz);
		memcpy(readed.data(), all.data() + hdr_sz, readed.size());
		PIByteArray msg;
		readed >> msg;
		if (msg.isNotEmpty()) {
			messageReceived(msg);
			// piCoutObj << "message" << msg;
		}
		int wi[3] = {sacnt, sacnt, 0};
		shm->write(wi, 12);
	}
}


void PISingleApplication::waitFirst() const {
	while (!started)
		piMSleep(50);
}
