//---------------------------------------------------------------------------

#include <vcl.h>

#pragma hdrstop

#include "TZec.h"
#pragma package(smart_init)



bool TZec::_stopZec = false;

DllWrapper* TZec::_dllWrapper;
THelper* TZec::_helperZec = new THelper();
TLogger* TZec::_loggerZec = new TLogger();
TPinger* TZec::_pingerZec = new TPinger();

bool TZec::_connUtoku = false;
bool TZec::_inError = false;
bool TZec::_prijavljen = false;
bool TZec::_gotInfo = false;

const AnsiString TZec::_dan = "86400000";
const AnsiString TZec::_8sati = "28800000";
const AnsiString TZec::_sat = "3600000";

AnsiString TZec::_server;
AnsiString TZec::_app = "";
int TZec::_port;
TCompInfo TZec::_info;

TFormatSettings _fmt = TFormatSettings::Create(GetThreadLocale());


void TZec::Gotov()
{
	TZec::_loggerZec->Log("Konektovan na RabbitMQ", llDebug);
	TZec::_inError = false;
	TZec::_connUtoku = false;
    TZec::_dllWrapper->_rdy = true;
	TZec::_dllWrapper->_rekonekcija();
}

void TZec::Konektovan()
{
	TZec::_loggerZec->Log("Pravim razmene.", llDebug);
	DecExch razmene = (DecExch)TZec::_dllWrapper->NapraviPrijemneRazmene;
	razmene(Gotov, TZec::_info._pib.c_str(), TZec::_dllWrapper->_uid.c_str(), TZec::_app.c_str());
	TZec::_loggerZec->Log("Razmene proglasene", llDebug);
}

void TZec::PosaljiPoruku(const std::map<AnsiString, AnsiString>& hederiMapa, AnsiString poruka, AnsiString uid, CallbackStatus statusCallback,
	bool naSve, AnsiString trajanje)
{

	Bloker();
	if (TZec::_stopZec)
	{
		return;
	}
	const char* rou = uid.c_str();
	const char* msg = poruka.c_str();
	auto hederi = Hederi(hederiMapa, false);
	auto vrednosti = Hederi(hederiMapa, true);
	SendMsg salji = (SendMsg)_dllWrapper->PosaljiPoruku;
	const char* ttl = trajanje.c_str();
	try
	{
		if (TZec::_dllWrapper->_rdy)
		{
			salji(rou, msg, hederi, vrednosti, statusCallback, naSve, ttl);
			TZec::_loggerZec->Log("Poruka poslata", llDebug);
		}else
		{
			TZec::_loggerZec->Log("Ne saljemo poruku, stanje nije ok", llCritical);
        }

	}catch(...){}

	int size = hederiMapa.size();
	for (int dex = 0; dex < size; dex++)
	{
		delete[] hederi[dex];
		delete[] vrednosti[dex];
	}
	delete[] hederi;
	delete[] vrednosti;
}

void TZec::PoslajiLog(UnicodeString logPoruka, LoggingLevel nivo, CallbackStatus statusCallback)
{
	try
	{
		TZec::Bloker();
		if (TZec::_stopZec)
		{
			return;
		}
		std::map<AnsiString, AnsiString> hederi;
		TDateTime timestamp = Now();
		_fmt.DateSeparator = '/';
		_fmt.LongDateFormat = "mm/dd/yyyy hh:nn:ss";
		_fmt.ShortDateFormat = "mm/dd/yyyy hh:nn:ss";
		AnsiString ts = FormatDateTime("mm/dd/yyyy hh:nn:ss", timestamp, _fmt);
		hederi["vreme"]=ts;
		hederi["tip"] = TipPorukeUStringInternal(tpLiveLogovi);
		hederi["pib"] = TZec::_info._pib;
		hederi["hostname"] = TZec::_info._defMagacin + " - " + TZec::_info._hostname;
		hederi["uid"] = TZec::_info._uid;
		hederi["ver"] = TZec::_info._verzijaMP;
		hederi["nivo"] = nivo;
		if (nivo == llCritical)
		{
			TZec::PosaljiPoruku(hederi, logPoruka.t_str(), "bus.fan", statusCallback, true, _sat);
		}else
		{
			TZec::PosaljiPoruku(hederi, logPoruka.t_str(), "bus.fan", statusCallback, true);
		}

	}catch(...)
	{
		TZec::_loggerZec->Log("Neuspelo slanje zahteva", llCritical);
	}
}

