/*
//---------------- 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|_CSharp}, 
NATIVE, UNTESTED, BYTES, STOPPING, BYTES, BITWISE,
ABSTRACT :

IMPORT: {String_functions}, {File_path}, {Configuration} ;

NATIVE: "using System.Diagnostics;" .
NATIVE: "using System.IO;" .

NATIVE: "///copy System_functions.cs" .


*/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using AL4;

namespace AL4_project
{
    public class System_functions
    {
        
        /*
        ---------------------------------- 'Platform dependent information'
        
        FUN Is_int32 ==> BOOL : = 3.ShiftL(31) < 0 .
        
        FUN Is_int64 ==> BOOL : = !Is_int32 .
        
        
        FUN Platform_info|rmation ==> STR :
            RESULT = "C#;.Net(" Dot_net_version 
            	");Windows(" Windows_version ")" .
            	
        FUN platform_info|_lowered ==> STR, PUBLIC :
            RESULT = Platform_info.Lower .
            	
		FUNCTION Dot_net_version ==> STR, NATIVE :
		@	"	RESULT = Environment.Version.ToString();" .
		
		FUNCTION Windows_ver|sion ==> STR :
			RESULT = windows_ver
			CASE "Windows NT 6.2" IN RESULT ? 
			     {Config} reg|istry_temp = New_read_config(
			     	"", 'MACHINE', "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\"
			     	"Windows NT\CurrentVersion")
			     	
			     RESULT = "Windows " reg.Int("CurrentMajorVersionNumber").S 
			     	"." reg.Int("CurrentMinorVersionNumber").S ", " _
			; .
			
		FUNCTION windows_ver|sion ==> STR, NATIVE :
		@	"	RESULT = Environment.OSVersion.ToString();"
		.

        FUN TEST|_stage ==> BOOL, NATIVE :
        @   "   RESULT = AL4_system.Is_TEST_stage();" .
        
		FUNCTION Maximum_recursion|_deep ==> INT, NATIVE :
		@	"	RESULT = AL4_system.Max_recursion_deep;" .
		
		FUN Set_maximum_recursion|_deep(
			INT Desired|_value), NATIVE :
			CASE Desired > 10240 ? INT value|_to_set = 10240
			ELSE value = Desired ;
			NATIVE @
			"	AL4_system.Max_recursion_deep = (int)X_value;"
			"	long[] old = AL4_system.stack_functions;"
			"	AL4_system.stack_functions = new long[X_value * 2];"
			"	for (int i = 0; i < AL4_system.SP_fun; i++) {"
			"		if (i >= AL4_system.stack_functions.Length) break;"
			"		if (i >= old.Length) break;"
			"		AL4_system.stack_functions[i] = old[i];" 
			"}"
			.
        
*/

		public static string test_command_line = "";
/*		
        

-------------------------------- 'command line, environment variables'
 		       
		*/
		public static List<string> parameters = null;
		/*
        
 		FUN Count_param
 			|eters_in_command_line ==> INT, NATIVE :
		@   "   RESULT = System_functions.Get_count_param();" .
		
		*/
		public static int Get_count_param()
		{
			if (parameters != null) return parameters.Count;
			return Environment.GetCommandLineArgs().Length; 
		}
		/*
		
		FUN Parameter
			|_in_command_line_by_index(INT Index|_of_parameter) ==> STR
			:
		    CASE Index < 0 || Index >= Count_param ? ==> ;
		    RESULT = do_get_parameter(Index) .
		FUN do_get_parameter(INT Index|_of_parameter) ==> STR, NATIVE : @
		    "	RESULT = System_functions.Get_parameter((int)X_Index);" .
		*/	    
	    public static string Get_parameter(int X_Index)
	    {
	    	if (parameters != null)
	    	{
	    		if (X_Index < 0 || X_Index >= parameters.Count) return "";
	    		return parameters[X_Index];
	    	}
	    	if (X_Index < 0 || X_Index >= Environment.GetCommandLineArgs().Length)
	    		return "";
	    	return Environment.GetCommandLineArgs()[X_Index];
	    }
		/*       
	
        FUNCTION Get_command_line ==> STR, NATIVE,
        DEPRECATED('Parameter, Count_param') :
           @"    if (System_functions.test_command_line != " #QU #QU ")"
            "        RESULT = System_functions.test_command_line;"
            "    else RESULT = Environment.CommandLine;" .
        */
        public static string Get_command_line()
        {
            return Environment.CommandLine;
        }

