/*
//---------------- set of system functions to use in a final (compiled) project
// This file should be copied to the destination directory if it is necessary.
// To do so, use an option /copy System_functions.cs (source directory should
// be listed with /src option).

CLASS {System_functions|_Gcc},
	NATIVE, UNTESTED, BYTES, STOPPING, BYTES,
	ABSTRACT :

IMPORT: {String_functions}, {File_path} ;

NATIVE: "#include <iostream>" .
NATIVE: "#include <fstream>" .
NATIVE: @
        "#ifdef MSWINDOWS"
        "#include <shlobj.h>"
        "#else"
        "//#include <termcap.h> // -ltermcap or -ltermlib also..."
        "#endif" .
NATIVE: @
        "#ifdef MSWINDOWS"
        "   #include <windows.h>"
        "#endif" .
NATIVE: @
        "#ifdef MSWINDOWS"
        "#else"
        "   #include <pwd.h>"
        "   #include <netdb.h>"
        "   #include <netinet/in.h>"
        "   #include <arpa/inet.h>"
        "   #include <sys/socket.h>"
        "#endif" .

NATIVE: "///copy System_functions.cpp" .
NATIVE: "///copy System_functions.h" .

*/

#ifdef SEPARATE_UNITS
    #include "System_functions.h"
#endif
#include <cstdlib>
#include <string>

std::string System_functions::Operating_system()
{
    #ifdef LINUX
           return "Linux";
           #define OS_OK
    #endif
    #ifdef OS_X
           return "OS X";
           #define OS_OK
    #endif
    #ifdef iOS
           return "OS X";
           #define iOS
    #endif
    #ifdef ANDROID
           return "Android";
           #define OS_OK
    #endif
    #ifdef QNX
           return "QNX";
           #define OS_OK
    #endif
    #ifndef OS_OK
           #ifdef STDSTRINGS
                  return std::string("Win32");
           #else
                  return std::string("Windows");
           #endif
    #endif
}

/*

FUN Platform_info|rmation ==> STR, NATIVE :
    STR s|tarting_static_part = "C++/gcc/"
    NATIVE @
    "	RESULT = X_s + System_functions::Operating_system();"
    .
    
FUN TEST|_stage ==> BOOL, NATIVE :
@   "   RESULT = AL4_system::Is_TEST_stage();" .    
*/

std::string System_functions::test_command_line;
int System_functions::Max_recursion_deep = 128;

/*

FUNCTION Maximum_recursion|_deep ==> INT, NATIVE :
@	"	RESULT = System_functions::Max_recursion_deep;" .

FUN Set_maximum_recursion|_deep(
	INT Desired|_value), NATIVE :
	CASE Desired > 10240 ? INT value|_to_set = 10240
	ELSE value = Desired ;
	NATIVE @
	"	System_functions::Max_recursion_deep = X_value;" .

----------------------------------------- 'MAX_INT, MIN_INT'

CONST INT :
    MAX_INT|EGER_VALUE = 0x7FFF_FFFF
    MIN_INT|EGER_VALUE = 0x8FFF_FFFF .

FUN Count_param|eters_in_command_line ==> INT,
NATIVE, CALLBACK : // CALLBACK is necessary to prevent removing due optimize
@   "   RESULT = AL4_system::Parameters.size();" .

FUN Parameter|_in_command_line_by_index(
INT Index|_of_parameter) ==> STR
	:
	RESULT = do_get_parameter(Index) .
FUN do_get_parameter(INT Index|_parameter) ==> STR, NATIVE :
	INT max_index = Count_param
    NATIVE @
    "	if (X_Index >= 0 && X_Index < X_max_index)"   
	"   	RESULT = (std::string)AL4_system::Parameters[X_Index];" .

FUNCTION Get_command_line ==> STR, NATIVE,
    DEPRECATED('Parameter, Count_param') :
   @"#ifdef MSWINDOWS"
    "    if (System_functions::test_command_line.length() > 0)"
    "        RESULT = System_functions::test_command_line;"
    "    else RESULT = System_functions::get_command_line();" 
    "#endif" .
*/

#ifdef MSWINDOWS
std::string System_functions::get_command_line() 
{
	std::string Result = GetCommandLineA();
    return Result;
}
#endif

