/*
    PIP - Platform Independent Primitives
    Initialization
    Copyright (C) 2016  Ivan Pelipenko peri4ko@gmail.com

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU 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 General Public License for more details.

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

#include "piplatform.h"
#include "piinit.h"
#include "pisignals.h"
#include "piobject.h"
#include "pisysteminfo.h"
#include "pidir.h"
#include "piprocess.h"
#ifdef WINDOWS
#  include <winsock2.h>
   extern FILETIME __pi_ftjan1970;
#else
#  include <pwd.h>
#  include <sys/utsname.h>
#  include <pthread.h>
#  ifdef BLACKBERRY
#    include <signal.h>
#  else
#    include <csignal>
#  endif
#endif
#ifdef MAC_OS
#  include <mach/mach_traps.h>
#  include <mach/mach.h>
#  include <mach/clock.h>
//#  include <crt_externs.h>
   extern clock_serv_t __pi_mac_clock;
#endif
#ifdef PIP_ICU
#  include <unicode/uclean.h>
#  include <unicode/ucnv.h>
#endif
/*
#ifdef WINDOWS
#  include <conio.h>
#  include <io.h>
#  include <windows.h>
#  include <wincon.h>
#else
#  include <fcntl.h>
#  include <sys/ioctl.h>
#  include <pthread.h>
#endif*/


#ifdef HAS_LOCALE
static locale_t currentLocale_t = 0;
#endif


void __sighandler__(PISignals::Signal s) {
	//piCout << Hex << int(s);
	if (s == PISignals::StopTTYInput || s == PISignals::StopTTYOutput)
		piMSleep(10);
	if (s == PISignals::UserDefined1)
		dumpApplicationToFile(PIDir::home().path() + PIDir::separator + PIStringAscii("_PIP_DUMP_") + PIString::fromNumber(PIProcess::currentPID()));
}


#ifdef ANDROID
void android_thread_exit_handler(int sig) {
	pthread_exit(0);
}
#endif


PIInit::PIInit() {
	PISystemInfo * sinfo = PISystemInfo::instance();
	sinfo->execDateTime = PIDateTime::current();
	setFileCharset("UTF-8");
#ifndef ANDROID
	PISignals::setSlot(__sighandler__);
	PISignals::grabSignals(PISignals::UserDefined1);
#  ifndef WINDOWS
	PISignals::grabSignals(PISignals::StopTTYInput | PISignals::StopTTYOutput);
	sigset_t ss;
	sigemptyset(&ss);
	sigaddset(&ss, SIGALRM);
	sigprocmask(SIG_BLOCK, &ss, 0);
	pthread_sigmask(SIG_BLOCK, &ss, 0);
	signal(SIGPIPE, SIG_IGN);
	PIStringList ifpathes;
	ifpathes << PIStringAscii("/bin/ifconfig") << PIStringAscii("/sbin/ifconfig")
			<< PIStringAscii("/usr/bin/ifconfig") << PIStringAscii("/usr/sbin/ifconfig");
	piForeachC (PIString & i, ifpathes)
		if (fileExists(i)) {
			sinfo->ifconfigPath = i;
			piBreak;
		}
#  else
	// OS version
	DWORD dwVersion = GetVersion();
	DWORD dwMajorVersion = (DWORD)(LOBYTE(LOWORD(dwVersion)));
	DWORD dwMinorVersion = (DWORD)(HIBYTE(LOWORD(dwVersion)));
	sinfo->OS_version = PIString(dwMajorVersion) + "." + PIString(dwMinorVersion);
	
	// WinSock inint
	WSADATA wsaData;
	WSAStartup(MAKEWORD(2, 2), &wsaData);
	
	// Timers init
	SYSTEMTIME jan1970 = {1970, 1, 4, 1, 0, 14, 15, 0};
	SystemTimeToFileTime(&jan1970, &__pi_ftjan1970);
	LARGE_INTEGER pf;
	pf.QuadPart = -1;
	if (QueryPerformanceFrequency(&pf) != 0) __pi_perf_freq = pf.QuadPart;
	if (__pi_perf_freq == 0) __pi_perf_freq = -1;
	
	// Sleep precision init
	ntlib = LoadLibrary("ntdll.dll");
	if (ntlib) setTimerResolutionAddr = (PINtSetTimerResolution)GetProcAddress(ntlib, "NtSetTimerResolution");
	/*if (setTimerResolution) setTimerResolutionAddr(1, TRUE, &prev_res);*/
#  endif
	//piDebug = true;
#  ifdef HAS_LOCALE
	//cout << "has locale" << endl;
	if (currentLocale_t != 0) {
		freelocale(currentLocale_t);
		currentLocale_t = 0;
	}
	currentLocale_t = newlocale(LC_ALL, setlocale(LC_ALL, ""), 0);
#  else
	setlocale(LC_ALL, "");
	setlocale(LC_NUMERIC, "C");
#  endif
#else
	struct sigaction actions;
	memset(&actions, 0, sizeof(actions));
	sigemptyset(&actions.sa_mask);
	actions.sa_flags = 0;
	actions.sa_handler = android_thread_exit_handler;
	sigaction(SIGTERM, &actions, 0);
#endif
#ifdef PIP_ICU
	__syslocname__ = __sysoemname__ = 0;
	//__syslocname__ = new char[256];
	//memset(__syslocname__, 0, 256);
	UErrorCode e((UErrorCode)0);
	u_init(&e);
#  ifdef WINDOWS
	CPINFOEX cpinfo;
	int l = 0;
	/*GetCPInfoEx(CP_ACP, 0, &cpinfo);
	for (l = 0; l < MAX_PATH; ++l)
		if (cpinfo.CodePageName[l] == '\0' || cpinfo.CodePageName[l] == ' ')
			break;
	memcpy(__syslocname__, "windows-", 8);
	memcpy(&(__syslocname__[8]), cpinfo.CodePageName, l);*/
	GetCPInfoEx(CP_OEMCP, 0, &cpinfo);
	for (l = 0; l < MAX_PATH; ++l)
		if (cpinfo.CodePageName[l] == '\0' || cpinfo.CodePageName[l] == ' ')
			break;
	__sysoemname__ = new char[256];
	memset(__sysoemname__, 0, 256);
	memcpy(__sysoemname__, "ibm-", 4);
	memcpy(&(__sysoemname__[4]), cpinfo.CodePageName, l);
	//piCout << cpinfo.CodePageName;
#  else
	/*PIString en(getenv("LANG"));
	if (!en.isEmpty())
		en = en.mid(en.find(".") + 1);
	PIByteArray enba = en.toByteArray();
	memcpy(__syslocname__, enba.data(), enba.size_s());*/
#  endif
	//piCout << __syslocname__;
	//piCout << __sysoemname__;
#endif
#ifdef MAC_OS
	host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &__pi_mac_clock);