        /*
        
        FUN Get_environment_var|iable(
            STR Name|_of_variable) ==> STR, NATIVE :
           @"  string Result = System_functions.Do_Get_environment_var(X_Name);"
            "  if (Result == null) Result = " #QU #QU ";"
            "  RESULT = Result; " .
        */
        
        
		[DllImport("kernel32.dll", CharSet = CharSet.Ansi)]
        public static extern uint GetEnvironmentVariable(
        	string lpName, [Out] StringBuilder lpBuffer, uint nSize);
                
        public static string Do_Get_environment_var(string Name)
        {
            string Result = Environment.GetEnvironmentVariable(Name);
            if (Result == null) {
            	StringBuilder value_s = new StringBuilder();
	            uint SizeNeeded = GetEnvironmentVariable(Name, value_s, 0);
	            if (SizeNeeded > 0) 
	            { 
	            	value_s.Capacity = (int)(SizeNeeded); 
		            GetEnvironmentVariable(Name, value_s, SizeNeeded);
		            Result = value_s.ToString();
	            }
	            else Result = "";
            }
            return Result;
        }

        /*
        
        FUNCTION Computer_name ==> STR, NATIVE :
           @"    RESULT = System.Environment.MachineName;" .
           
        FUNCTION User_name ==> STR, NATIVE :
            @"	RESULT = System.Environment.UserName;" .
           
        
----------------- 'special directories'
        
        FUN Set_working_dir|ectory(STR Path|_directory), NATIVE : @
        	"	if (Directory.Exists(X_Path))"
            "    	Directory.SetCurrentDirectory(X_Path);" .
        
        FUN Get_working_dir|ectory ==> STR, NATIVE :
            "    RESULT = Directory.GetCurrentDirectory() + '\\';" .
            
        FUN Work_dir ==> STR :
	        RESULT = Get_working_dir .
        
        FUN Get_windows_dir|ectory ==> STR, NATIVE :
            "    RESULT = System.IO.Path.GetDirectoryName"
            "(Environment.SystemDirectory);" .
		
-------- 'application directories'		
            
        FUN Start_dir|ectory_of_executable_if_available ==> STR :
            RESULT = argument_0.Extract_path.Trailing_slash .

        
        FUN argument_0 ==> STR, NATIVE : @
            "    RESULT = Environment.GetCommandLineArgs()[0];" .
        
        FUNCTION Application_name ==> STR :
			RESULT = argument_0.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 .
            
        FUN Get_temp_dir
            ||Get_temporary_dir|ectory ==> STR
            :
			RESULT = do_get_temp_dir.Trailing_slash .
			
        FUN do_get_temp_dir ==> STR, NATIVE : @
        "	RESULT = Path.GetTempPath();" .
        
        CONST INT {special_folders} :
              APPLICATION_DATA = 26
              COMMON_APP|LICATION_DATA = 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, 
            IF Sp IS CONST, THEN Sp IN {special_folders} :
	        RESULT = do_get_special_dir(Sp).Trailing_slash .
	        
        FUN do_get_special_dir(INT Sp|ecial_folder_index) ==> STR, NATIVE :
           @"    RESULT = Environment.GetFolderPath("
            "             (System.Environment.SpecialFolder)X_Sp);" .
            
        */
        public static string Start_dir()
        {
            string Result = Path.GetDirectoryName(Environment.GetCommandLineArgs()[0]);
            if (Result != "" && Result[Result.Length - 1] != '\\')
                Result += "\\";
            return Result;
        }

        
        /*
        
--------------- 'execute external program'

CONST INT : 
	EXECUTE_REDIRECT_OUT || REDIR_OUT = 1
	EXECUTE_REDIRECT_ERR || REDIR_ERR = 2
	EXECUTE_REDIRECT_IN  || REDIR_IN = 4
	EXECUTE_HIDDEN|_AS_POSSIBLE || EXEC_HIDDEN = 8
	EXECUTE_RUNAS || EXECUTE_RUN_AS = 16
	.
        
        FUN Execute_program|_and_wait_until_ended_redirect_output(
                STR App|lication_path,
                STR Param|eters,
                INT Redirect|_output_error) ==> STR
                :
            ----------------------- 'prevent executing any program in TEST mode'
            CASE TEST ?
                 STOP "In TEST mode, can not execute external programs" ;
            --------------- 'fix passing symbol ^ in parameters'
            STR p|aram_fixed = Param
            FOR i IN [p.Len-1 DOWNTO 0] :
                CASE p.[i] == "^" ? p = p.[0 TO i] p.[i TO *] ;
            ;
            ---------------
            RESULT = execute_program(App, p, Redirect) .

        FUN execute_program|_LOCAL(
        	STR App|lication_path, STR Param|eters,
        	INT Redirect|_output_error) ==> STR, NATIVE :
           @"    string outp = " #QU #QU ";"
            "    try {"
            //"AL4_system.WriteLine("#QU"1\n"#QU");"
            "        Process p = new Process {"
            "            StartInfo = new ProcessStartInfo {"
            "                FileName = X_App,"
            "                Arguments = X_Param,"
            "				 WindowStyle = (X_Redirect & 8) != 0 ? "
            "					ProcessWindowStyle.Hidden : "
            "					ProcessWindowStyle.Normal,"
            "				 UseShellExecute = (X_Redirect & 8) != 0"     
            "            }"
            "        };"
            "		 if ((X_Redirect & 16) != 0) {"
            "			 p.StartInfo.Verb = "#QU"runas"#QU";"     
            "		 }"     
            //"AL4_system.WriteLine("#QU"2\n"#QU");"
            "        if ((X_Redirect & 7) != 0) {"
            "           p.StartInfo.UseShellExecute = false;"
            "           p.StartInfo.RedirectStandardOutput ="
            "               (X_Redirect & 1) != 0;"
            "           p.StartInfo.RedirectStandardError ="
            "               (X_Redirect & 2) != 0;"
            "		    "     
            //"AL4_system.WriteLine("
            //#QU"3 FileName=["#QU" + X_App + "
            //#QU"] Arguments=["#QU" + X_Param + "#QU"]\n"#QU");"
            "           p.Start();"
            //"AL4_system.WriteLine("#QU"4\n"#QU");"
            "           if ((X_Redirect & 2) != 0 &&"
            "              !p.StandardError.EndOfStream)"
            "              outp += p.StandardError.ReadToEnd();"
            //"AL4_system.WriteLine("#QU"5\n"#QU");"
            "           if ((X_Redirect & 1) != 0 &&"
            "              !p.StandardOutput.EndOfStream)"
            "              outp += p.StandardOutput.ReadToEnd();"
            //"AL4_system.WriteLine("#QU"6\n"#QU");"
            "           p.WaitForExit();"
            "           return outp;"
            "        }"
            "        p.StartInfo = new ProcessStartInfo("#QU"cmd.exe"#QU", "
            "           "#QU"/c "#QU" + (X_App + "#QU" "#QU" + X_Param)"
            "           .TrimEnd());"
            "		 p.StartInfo.UseShellExecute = true;"
            "		 if ((X_Redirect & 8) != 0) {"
            "			p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;"     
            "		 }"     
            "		 if ((X_Redirect & 16) != 0) {"
            "			 p.StartInfo.Verb = "#QU"runas"#QU";"     
            "		 }"     
            "        p.Start();"
            "        p.WaitForExit();"
            "        RESULT = " #QU #QU ";"
            "    }"
            "    catch (Exception E) {"
            "          outp += " #QU "\r\n" #QU " + E.Message;"
            "          return outp;"
            "    }" .
        */
        public static string Execute_program(string App, string Param, bool Redirect)
        {
            Process p = new Process
            {
                StartInfo = new ProcessStartInfo
                {
                    FileName = App,
                    Arguments = Param
                }
            };
            if (Redirect)
            {
                p.StartInfo.RedirectStandardOutput = true;
                p.StartInfo.UseShellExecute = false;
                p.Start();
                string outp = p.StandardOutput.ReadToEnd();
                p.WaitForExit();
                return outp;
            }
            p.Start();
            p.WaitForExit();
            return "";
        }