/*

FUNCTION Change_command_line(STR Val|ue_to_set), NATIVE :
    "    System_functions::test_command_line = X_Val;" .


FUN Set_working_dir|ectory(STR Path|_directory), NATIVE :
    "    System_functions::set_working_dir(X_Path);" .
*/
void System_functions::set_working_dir(const std::string& Path_dir)
{
	std::string path;
    int len = (int)Path_dir.length(); 
    for (int i = 0; i < len; i++)
        path += Path_dir[i];
        
    #ifdef MSWINDOWS
        LPCSTR s = path.c_str();
        SetCurrentDirectoryA(s);
    #else
        chdir(path.c_str());
    #endif
}

/*

FUN Get_working_dir|ectory ==> STR :
  = do_Get_working_dir.Trailing_slash .
FUN do_Get_working_dir ==> STR, NATIVE : @
    "    RESULT = System_functions::get_working_dir();" .
*/
std::string System_functions::get_working_dir()
{
	char buf[MAX_PATH];
    
    #ifdef MSWINDOWS
        GetCurrentDirectoryA(MAX_PATH, buf);
        return std::string(buf);
    #else
        std::string Result = std::string(getcwd(buf, MAX_PATH));
        return Result;
    #endif
}


/*

FUN Get_windows_dir|ectory ==> STR, NATIVE :
    "    RESULT = System_functions::get_windows_dir();" .
*/
std::string System_functions::get_windows_dir()
{
    #ifdef MSWINDOWS
        char buf[MAX_PATH];
        GetWindowsDirectoryA(buf, sizeof(buf));
        return std::string(buf);
    #else
        return std::string("");
    #endif
}

/*

FUN Get_system_drive ==> STR :
    RESULT = Get_windows_dir[0 TO 1] "\"
    CASE RESULT[1] != ":" ? RESULT = "C:\" ; .
    

FUN Execute_program(STR App|lication_path, STR Param|eters,
        INT Redirect|_output_error) ==> STR, NATIVE :
    ----------------------- 'prevent executing any program in TEST mode'
    CASE TEST ?
         STOP "In TEST mode, can not execute external programs" ;
    NATIVE @" RESULT = System_functions::do_execute_program("
            "          X_App, X_Param, X_Redirect);" .
*/
std::string System_functions::do_execute_program(const std::string& App, 
    const std::string& Param, const int Redirect)
{
    std::string Result;
    #ifdef MSWINDOWS
    try
    {
        SECURITY_ATTRIBUTES sa;
        sa.nLength = sizeof(sa);
        sa.lpSecurityDescriptor = NULL;
        sa.bInheritHandle = TRUE;

        HANDLE rPipe, wPipe;

        CreatePipe(&rPipe, &wPipe, &sa, 0);

        PROCESS_INFORMATION pi;
        STARTUPINFOA si;

        ZeroMemory(&pi, sizeof(pi));
        ZeroMemory(&si, sizeof(si));

        si.cb = sizeof(si);
        si.dwFlags = STARTF_USESTDHANDLES;
        if ((Redirect & 1) != 0)
            si.hStdOutput = wPipe;
        if ((Redirect & 2) != 0)
            si.hStdError = wPipe;

        TCHAR cdir[MAX_PATH];
        GetCurrentDirectoryA(MAX_PATH, (LPSTR)cdir);
        std::string cmd = App + " " + Param;
        std::string wCmd;
        
        int len = (int)cmd.length();
        for (int i = 0; i < len; i++)
        wCmd += cmd[i];
        const char* tCmd_w = wCmd.c_str();
        LPSTR tCmd = (LPSTR)tCmd_w;

        BOOL ok =
        	CreateProcessA
        	(
        	NULL, tCmd, NULL, NULL, TRUE, 
        	NORMAL_PRIORITY_CLASS, NULL,
        	(LPCSTR)cdir,
            &si, &pi);
        CloseHandle(wPipe);

        if (ok && Redirect != 0)
        {
            char buf[1024];
            DWORD n;
            do
            {
                ok = ReadFile(rPipe, buf, sizeof(buf)-1, &n, NULL);
                buf[n] = (char)0;
                Result += buf;
            } while (ok);
        }
        else
        {
            WaitForSingleObject(pi.hThread, INFINITE);
        }

        CloseHandle(rPipe);
        CloseHandle(pi.hThread);
        CloseHandle(pi.hProcess);

    }
    catch (std::exception& ex) {}
    #else
        std::string cmd = App;
        if (Param.length() > 0) cmd += std::string(" ") + Param;
    	//AL4_system::WriteLine(std::string("executing: ") + cmd);
    	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    	if (Redirect == 0)
    		system(&cmd[0]);
    	else
    	{
    		FILE* rpopen = popen(&cmd[0], "r");
    		if (rpopen)
    		{
    			char buff[512];
    			while(fgets(buff, sizeof(buff), rpopen) != NULL)
    				Result += buff;
    			pclose(rpopen);
    		}
    	}
    #endif
    return Result;
}