#endif
	char cbuff[1024];
	memset(cbuff, 0, 1024);
	if (gethostname(cbuff, 1023) == 0)
		sinfo->hostname = cbuff;
#ifdef WINDOWS
	SYSTEM_INFO sysinfo;
	GetSystemInfo(&sysinfo);
	sinfo->processorsCount = sysinfo.dwNumberOfProcessors;
	switch (sysinfo.wProcessorArchitecture) {
	case PROCESSOR_ARCHITECTURE_AMD64: sinfo->architecture = PIStringAscii("x64"); break;
	case PROCESSOR_ARCHITECTURE_ARM: sinfo->architecture = PIStringAscii("ARM"); break;
	case PROCESSOR_ARCHITECTURE_IA64: sinfo->architecture = PIStringAscii("Intel Itanium-based"); break;
	case PROCESSOR_ARCHITECTURE_INTEL: sinfo->architecture = PIStringAscii("x86"); break;
	case PROCESSOR_ARCHITECTURE_UNKNOWN:
	default: sinfo->architecture = PIStringAscii("unknown"); break;
	}
	int argc_(0);
	wchar_t ** argv_ = CommandLineToArgvW(GetCommandLineW(), &argc_);
	if (argc_ > 0 && argv_ != 0)
		sinfo->execCommand = argv_[0];
	LocalFree(argv_);
	memset(cbuff, 0, 1024);
	ulong unlen = 1023;
	if (GetUserName(cbuff, &unlen) != 0)
		sinfo->user = cbuff;
#else
	sinfo->processorsCount = piMaxi(1, int(sysconf(_SC_NPROCESSORS_ONLN)));
	passwd * ps = getpwuid(getuid());
	if (ps)
		sinfo->user = ps->pw_name;
	else {
		memset(cbuff, 0, 1024);
		char * l = getlogin();
		if (l)
			sinfo->user = l;
	}
	struct utsname uns;
	if (uname(&uns) == 0) {
		sinfo->OS_version = uns.release;
		sinfo->architecture = uns.machine;
	}
#endif
	sinfo->OS_name =
#ifdef WINDOWS
		PIStringAscii("Windows");
#else
#  ifdef QNX
		PIStringAscii("QNX");
#  else
#    ifdef MAC_OS
		PIStringAscii("MacOS");
#    else
#      ifdef ANDROID
		PIStringAscii("Android");
#      else
#        ifdef FREE_BSD
		PIStringAscii("FreeBSD");
#        else
		uns.sysname;
#        endif
#      endif
#    endif
#  endif
#endif
}


PIInit::~PIInit() {
	if (file_charset) delete file_charset;
	file_charset = 0;
#ifdef WINDOWS
	WSACleanup();
	//if (setTimerResolution) setTimerResolutionAddr(prev_res, TRUE, &prev_res);
	if (ntlib) FreeLibrary(ntlib);
	ntlib = 0;
#endif
#ifdef MAC_OS
	mach_port_deallocate(mach_task_self(), __pi_mac_clock);
#endif
#ifdef PIP_ICU
	if (__syslocname__) delete __syslocname__;
	if (__sysoemname__) delete __sysoemname__;
	u_cleanup();
#endif
	//if (currentLocale_t != 0) freelocale(currentLocale_t);
}


void PIInit::setFileCharset(const char *charset) {
	if (file_charset) delete file_charset;
	file_charset = 0;
	if (charset) {
		file_charset = new char[1024];
		memset(file_charset, 0, 1024);
		strcpy(file_charset, charset);
	}
}


bool PIInit::fileExists(const PIString & p) {
	FILE * f = fopen(p.data(), "r");
	if (f == 0)
		return false;
	fclose(f);
	return true;
}



int __PIInit_Initializer__::count_(0);
PIInit * __PIInit_Initializer__::__instance__(0);


__PIInit_Initializer__::__PIInit_Initializer__() {
	count_++;
	if (count_ > 1) return;
	//piCout << "create PIInit";
	__instance__ = new PIInit();
}


__PIInit_Initializer__::~__PIInit_Initializer__() {
	count_--;
	if (count_ > 0) return;
	//piCout << "delete PIInit";
	if (__instance__ != 0) {
		delete __instance__;
		__instance__ = 0;
	}
}