        /*
        
        FUN Start_program_ok(
        	STR App|lication_path, STR Param|eters) ==> BOOL
        	:
            CASE TEST ? STOP "In TEST mode, can not start external programs" ;
                 
            RESULT = start_process(App, Param, TRUE, Set_env= TRUE,
            		 Run_as= FALSE) != 0 .

        FUN Start_program|_and_forget(
                STR App|lication_path, STR Param|eters)
                :
            ----------------------------------------------------- ''
            CASE TEST ? STOP "In TEST mode, can not start external programs" ; 
            NONE = start_process(App, Param, TRUE, Set_env= TRUE, Run_as= FALSE)
            .

        FUN Program_run_as|_and_forget(
                STR App|lication_path, STR Param|eters)
                :
            ----------------------------------------------------- ''
            CASE TEST ? STOP "In TEST mode, can not start external programs" ;
            NONE = start_process(App, Param, TRUE, Set_env= TRUE, Run_as= TRUE).

        FUN Start_file|_not_necessary_executable(
                STR Dok|ument_path)
                :
            ----------------------------------------------------- ''
            CASE TEST ? STOP "In TEST mode, can not start external programs" ;
            NONE = start_process(Dok, "", TRUE, Set_env= FALSE, Run_as= FALSE) .

        FUN Start_program_hidden(
                STR App|lication_path, STR Param|eters)
                :
            ----------------------------------------------------- ''
            CASE TEST ? STOP "In TEST mode, can not start external programs" ;
            //BOOL ok|_started = 
            NONE= start_process(App, Param, FALSE, Set_env= TRUE, Run_as= FALSE)
            //DEBUG:
            //      CASE !ok ?
            //           << "could not run: " App " Param" #NL ;
            //;
            .
            
            
        FUN start_process(
        STR App|lication_path, STR Param|eters, BOOL Visible|_window,
        	BOOL Set_env|ironment_variables,
        	BOOL Run_as|_another_user_if_true_env_ignored) ==> INT,
        NATIVE :
			"	 try {"
            "    RESULT = System_functions.Start_program("
            "		X_App, X_Param, X_Visible, X_Set_env, X_Run_as);"
			"	 } catch { RESULT = 0; }" .
        */
        [DllImport("shell32.dll")]
		public static extern IntPtr ShellExecuteW(
		    IntPtr hwnd,          
		    		[MarshalAs(UnmanagedType.LPTStr)]
		    String lpOperation,   
		    		[MarshalAs(UnmanagedType.LPTStr)]
		    String lpFile,        
		    		[MarshalAs(UnmanagedType.LPTStr)]
		    String lpParameters,  
		    		[MarshalAs(UnmanagedType.LPTStr)]
		    String lpDirectory,   
		    Int32 nShowCmd);      
		    