/*

FUN Start_program(STR App|lication_path, STR Param|eters) :
    CASE TEST ? STOP "In TEST mode, can not start external programs" ;
    start_program(App, Param) .

FUN start_program(STR App|lication_path, STR Param|eters),
        NATIVE :
    "    System_functions::do_start_program(X_App, X_Param);" .
*/
void System_functions::do_start_program(const std::string& App, const std::string& Param)
{
    #ifdef MSWINDOWS
	    std:: string cmd = App + " " + Param;
	    char* ccmd = &cmd[0u];
	    WinExec(ccmd, SW_SHOW);
    #else
        std::string cmd = std::string("start ") + App + std::string(" ") + Param;
        system(cmd.c_str());
    #endif
}

/*

FUN Ref_count({Object} Obj|ect_inspecting) ==> INT, NATIVE :
    "RESULT = X_Obj->Count;" .

FUN Stop_if_test(STR T|est_must_be_present) :
    CASE !TEST ? ==> ;
    CASE "test" IN T.Lower ? ==> ;
    
    STOP "In test mode files can not be changed unless these have "
         "'test' substring in a name or a path".
         
FUNCTION Sleep|_milliseconds(INT M|illiseconds), NATIVE :
@   "#ifdef MSWINDOWS"
    "    Sleep(X_M);"
    "#else"
    "    usleep(1000L * (long)X_M);"
    "#endif" .         
//@   "   Sleep(X_M); " .

FUN Get_environment_var|iable(
    STR Name|_of_variable) ==> STR, NATIVE :
   @"  RESULT = System_functions::do_get_environment_var(X_Name);" .
*/
std::string System_functions::do_get_environment_var(const std::string& Name)
{
    #ifdef MSWINDOWS
        std ::string AsciiName;
        AsciiName = Name;
        char* name = &AsciiName[0u];
        size_t size = GetEnvironmentVariableA(name, NULL, 0);
        if (size == 0) return std::string("");
        char *buf = (char*)malloc(size);
        GetEnvironmentVariableA(name, buf, size);
        std::string Result = std::string(buf);
        free(buf);
    #else
        const char * val = ::getenv( Name.c_str() );
        if ( val == 0 )
            return std::string("");
        std::string Result(val);
    #endif
	return Result;
}

/*

FUNCTION Start_dir ==> STR :
    RESULT = startup_path.Trailing_slash .

FUN startup_path ==> STR, NATIVE :
    "    RESULT = System_functions::start_dir();" .
    
    
FUNCTION Application_name ==> STR :
    RESULT = startup_path.Extract_file_name .

FUNCTION Get_application_data_dir ==> STR :
    RESULT = (get_special_dir(APPLICATION_DATA).Trailing_slash
    "" Application_name).Trailing_slash .
       
FUN Get_recent_files_dir ==> STR :
    RESULT = get_special_dir(RECENT).Trailing_slash .
       
    
CONST INT {special_folders} :
      APPLICATION_DATA = 26
      COMMON_APPLICATION_DATE = 35
      COMMON_DESKTOP_DIR|ECTORY = 25
      COMMON_DOCUMENTS = 46
      COMMON_FAVORITES = 31
      COMMON_PROGRAMS = 32
      COMMON_START_MENU = 22
      COMMON_STARTUP = 24
      COOKIES|_DIRECTORY = 33
      DESKTOP_DIR|ECTORY = 16
      FONTS|_DIRECTORY = 20
      HISTORY|_DIRECTORY = 34
      INTERNET_CACHE = 32
      LOCAL_APPLICATION_DATA = 28
      MY_DOCUMENTS = 5
      MY_PICTURES = 39
      NETWORK_SHORTCUTS = 19
      NON_LOCALIZED_STATUP = 29
      PRINTERS = 4
      PRINT_HOOD = 27
      PROGRAMS = 2
      RECENT|_FILES = 8
      RECYCLE_BIN = 10
      SEND_TO|_DIRECTORY = 9
      START_MENU = 11
      STARTUP|_DIRECTORY = 7
      SYSTEM|_DIRECTORY = 37
      USER_PROFILE = 40
      WINDOWS|_DIRECTORY = 36 .
              

FUN get_special_dir|ectory(
INT Sp|ecial_filder_index ) ==> STR, 
NATIVE, IF Sp IS CONST, THEN Sp IN {special_folders} :
   @"    char buf[MAX_PATH];"
    "#ifdef MSWINDOWS"
    "    buf[0] = (char)0;"
    "    SHGetFolderPathA(0, X_Sp, NULL, //SHGFP_TYPE_CURRENT"
    "		0, buf);"
    "    RESULT = buf;"
    "#else //--------------------------------- LINUX"
    "   switch(X_Sp)"
    "   {"
    "   case 5: // MY_DOCUMENTS --> User's HOME directory"
    "   case 16: // DESKTOP_DIR|ECTORY --> User's HOME directory"
    "       struct passwd* pw = getpwuid(getuid());"
    "       RESULT = std::string(pw->pw_dir);"
    "       break;"
    "   }"
    "#endif"
    .
    
*/
#ifndef MSWINDOWS
    #include <pwd.h>