void TZec::Prijava(CallbackStatus statusCallback)
{
	try
	{
		TZec::Bloker();
		if (TZec::_stopZec)
		{
			return;
		}
		std::map<AnsiString, AnsiString> hederi;
		TDateTime timestamp = Now();
		_fmt.DateSeparator = '/';
		_fmt.LongDateFormat = "mm/dd/yyyy hh:nn:ss";
		_fmt.ShortDateFormat = "mm/dd/yyyy hh:nn:ss";
		AnsiString ts = FormatDateTime("mm/dd/yyyy hh:nn:ss", timestamp, _fmt);
		hederi["vreme"]=ts;
		hederi["tip"] = TipPorukeUStringInternal(tpPrijava);
		hederi["pib"] = TZec::_info._pib;
		hederi["hostname"] = TZec::_info._defMagacin + " - " + TZec::_info._hostname;
		hederi["uid"] = TZec::_info._uid;
		hederi["ver"] = TZec::_info._verzijaMP;
		TZec::_loggerZec->Log("Slanje prijave", llDebug);
		TZec::PosaljiPoruku(hederi, "", "bus.fan", statusCallback, true, 60000);
	}catch(...){}
}

void TZec::KonekcijaZatvorena()
{
	TZec::_loggerZec->Log("Zatvaram konekciju", llDebug);
	TZec::_connUtoku = false;
	TZec::_dllWrapper->_rdy = false;
}


void TZec::KonektujSe(NoParamsFunc rekonekcija, int paralelnoPoruka, AnsiString app)
{
	_app = app;
	if (TZec::_connUtoku)
	{
		TZec::_loggerZec->Log("Konekcija je vec u toku, zaustavljam pokusaj.", llCritical);
		return;
	}
	_connUtoku = true;

	if (TZec::_dllWrapper != nullptr)
	{
		_loggerZec->Log("Brisem dll wrapper", llDebug);
		try
		{
			_dllWrapper->Diskonekcija();
			Sleep(2000);
		}catch(...){}
		delete TZec::_dllWrapper;
		TZec::_dllWrapper = nullptr;
	}

	_loggerZec->Log("Novi dll wrapper", llDebug);
	TZec::_dllWrapper = new DllWrapper();

	auto serverOp = _helperZec->ProcitajJSON("zec", "server");
	auto portOp = _helperZec->ProcitajJSON("zec", "port");
	_server = serverOp->DohvatiRezultat();
	_port = StrToIntDef(portOp->DohvatiRezultat(), 3050);
	delete serverOp;
	delete portOp;
	_loggerZec->Log("Procitao confit", llDebug);
	_dllWrapper->_paralelnoPoruka = paralelnoPoruka;
	_dllWrapper->_rekonekcija = rekonekcija;
	AnsiString uid = "???";
	if (!_gotInfo)
	{
		auto uidOp = _helperZec->DohvatiHostInformacije();

		if (uidOp->OperacijaUspela())
		{
			_info = uidOp->DohvatiRezultat();
			uid = _info._uid;
		}
		_gotInfo = true;
		delete uidOp;
	}

	_dllWrapper->_uid = uid + "." + app;
	auto userOp = TZec::_helperZec->ProcitajJSON("zec", "user");
	auto passOp = TZec::_helperZec->ProcitajJSON("zec", "pass");
	AnsiString serverAddress = _server;
	int port = _port;

	AnsiString user = userOp->DohvatiRezultat();
	AnsiString pass = passOp->DohvatiRezultat();

	delete userOp;
	delete passOp;

	ConnectFunc conn = (ConnectFunc)_dllWrapper->Konekcija;
	AnsiString err = "";
	try
	{
		TZec::_loggerZec->Log("Krece konekcija.", llDebug);
		err = conn(Konektovan, KonekcijaZatvorena, user.c_str(), pass.c_str(), serverAddress.c_str(), port, paralelnoPoruka);
		if (!err.IsEmpty())
		{
			_connUtoku = false;
			_inError = true;
			_loggerZec->Log("Greska pri konekciji", llCritical);
            _loggerZec->Log(err, llCritical);
		}
	}catch(...){_connUtoku = false;}
	TTask::Create([&](){Provera();})->Start();
}