		[DllImport("kernel32.dll")]
		public static extern int GetLastError();

        public static int Start_program(string App, string Param, bool Visible,
        	bool Set_env, bool Run_as)
        {
        
        	if (Run_as)
        	{
        		int SW_SHOW = 5;
        		int SW_HIDE = 0;
        	
        		int r = (int)ShellExecuteW((IntPtr)0, "runas", "cmd.exe",
        		"/c \"" + App + "\" " + Param, "", 
        		Visible ? SW_SHOW : SW_HIDE);
        		
                //Console.WriteLine("r=" + r.ToString());
                //if (r <= 32) {
                //    Console.WriteLine("LastError:" + GetLastError().ToString());
                //    Console.WriteLine(App);
                //}
        			
        		return r > 32 ? r : 0;
        	}
			ProcessStartInfo psi = new ProcessStartInfo(App, Param);
        	if (!Visible) psi.WindowStyle = ProcessWindowStyle.Hidden;
        	//else psi.WindowStyle = ProcessWindowStyle.Normal;
        	if (Set_env) {
        		psi.EnvironmentVariables.Clear();
        		var s_env = Environment.GetEnvironmentVariables(
        			EnvironmentVariableTarget.Process);
				foreach (System.Collections.DictionaryEntry ent in s_env)
				{
                    psi.EnvironmentVariables.Add(
                    	(string)ent.Key, (string)ent.Value);
				}
				s_env = Environment.GetEnvironmentVariables(
        			EnvironmentVariableTarget.Machine);
				foreach (System.Collections.DictionaryEntry ent in s_env)
				{
					if (psi.EnvironmentVariables[(string)ent.Key] != null)
                    	psi.EnvironmentVariables[(string)ent.Key] 
                    		= (string)ent.Value;
	                else
	                    psi.EnvironmentVariables.Add(
	                    	(string)ent.Key, (string)ent.Value);
				}
				psi.UseShellExecute = false;
				psi.CreateNoWindow = !Visible;	
        	}
            Process p = Process.Start(psi);
            if (p != null) return p.Id;
            return 0;
        }