#endif

std::string System_functions::start_dir()
{
    #ifdef MSWINDOWS
	    std::string Result = get_command_line();
		if (Result.length() == 0) return get_working_dir();
		if (Result[0] == '\"')
		{
	        int len = (int)Result.length();
			for (int i = 1; i < len; i++)
				if (Result[i] == '\"')
				{
					Result = Result.substr(1, i-2);
					break;
				}
		}
		else
		{
	        int len = (int)Result.length();
			for (int i = 1; i < len; i++)
				if (Result[i] == ' ')
				{
					Result = Result.substr(0, i);
					break;
				}
		}
		for (int i = Result.length()-1; i >= 0; i--)
			if (Result[i] == '\\' || Result[i] == '/')
			{
				Result = Result.substr(0, i);
				break;
			}
		return Result;
    #else //----------------------------------------- LINUX
        char buf[MAX_PATH];
        if (readlink("/proc/self/exe", buf, MAX_PATH) > 0)
        {
	        std::string Result(buf);
	        for (int i = Result.length()-1; i > 0; i--)
	            if(Result[i] == '/')
	            {
	                Result = Result.substr(0, i);
	                break;
	            }
			return Result;
        }
        return "";
    #endif
}

/*
---------------------------------------- 'environment'

FUNCTION Computer_name ==> STR, NATIVE :
@   "   #ifdef MSWINDOWS"
    "		char buf[MAX_PATH];"
    "		DWORD Sz = MAX_PATH;"
    "		GetComputerNameA((LPSTR)buf, &Sz);"
    "		std::string wResult = buf;"
    "   #else //------------------------------- LINUX"
    ''   	const char* ip_str = "127.0.0.1";''
    "   	struct in_addr ip;"
    "   	struct hostent* h;"
    "   	if (!inet_aton(ip_str, &ip))"
    "       	return RESULT;"
    "   	if ((h = gethostbyaddr((const void *)&ip, sizeof ip, AF_INET))"
    "       	== NULL)"
    "       	return RESULT;"
    "   	RESULT = std::string(h->h_name);"
    "   #endif"
    .

FUNCTION Allocate_bytes(
BYTE Buffer|_bytes[], INT Size|_allocate ), NATIVE :
    "    X_Buffer->Reallocate(X_Size);" .
    
FUN Save_DynAStr_to_file(
STR S|trings_to_save[], STR Path|_file ),
    NATIVE :
   @"    std::fstream fs(&X_Path[0], std::ios_base::out"
    "        | std::ios_base::trunc"
    "        | std::ios_base::binary);"
    "    if (!fs.is_open()) return;"
    "    for(int i = 0; i < X_S->Count(); i++)"
    "    {"
    "       std::string s = X_S->get(i);"
    "       fs << s << " #QU "\r\n" #QU ";"
    "	 }"
    "    fs.close();" .
    
FUN Total_obj_index(
{Object} Obj|ect_of_any_class ) ==> INT, NATIVE :
   @"    RESULT = X_Obj->Obj->TOTAL_obj_index;" .
   
FUN Absolute_path( STR Rel|ative_path ) ==> STR :
    CASE ":" IN Rel ? RESULT = Rel ==> ;
    RESULT = Get_working_dir.Trailing_slash Rel
    FOR i IN [0 TO RESULT.Len] :
        INT pos|ition_dot_dot = RESULT.Find(Path_sep "..")
        CASE pos < 0 ? ==> ;
        FOR j IN [pos-1 DOWNTO 0] :
            CASE RESULT[j] == Path_sep ?
                 RESULT = RESULT[0 TO j-1] RESULT[pos+3 TO *]
                 BREAK j, CONTINUE i ;
        ;
        ==> ; .
    

END
*/