void TZec::Provera()
{
	do
	{
		try
		{
			try
			{
 				if (TZec::_stopZec)
				{
					TZec::_loggerZec->Log("Zec staje zbog stop.", llInfo);
					break;
				}
			}catch(...){break;}

			if (!TZec::_dllWrapper->StatusSoketa() || TZec::_inError)
			{
				TZec::_dllWrapper->_rdy = false;
				TZec::_loggerZec->Log("Konekcija ka RabbitMQu nije ok server:" + TZec::_server + " port:" + IntToStr(TZec::_port), llCritical);

				if (TZec::_stopZec)
				{
					TZec::_loggerZec->Log("Izlazim iz provere stanja, sve ok, regularno zaustavljanje",llDebug);
					return;
				}
				if (TZec::_pingerZec->TestPort(TZec::_server, TZec::_port))
				{
					TZec::_loggerZec->Log("Ping ka serveru ok.", llDebug);
					if (!TZec::_connUtoku)
					{
						TZec::_loggerZec->Log("Konekcija nije u toku, idem ponovo.", llDebug);
						try
						{
							TZec::_loggerZec->Log("Ponovna konekcija.", llDebug);
							TZec::KonektujSe(TZec::_dllWrapper->_rekonekcija, TZec::_dllWrapper->_paralelnoPoruka);
						}catch(Exception &ex)
						{
							TZec::_loggerZec->Log("Greska pri konekciji: " + ex.Message, llCritical);
							TZec::_connUtoku=false;
						}catch (std::exception &stex)
						{
							TZec::_loggerZec->Log(AnsiString("Greska pri konekciji: ") + stex.what(), llCritical);
							TZec::_connUtoku=false;
						}catch(...)
						{
							TZec::_loggerZec->Log("Greska pri konekciji.", llCritical);
							TZec::_connUtoku=false;
						}
						return;
					}else
					{
						TZec::_loggerZec->Log("Konekcija je u toku.", llDebug);
                    }
				}else
				{
					TZec::_loggerZec->Log("Nema pinga ka serveru.", llCritical);
				}
			}else
			{
				TZec::_loggerZec->Log("Sve ok.", llDebug);
            }
		}catch(Exception &ex)
		{
			TZec::_loggerZec->Log("Greska pri proveri stanja: " + ex.Message, llCritical);
		}catch (std::exception &stex)
		{
			TZec::_loggerZec->Log(AnsiString("Greska pri proveri stanja: ") + stex.what(), llCritical);
		}catch(...)
		{
			TZec::_loggerZec->Log("Greska pri proveri stanja.", llCritical);
		}
		Sleep(10000);
	}while(true);
}


void TZec::Bloker()
{
	int slept = 0;
	int quartSec = 0;
	int minuteSec = 0;
	while(!TZec::_dllWrapper->_rdy)
	{
		try
		{
			if (TZec::_stopZec)
			{
				break;
			}
		}catch(...){break;}
		Sleep(250);
		if (++quartSec == 4)
		{
			quartSec = 0;
			if (++minuteSec == 60)
			{
				minuteSec = 0;
				TZec::_loggerZec->Log("Nema konekcije ka rabbitu " + IntToStr(++slept) + " minuta.", llCritical);
			}
		}

	}
}