        /*
        
------- 'mode test'        
            
        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".
                 
------------------ 'profiler helper'

		FUN Profiler_save_reset|_counters, NATIVE : @
		"	AL4_system.Profiler_save_reset();" .
                 
--------------- 'sound'

		FUN System_beep, NATIVE : @
		"Console.Beep();" .
		
		FUN System_tone_beep(
		INT Freq|uency, INT Dur|ation), NATIVE : @
		"Console.Beep((int)X_Freq, (int)X_Dur);" .
                 
------------------------ 'sleep'
                 
        FUNCTION Sleep|_milliseconds(
        INT M|illiseconds), NATIVE :
            "    System.Threading.Thread.Sleep((int)X_M);" .
            
-------------------------------- 'internal object info'
        
        FUN Ref_count({Object} Obj|ect_inspecting) ==> INT, NATIVE :
            "RESULT = X_Obj.Count;" .

        FUN Total_obj_index(
        	{Object} Obj|ect_of_any_class ) ==> INT,
            NATIVE :
           @"    RESULT = X_Obj.Obj.TOTAL_obj_index;" .

        ---------------------------------------- 'miscellaneous'
        
        FUNCTION Allocate_bytes(
        	BYTE Buffer|_bytes[], INT Size|_allocate ), 
            NATIVE :
            "    ((DynAByte) X_Buffer).Reallocate((int)X_Size);" .
            
        FUN Save_strings_to_file(
        	STR S|trings_to_save[], STR Path|_file ),
            NATIVE :
           @"    StreamWriter sw = new StreamWriter(X_Path);"
            "    for (int i = 0; i < X_S.Count; i++)"
            "         sw.WriteLine(X_S[i]);"
            "    sw.Close();" .
            
		FUN Absolute_path|_from_relative_path(
			STR Path|_relative ) ==> STR, 
            NATIVE :
		CASE Path == "" ? RESULT = Get_working_dir ==> ;
			NATIVE @
		"	try {"
		"   RESULT = Path.GetFullPath(X_Path);"
		"	} catch (Exception) { RESULT = X_Path; }" .
		
		FUN Short_path(STR Path|_to_short) ==> STR, NATIVE : @
		    "RESULT = System_functions.Short_path(X_Path);" .
		    
		*/
		
		[DllImport("kernel32.dll", CharSet = CharSet.Unicode, 
			BestFitMapping = false)]
	    public static extern int GetShortPathName(
	    	string path, StringBuilder fullpath, 
	    	[In] int length);
		public static string Short_path(string X_Path) {
			StringBuilder o_path = new StringBuilder(X_Path.Length);
			GetShortPathName(X_Path, o_path, X_Path.Length);
			return o_path.ToString();
		}
		
		/*
           
		FUN Trace_levels ==> INT, NATIVE :
		@	"	RESULT = AL4_system.Get_trace_levels();" .
		
		FUN Trace_fun|ction(
			INT Level|_of_stack_0_is_current_function_Trace_fun) ==> STR,
			NATIVE :
		@	"	RESULT = AL4_system.Get_trace_fun((int)X_Level);" .
		
		FUN Trace_counter(
			INT Level|_of_stack_0_is_current_function_Trace_fun) ==> INT,
			NATIVE :
		@	"	RESULT = (int)AL4_system.Get_trace_counter((int)X_Level);" .
		
		FUN Trace_stack(INT Max_levels) ==> STR :
			FOR i IN [0 TO Trace_levels-1] :
			    CASE i >= Max_levels ? ==> ;
			    CASE RESULT != "" ? RESULT = RESULT #NL ;
			    RESULT = RESULT Trace_fun(i) #TAB Trace_counter(i).Str ; .
        	
        FUN Byte|_from_integer(
        INT Value|_to_get_lower_byte_only) ==> BYTE,
            NATIVE : @ "    RESULT = (byte)X_Value;" .
        
        FUN Copy_bytes|_from_one_array_to_another(
        BYTE Dest|ination_array[*],
        BYTE Source|_array[*])
        	:
        	INT count|_to_copy = Source[].Count
        	CASE count > Dest[].Count ?
        	     count = Dest[].Count ;
        	CASE count <= 0 ? ==> ;
        	do_Copy_bytes(Dest[0 TO *], Source[0 TO *]) .
        FUN do_Copy_bytes(BYTE Dest|ination_array[*], BYTE Source|_array[*]),
        NATIVE: @
        	"	for (int i = 0; i < X_Source.Count; i++)"
        	"		X_Dest.set_byte(i, X_Source.get_byte(i));" .
        	
        FUN Copy_int|egers_from_one_array_to_another(
        INT Dest|ination_array[*],
        INT Source|_array[*])
        	:
        	INT count|_to_copy = Source[].Count
        	CASE count > Dest[].Count ?
        	     count = Dest[].Count ;
        	CASE count <= 0 ? ==> ;
        	do_Copy_int(Dest[0 TO *], Source[0 TO *]) .
        FUN do_Copy_int(INT Dest|ination_array[*], INT Source|_array[*]),
        NATIVE: @
        	"	for (int i = 0; i < X_Source.Count; i++)"
        	"		X_Dest.set_int(i, X_Source.get_int(i));" .
        	
		FUN Fill_bytes(
		BYTE Dest|ination_array[*],
		BYTE Value|_to_fill_with), NATIVE :
			"	for (int i = 0; i < X_Dest.Count; i++)"
			"		X_Dest.set_byte(i, X_Value);" .
        
		FUN Fill_int|egers(
		INT Dest|ination_array[*],
		INT Value|_to_fill_with), NATIVE :
			"	for (int i = 0; i < X_Dest.Count; i++)"
			"		X_Dest.set_int(i, X_Value);" .
        
			
		FUN Real_as_array_of_bytes(
		REAL R|eal_value, BYTE A|rray_bytes|_preallocated_8_items[]), NATIVE : @
		"	byte[] arr = BitConverter.GetBytes(X_R);"
		"	for (int i = 0; i < X_A.Count; i++)"
		"	{"
		"		X_A.set_byte(i, arr[i]);"     
		"	}" .     
		
		//--------------------- 'debug control'
		
		FUN Trace_blocks(INT Deep|_function_level), NATIVE : @
		"AL4_system.Level_trace_blocks "
		"= X_Deep > 0 ? AL4_system.Stack_functions.Length + (int)X_Deep : 0;"
		.
		
		--------------------- 'play audio file'
		ENUM {play_command} :
	    'PLAY|_FROM_POSITION_PERCENTS',
	    'PAUSE|_PLAYING_AND_CHANGE_POSITION',
	    'RESUME|_PLAYING',
	    'STOP|_PLAYING',
	    'POSITION_INFO|RMATION_GET_POSITION_PLAYING',
	    'LENGTH_INFO|RMATION_MILLISECONDS',
	    'VOLUME|_GET_SET_PERCENTS' .
		

		FUN Play_audiofile(
		STR Full_path|_file,
		{play_command} Command|_to_execute,
		REAL Pos|ition_to_set
		) ==> REAL
			:
			STR path|_to_work = Full_path
			CASE ?
			["!" IN path] ?     
			     path = Short_path(path) 
			[" " IN path] ?
			     path = Short_path(path) 
			;
		    CASE "!" IN path ?
		         path = "mpegvideo!" path 
		         //path = "waveaudio!" path 
		         //!Due to a bug in mciSendString (mpegvideo! prefix is not 
		         //!working for commands pause, stop, etc. except play) we
		    	 //!just do not handle files containing '!' in its full path.
		    	 //!Copy it to temp directory renaming it to avoid using '!'
		    	 //!in their names and play it from there -- for workaround.
		    	 //!(VK, 2024-Mar-18)
		    	 ==>
		    ;
			CASE Command ? 
			['PLAY']:
                //DEBUG: "play audio file: " path #NL ;
			    NONE = do_play_audiofile(path, 'PLAY', -1)
			    STR info|_pos_len
			    STR spl|itted[]
			    INT len|gth_file
			    BOOL then_resume = TRUE
				------------------------ 'reposition', REUSED
				CASE Pos >= 0 ?
				     info = do_play_audiofile(path, 'POSITION_INFO', -1)
				     spl[].Split(info, ";")
				     len = spl[1].Int
				     CASE len > 0 ?
                          //DEBUG: ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>"
                          //       "set position to " (Pos / 100.0 * len).Int.S
                          //       #NL ;
				          //NONE = do_play_audiofile(Path, 'PAUSE', 0)
				          CASE then_resume ? 
					           NONE = do_play_audiofile(
					              path, 'RESUME', (Pos / 100.0 * len).Int)
				          ;
				     ;
				;
				-------------------------
			['PAUSE']:
                //DEBUG: "pause audio file: " path #NL ;
                //CASE "!" IN path ?
                //     NONE = do_play_audiofile(path, 'STOP', -1)
                //ELSE
			    NONE = do_play_audiofile(path, 'PAUSE', -1) //;
			    then_resume = FALSE
			    LIKE .................... 'reposition'
			['RESUME']:
                //DEBUG: "resume audio file: " path #NL ;
				CASE Pos > 0 ? 
				     then_resume = TRUE
				     LIKE .................... 'reposition'
				ELSE NONE = do_play_audiofile(path, 'RESUME', -1) ;
			['STOP']:
                //DEBUG: "stop audio file: " path #NL ;
			    NONE = do_play_audiofile(path, 'STOP', -1)
			['POSITION_INFO']:
			    info = do_play_audiofile(path, 'POSITION_INFO', -1)
			    spl[].Split(info, ";")
			    len = spl[1].Int
			    RESULT = spl[0].Int * 100.0 / len
                //DEBUG: "position_info (" path "): " info " ==> " RESULT #NL ;
			['LENGTH_INFO']:
			    info = do_play_audiofile(path, 'LENGTH_INFO', -1)
			    spl[].Split(info, ";")
			    len = spl[1].Int
			    RESULT = len
			['VOLUME']:
				INT volume|_to_set = (Pos * 10).Int
				CASE volume > 1000 ? volume = 1000 ;
			    info = do_play_audiofile(path, 'VOLUME', volume)
			    RESULT = info.Int
			; .

		FUN do_play_audiofile(
		STR Path|_file,
		{play_command} Command|_to_execute,
		INT Pos|ition_to_set
		) ==> STR, NATIVE : 
		NATIVE @
		"RESULT = System_functions.Play_audiofile(X_Path, X_Command, X_Pos);".
		    
		*/
		