void TZec::PrijemPorukeInterno(const char** zaglavljeNazivi, const char** zaglavljeVrednosti, const char* poruka, int brPoruke)
{
	std::map<AnsiString, AnsiString> zaglavlja;
	int indeks = 0;
	while (zaglavljeNazivi[indeks] != nullptr)
	{
		zaglavlja[zaglavljeNazivi[indeks]] = zaglavljeVrednosti[indeks];
		indeks++;
	}
	TZec::_dllWrapper->PoPrijemu(zaglavlja, poruka, brPoruke);
}

void TZec::Prijem(AnsiString kanal, RcvMsgExternal prijem)
{
	Bloker();
	const char* channel = kanal.c_str();
	SubscribeChn sub = (SubscribeChn)_dllWrapper->EventPoruka;
	_dllWrapper->PoPrijemu = prijem;
	sub(PrijemPorukeInterno, channel);
}

void TZec::PotvrdiPrijem(int tag)
{
	Bloker();
	_dllWrapper->PotvrdiPrijem(tag);
}

const char* const* TZec::Hederi(const std::map<AnsiString, AnsiString>& hederiMapa, bool vrednosti)
{
	int size = hederiMapa.size();
	int indeks = 0;
	char** hederi = new char*[size+1];
	for (const auto& kv : hederiMapa)
	{
		AnsiString vrednost = vrednosti ? kv.second : kv.first;
		hederi[indeks] = new char[vrednost.Length()+1];
		memcpy(hederi[indeks], vrednost.c_str(), vrednost.Length());
		hederi[indeks][vrednost.Length()] = '\0';
		indeks++;
	}
	hederi[indeks] = nullptr;
	return const_cast<const char* const*>(hederi);
}

AnsiString TZec::TipPorukeUString(TipPoruke tip)
{
	return TipPorukeUStringInternal(tip);
}
TipPoruke TZec::StringUTipPoruke(AnsiString tip)
{
	return StringUTipPorukeInternal(tip);
}


void TZec::IzvrsiSql(AnsiString uid, UnicodeString sql, CallbackStatus statusCallback)
{
	Bloker();
	std::map<AnsiString, AnsiString> hederi;
	TDateTime timestamp = Now();
	_fmt.DateSeparator = '/';
	_fmt.LongDateFormat = "mm/dd/yyyy hh:nn:ss";
	_fmt.ShortDateFormat = "mm/dd/yyyy hh:nn:ss";
	AnsiString ts = FormatDateTime("mm/dd/yyyy hh:nn:ss", timestamp, _fmt);
	hederi["vreme"]=ts;
	hederi["tip"] = TipPorukeUStringInternal(tpSqlExec);
	hederi["zahtev"] = _dllWrapper->_uid;
	PosaljiPoruku(hederi, sql.t_str(), uid, statusCallback, false);
}
void TZec::PrijaviIzvrsenjeSql(AnsiString uid, UnicodeString sta, bool greska,
	bool rezultat, CallbackStatus statusCallback)
{
	Bloker();
	std::map<AnsiString, AnsiString> hederi;
	hederi["tip"] = TipPorukeUStringInternal(tpSqlReport);
	hederi["izvrsio"] = _dllWrapper->_uid;
	TDateTime timestamp = Now();
	_fmt.DateSeparator = '/';
	_fmt.LongDateFormat = "mm/dd/yyyy hh:nn:ss";
	_fmt.ShortDateFormat = "mm/dd/yyyy hh:nn:ss";
	AnsiString ts = FormatDateTime("mm/dd/yyyy hh:nn:ss", timestamp, _fmt);
	hederi["vreme"]=ts;
	hederi["greska"] = greska ? "da" : "ne";
	hederi["rezultat"] = rezultat ? "da" : "ne";
	PosaljiPoruku(hederi, sta.t_str(), uid, statusCallback, false);
}