		[DllImport("winmm.dll")]
		public static extern uint mciSendString( 
		    string lpstrCommand,
		    StringBuilder lpstrReturnString,
		    int uReturnLength,
		    IntPtr hWndCallback
		);
		
		public static string Play_audiofile(
			string file, int command, long position) 
		{
			StringBuilder s = new StringBuilder();
			uint r;
			if (file == "") return "";
			switch(command) {
			case 0:
				string cmd = "play " + file;
				if (position >= 0) cmd = cmd + " from " + position.ToString();
				r = mciSendString(cmd, s, 0, IntPtr.Zero);
				break;
			case 1:
				r = mciSendString(
					"pause " + file + " wait", s, 0, IntPtr.Zero);
				break;
			case 2:
				if (position >= 0) {
					cmd = "set " + file + " time format milliseconds"; 
					r = mciSendString(cmd, s, 0, IntPtr.Zero);
					cmd = "set " + file + " seek exactly on";
					r = mciSendString(cmd, s, 0, IntPtr.Zero);
					cmd = "seek " + file + " to " 
							+ position.ToString() + " wait";  
					r = mciSendString(cmd, s, 0, IntPtr.Zero);
					r = mciSendString(
						"play " + file + " from " + position.ToString(), 
						s, 0, IntPtr.Zero);
				}
				else {
					r = mciSendString(
						"resume " + file, s, 0, IntPtr.Zero);
				}
				break;
			case 3:
				r = mciSendString("stop " + file, s, 0, IntPtr.Zero);
				r = mciSendString("close " + file, s, 0, IntPtr.Zero);
				break;
			case 4:
		    case 5:
				cmd = "set " + file + " time format milliseconds"; 
				r = mciSendString(cmd, s, 0, IntPtr.Zero);
		    	r = mciSendString(
					"status " + file + " position", s, 100, IntPtr.Zero);
		    	string ret = s.ToString();
		    	r = mciSendString(
					"status " + file + " length", s, 100, IntPtr.Zero);
		    	ret = ret + ";" + s.ToString();
		    	return ret;
		    case 6:
		    	r = mciSendString(
					"status " + file + " volume", s, 100, IntPtr.Zero);
		    	ret = s.ToString();
		    	if (position >= 0) {
					cmd = "setaudio " + file + " volume to " + position.ToString(); 
					r = mciSendString(cmd, s, 0, IntPtr.Zero);
		    	}
		    	return ret;
			} 
			return "";
		}
		
		
		/*
			

        */
        

    }
}
/*
END
*/