void TZec::PosaljiTekst(AnsiString uid, AnsiString tekst, bool kratkotrajno,
	CallbackStatus statusCallback)
{
	Bloker();
	std::map<AnsiString, AnsiString> hederi;
	TDateTime timestamp = Now();
	_fmt.DateSeparator = '/';
	_fmt.LongDateFormat = "mm/dd/yyyy hh:nn:ss";
	_fmt.ShortDateFormat = "mm/dd/yyyy hh:nn:ss";
	AnsiString ts = FormatDateTime("mm/dd/yyyy hh:nn:ss", timestamp, _fmt);
	hederi["vreme"]=ts;
	hederi["tip"] = TipPorukeUStringInternal(tpPoruka);
	PosaljiPoruku(hederi, tekst.c_str(), uid, statusCallback, false, "10000");
}

void TZec::StatusSinhronizacije(std::map<AnsiString, AnsiString> stanja, CallbackStatus statusCallback)
{
	TZec::Bloker();
	if (TZec::_stopZec)
	{
		return;
	}
	std::map<AnsiString, AnsiString> hederi;
	TDateTime timestamp = Now();
	_fmt.DateSeparator = '/';
	_fmt.LongDateFormat = "mm/dd/yyyy hh:nn:ss";
	_fmt.ShortDateFormat = "mm/dd/yyyy hh:nn:ss";
	AnsiString ts = FormatDateTime("mm/dd/yyyy hh:nn:ss", timestamp, _fmt);

	hederi["vreme"]=ts;
	hederi["tip"] = TipPorukeUStringInternal(tpStanjeSinhro);
	hederi["pib"] = TZec::_info._pib;
	hederi["hostname"] = TZec::_info._defMagacin + " - " + TZec::_info._hostname;
	hederi["uid"] = TZec::_info._uid;
	hederi["ver"] = TZec::_info._verzijaMP;

	AnsiString telo = "";
	std::map<AnsiString, AnsiString>::iterator it;
	for (it = stanja.begin(); it != stanja.end(); ++it)
	{
		hederi[it->first] = it->second;
	}
	PosaljiPoruku(hederi, telo.c_str(), "bus.fan", statusCallback, true, 60000);
}


void TZec::PokreniUpdate(AnsiString uid, TPodesavanja* updejtPodesavanja,
	 CallbackStatus statusCallback)
{
	Bloker();
	std::map<AnsiString, AnsiString> hederi;
	hederi["tip"] = TipPorukeUStringInternal(tpUpdate);

	hederi["hitno"] = "ne";
	hederi["nag"] = "ne";
	hederi["poruka"] = updejtPodesavanja->_poruka;
	hederi["zakazano"] = "";
	hederi["backupbaze"] = updejtPodesavanja->_backupBaze ? "da" : "ne";
	hederi["backupapp"] = updejtPodesavanja->_backupApp ? "da" : "ne";
	hederi["restart"] = updejtPodesavanja->_restart ? "da" : "ne";
	hederi["fajlovi"] = updejtPodesavanja->_fajlovi ? "da" : "ne";
	hederi["skripte"] = updejtPodesavanja->_skripte ? "da" : "ne";
	switch(updejtPodesavanja->_tipUpdejta)
	{
		case tuPokretanje:
			hederi["nag"] = updejtPodesavanja->_porukaPonavljanje ?
				"da" : "ne";
		break;
		case tuOdmah:
			hederi["hitno"] = "da";
		break;
		case tuZakazan:
			_fmt.ShortDateFormat = "mm/dd/yyyy hh:nn:ss";
			hederi["zakazano"] = FormatDateTime("mm/dd/yyyy hh:nn:ss",
				updejtPodesavanja->_vreme, _fmt);
		break;
	}
	PosaljiPoruku(hederi, "", uid, statusCallback, false, _8sati);
}
void TZec::Diskonektuj()
{
	if (TZec::_dllWrapper != nullptr) {
		_dllWrapper->_stop = true;
		try
		{
			_dllWrapper->Diskonekcija();
        }catch(...){}
		delete TZec::_dllWrapper;
		TZec::_dllWrapper = nullptr;
	}
	if (TZec::_helperZec != nullptr) {
		delete TZec::_helperZec;
		TZec::_helperZec = nullptr;
	}
	if (TZec::_loggerZec != nullptr) {
		delete TZec::_loggerZec;
		TZec::_loggerZec = nullptr;
	}
	if (TZec::_pingerZec != nullptr) {
		delete TZec::_pingerZec;
		TZec::_pingerZec = nullptr;
	}
}

void TZec::PosaljiFajlove(AnsiString uid,
	 CallbackStatus statusCallback)
{
	Bloker();
	std::map<AnsiString, AnsiString> hederi;
	hederi["tip"] = TipPorukeUStringInternal(tpUploadFiles);
	hederi["primaoc"] = _dllWrapper->_uid;
	PosaljiPoruku(hederi, "", uid, statusCallback, false, "5000");
}
void TZec::PosaljiBazu(AnsiString uid,
	 CallbackStatus statusCallback)
{
	Bloker();
	std::map<AnsiString, AnsiString> hederi;
	hederi["tip"] = TipPorukeUStringInternal(tpUploadDb);
	hederi["primaoc"] = _dllWrapper->_uid;
	PosaljiPoruku(hederi, "", uid, statusCallback, false, "5000");
}
void TZec::PreuzmiFajl(AnsiString uid, AnsiString naziv, AnsiString fajl,
	 CallbackStatus statusCallback)
{
	Bloker();
	std::map<AnsiString, AnsiString> hederi;

	TDateTime timestamp = Now();
	_fmt.DateSeparator = '/';
	_fmt.LongDateFormat = "mm/dd/yyyy hh:nn:ss";
	_fmt.ShortDateFormat = "mm/dd/yyyy hh:nn:ss";
    AnsiString ts = FormatDateTime("mm/dd/yyyy hh:nn:ss", timestamp, _fmt);
    hederi["vreme"]=ts;
	hederi["pib"] = TZec::_info._pib;
	hederi["hostname"] = TZec::_info._defMagacin + " - " + TZec::_info._hostname;
	hederi["ver"] = TZec::_info._verzijaMP;
	hederi["uid"] = _info._uid;

	hederi["tip"] = TipPorukeUStringInternal(tpDownload);
	hederi["naziv"] = naziv.c_str();
	hederi["fajl"] = fajl.c_str();
	PosaljiPoruku(hederi, "", uid, statusCallback, false, _sat);
}
void TZec::PrijaviOperaciju(AnsiString uid, TipPoruke operacija, bool uspeh,
	 AnsiString poruka, CallbackStatus statusCallback)
{
	Bloker();
	std::map<AnsiString, AnsiString> hederi;
    TDateTime timestamp = Now();
	_fmt.DateSeparator = '/';
	_fmt.LongDateFormat = "mm/dd/yyyy hh:nn:ss";
	_fmt.ShortDateFormat = "mm/dd/yyyy hh:nn:ss";
	AnsiString ts = FormatDateTime("mm/dd/yyyy hh:nn:ss", timestamp, _fmt);
	hederi["vreme"]=ts;
	hederi["pib"] = TZec::_info._pib;
	hederi["hostname"] = TZec::_info._defMagacin + " - " + TZec::_info._hostname;
	hederi["ver"] = TZec::_info._verzijaMP;
	hederi["tip"] = TipPorukeUStringInternal(tpKrajOperacije);
	hederi["uid"] = _info._uid;
	hederi["operacija"] = TipPorukeUStringInternal(operacija);
	hederi["uspeh"] = uspeh ? "da" : "ne";
	hederi["poruka"] = poruka;
	if (uid.IsEmpty())
	{
		PosaljiPoruku(hederi, "", "bus.fan", statusCallback, true, _sat);
	}else
	{
		PosaljiPoruku(hederi, "", uid, statusCallback, false, _sat);
	}

}
