//-------------------------------------- AL4_system.java (created 2016-12-01)
import java.util.ArrayList;
import java.io.File;
import java.io.Console;
import java.io.PrintWriter;

public class AL4_system {

    public static int Recursion_counter = 0;
    public static final int Max_recursion_deep = 128;
    public static long Test_caller;
                       // lower 6 decimal digits = #of source code line in test function
                       //    calling ordinary code and test functions of level 2
                       // next 6 decimal digits = #of source code line in secondary test
                       //    functions calling ordinary functions.
    
    //-------------------------------------
    // in Java, byte is signed, in range [-128 TO 127]. 
    // So, we convert it to a range [0 TO 255] representing it as INT.
    public static int Byte_as_unsigned(byte b) {
        int Result = b;
        return Result & 0xFF;
    }

    //-------------------------------------
    // Safe operation S[i] (String S, int i)
    public static String Single_symbol(String S, int Pos) {
        if (S != null && Pos >= 0 && S.length() > Pos)
            return S.substring(Pos, Pos+1) + "";
        return (char)0 + "";
    }

    public static String Change_single_symbol(String S, int Pos, 
           String New_char) {
        return Substring(S, 0, Pos-1) + New_char + 
            Substring(S, Pos+1, S.length()-1);
    }

    //---------------------------
    // Safe Substring() of String
    public static String Substring(String S, int from, int to) {
        if (to < from || S.length() <= from) return "";
        if (from < 0) from = 0;
        if (S.length() <= to) to = S.length() - 1;
        return S.substring(from, to + 1);
    }
    
    //---------------------------
    // Optimized comparison of two sub-strings
    public static int CompareSubstrings(String S1, String S2, 
        int from1, int to1, int from2, int to2)
    {
        if (from1 < 0) from1 = 0;
        if (from2 < 0) from2 = 0;
        int last1 = to1;
        if (last1 >= S1.length()) last1 = S1.length() - 1;
        int last2 = to2;
        if (last2 >= S2.length()) last2 = S2.length() - 1;
        int j = from2;
        for (int i = from1; i <= last1; i++, j++)
        {
            if (j > last2) 
            {
                if (i <= last1) return 1;
                break;
            }
            int Result = (int)S1.charAt(i) - (int)S2.charAt(j);
            if (Result != 0) return Result;
        }
        if (j <= last2) return -1;
        return 0;
    }

    //---------------------------
    // Optimized comparison of a sub-string with a string
    public static int CompareSubstrWithStr(String S1,  
        int from1, int to1, String S2)
    {
        int last1 = to1;
        if (from1 < 0) from1 = 0;
        if (last1 >= S1.length()) last1 = S1.length() - 1;
        int j = 0;
        for (int i = from1; i <= last1; i++, j++)
        {
            if (j >= S2.length()) 
            {
                if (i <= last1) return 1;
                break;
            }
            int Result = (int)S1.charAt(i) - (int)S2.charAt(j);
            if (Result != 0) return Result;
        }
        if (j < S2.length()) return -1;
        return 0;
    }

    //--------------------------------------------
    // Optimized x IN A[]
    public static boolean ArrayContainsInt(int[] A, int X) {
        for (int i = 0; i < A.length; i++) 
            if (A[i] == X) return true;
        return false;
    }

    public static boolean ArrayContainsByte(byte[] A, byte X) {
        for (int i = 0; i < A.length; i++) 
            if (A[i] == X) return true;
        return false;
    }

    public static boolean ArrayContainsStr(String[] A, String X) {
        for (int i = 0; i < A.length; i++) 
            if (A[i].compareTo(X) == 0) return true;
        return false;
    }

    

    //-----------------------------------------
    // Operation S[i] = c (not available in C#)
    public static String Replace_symbol(String S, int i, String r) {
        if (i < 0 || i >= S.length()) return S;
        return S.substring(0, i) + r + S.substring(i + 1);
    }

    //---------------------------------------------------------
    // Assign value to short string (all chars after MaxLen
    // are ignored)
    public static String Assign_short_str(String Source, int MaxLen)
    {
        if (Source.length() > MaxLen)
            return Source.substring(0, MaxLen);
        return Source;
    }

    //----------------------
    // Convert int to String
    public static String Str(int x) {
        return Integer.toString(x);
    }

    //-------------------------
    // Convert double to String
    public static String Str(double x) {
        return Double.toString(x).replace(',', '.');
    }

    //-------------------------
    // Convert boolean to String
    public static String Str(boolean x) {
        if (x) return "TRUE";
        return "FALSE";
    }

    public static int Int(String s) {
        int Result = 0;
        boolean Neg = false;
        if (s.equals("")) return Result;
        int i = 0;
        if (s.charAt(i) == '-') {
            i++;
            Neg = true;
        }
        else if (s.charAt(i) == '+') i++;
        for (int j = i; j < s.length(); j++) {
            if (s.charAt(j) == '_') continue;
            if (s.charAt(j) >= '0' && s.charAt(j) <= '9')
                Result = Result * 10 + (int)s.charAt(j) - (int)'0';
            else break;
        }
        if (Neg) return -Result;
        return Result;
    }

    public static int RealAsInt(double X) {
        return (int)X;
    }

    //------------- 'string as a sequence of hexadecimal, 4 digits per char'
    private static String hex16(int value16)
    {
        String Result = "";
        for (int i = 0; i < 4; i++)
        {
            int digit = (value16 >> 12) & 0xf;
            value16 = value16 << 4;
            if (digit < 10) Result += "" + (char)((int)'0' + digit);
            else Result += "" + (char)((int)'A' + digit - 10);
        }
        return Result;
    }

    public static String StrHex16(String s)
    {
        String Result = "";
        for (int i = 0; i < s.length(); i++)
            Result += hex16((int)s.charAt(i));
        return Result;
    }

    public static int ShiftLeft(int i, int n) {
        return i << n;
    }

    public static int ShiftRight(int i, int n) {
        return i >> n;
    }

    public static int RotateLeft(int i, int n) {
        return (i << n) | (i >> (32 - n));
    }

    public static int RotateRight(int i, int n) {
        return (i >> n) | (i << (32 - n));
    }

    public static String Restrict_length(String src, int len) {
        String Result = src;
        if (Result.length() > len)
           Result = Result.substring(0, len);
        return Result;
    }

    //------------------------------------------
    // Find substring first position in a String
    public static int Find_str(String S, String Pattern) {
        if (S == null) return -1;
        int r = S.indexOf(Pattern);
        return r;
    }

    //--------------------------------------
    // Is object variable referencing NONE ?
    public static boolean Is_none(Ref_obj obj) {
        return obj.Obj.Is_NONE_object;
    }

    //---------------------------------------------------------------
    // Safe division of real numbers. Returns INF or NaN in bad cases
    public static double Safe_div(double X, double Y) {
        if (Y == 0) return X > 0 ?
            Double.POSITIVE_INFINITY : Double.NEGATIVE_INFINITY;
        try {
            return X / Y;
        }
        catch(ArithmeticException E_AE) {
            AL4_system.Any_exception(E_AE);
            return Double.NaN;
        }
    }

    //-------------------------------------------------
    // Safe division of int numbers. Returns very large
    // or negative large integer number in bad cases.
    public static int Safe_div_int(int X, int Y) {
        if (Y == 0) return X > 0 ?
            Integer.MAX_VALUE : Integer.MIN_VALUE;
        return X / Y;
    }

    //---------------------------------
    // Safe rest of division operation.
    public static int Safe_rest(int X, int Y) {
        if (Y == 0) return X;
        return X % Y;
    }

    //-----------------------
    // STOP operation
    public static void STOP(String message) throws Exception {
        throw new AL4StopException(message);
    }
    
    public static class Strings {
        public ArrayList<String> list = new ArrayList<String>();
    }

    //public static ThreadLocal<Strings> Errors = new ThreadLocal<Strings>() {
    //    @OVerride protected Strings 
    //    @Override public Strings get  
    //}
    
    public static Strings Errors = null;
    
    public static void Any_exception(Throwable e) {
        if (Errors == null)
            Errors = new Strings();
        if (Errors.list.size() >= 10) {
            Errors.list.remove(5);
            Errors.list.set(5, "");
        }
        Errors.list.add(e.getClass().getName() + ": " + e.getMessage());
    }
    
    public static void Reset_errors() {
        Errors = null;
    }
    
    public static int Errors_count() {
        if (Errors == null) return 0;
        return Errors.list.size(); 
    }
    
    public static String Errors_info(int index) {
        if (Errors == null || index < 0 || index >= Errors.list.size())
            return "";
        return Errors.list.get(index);
    }


    //------------------------------------------------------------
    // Operation X.{Type}. Returns NONE is X is not of type {Type}.
    public static Ref_obj As_obj(Ref_obj X, Ref_obj None_y) {
        AL4_object probe = X.Obj;
        try {
        while (probe != null) {
            if (probe.NONE_obj_instance().Obj == None_y.Obj)
                return X;
            probe = probe.NONE_base_obj();
        }
        }
        catch (Exception ex) {
            //if (None_y == null)
            //    AL4_system.WriteLine("!!! None_y == null");
            //ex.printStackTrace();
        }
        return None_y;
    }

    //----------------------------------------------------------
    // Application startup directory
    public static String Start_dir()
    {
        return Startup_path();
    }

    //----------------------------------------------------------
    // Application startup directory
    public static String Startup_path()
    {
        try {
            File f = new File(".");
            String s = f.getCanonicalPath();
            return s;
        }
        catch(Exception E) {
            AL4_system.Any_exception(E);
            return "";
        }
    }

    //---------------------------------------------------------
    // Clear test data
    public static void Clear_test_data(long[] test_lines)
    {
        for (int i = 0; i < test_lines.length; i++)
        {
            if (test_lines[i] == -1) continue;
            test_lines[i] = 0;
        }
    }

    //---------------------------------------------------------
    // Remove a range from test data
    public static void Remove_test_data(long[] test_lines, int from, int to)
    {
        for (int i = from; i <= to; i++)
        {
            if (i >= test_lines.length) return;
            if (test_lines[i] < 0) continue;
            if (test_lines[i] == 0) test_lines[i] = -9;
        }
    }


    //---------------------------------------------------------
    // Output test data of class
    public static void Output_test_data(
    	long[] test_lines,
        ArrayList<Integer> test_assert, 
        ArrayList<String> test_assert_failed_text,
        String Class_test_file,
        String Class_path_source) {

        String path = Class_test_file;
        String s = "";
        try {
            PrintWriter sw = new PrintWriter(path);
            try
            {
            	sw.println("source: " + Class_path_source);
                for (int i = 0; i < test_lines.length; i++)
                {
                    s = s + Long.toString(test_lines[i]) + " ";
                    if (s.length() >= 75)
                    {
                        sw.println(s);
                        s = "";
                    }
                }
                if (s != "") sw.println(s);
                if (test_assert != null)
                {
                    sw.println("* assertions failed in lines:");
                    for (int i = 0; i < test_assert.size(); i++)
                    {
                        sw.print(Integer.toString(test_assert.get(i)));
                        if (!test_assert_failed_text.get(i).equals(""))
                            sw.print(" " + test_assert_failed_text.get(i));
                        sw.println("");
                    }
                }
            }
            finally {
                sw.close();
            }
        }
        catch (java.io.FileNotFoundException E_fil) {
            AL4_system.Any_exception(E_fil);
            Write("Could not write test data");
        }
    }

    // adding an assertion failed to list of failed assertions:
    public static ArrayList<Integer> Assertion_failed(int line,
           ArrayList<Integer> test_assert)
    {
        if (test_assert == null) test_assert = new ArrayList<Integer>();
        test_assert.add(line);
        return test_assert;
    }

    public static ArrayList<String> Assertion_failed2(String assert_failed_text,
           ArrayList<String> test_assert_failed_text)
    {
        if (test_assert_failed_text == null)
            test_assert_failed_text = new ArrayList<String>();
        test_assert_failed_text.add(assert_failed_text);
        return test_assert_failed_text;
    }
    
    public interface Sim_Write {
    	public void do_write(String S);
    }
    
    public static boolean dup_Write;
    public static Sim_Write sim_Write;  

    public static void Write(String S) {
        try {
			if (null != sim_Write) {
				sim_Write.do_write(s);
				if (!dup_Write) return;
			}
            Console c = System.console();
            if  (c != null) {
                c.printf("%s", S);
            }
        }
        catch(Exception ex) {
            AL4_system.Any_exception(ex);
            ex.printStackTrace();
        }

    }

    public static void WriteLine(String S) {
        Write(S + "\r\n");
    }

    public static String ReadLine() {
        try {
            Console c = System.console();
            if  (c != null) {
                String s = c.readLine("");
                return s;
            }
        }
        catch(Exception ex) {
            AL4_system.Any_exception(ex);
            ex.printStackTrace();
        }
        return "";
    }

    public static void Save_strings(ArrayList<String> L, String Path) {
        try {
            String nl = System.getProperty("line.separator");
            java.io.BufferedWriter bw = null;
            bw = new java.io.BufferedWriter(
                new java.io.OutputStreamWriter(
                new java.io.FileOutputStream(Path, false)));
            for (int i = 0; i < L.size(); i++)
                 bw.append(L.get(i)).append(nl);
        }
        catch (Exception E_io) {}
    }
    
    public static boolean Is_TEST_stage()
    {
            return false;
    }
    
	//public static ThreadLocal<int> global_activity_counter_t
	//	= new ThreadLocal<int>() {
	//	@Override protected Int initialValue() {
	//		return 0;
	//	}
	//};
	public static int global_activity_counter = 65535;
	public static ArrayList<AL4_handler> long_operation_handlers 
		= new ArrayList<AL4_handler>();
	
	public static void check_long_operation() throws Exception
	{
		try {
		global_activity_counter = 65535;
		if (long_operation_handlers == null) return;
		//AL4_system.WriteLine("check long operation calls Handler");
		for (int i = long_operation_handlers.size()-1; i >= 0; i--)
			long_operation_handlers.get(i).Handler();
		} catch(Exception E) {
			E.printStackTrace();
		}
	}
	

      public static String[] Localize_name = new String[] {
          "{AL4_forms_Java_android}",
          "OK",
          "Yes",
          "No",
          "{Long_operation}",
          "PleaseWait",
          "ALongOp",
          "EventIs",
          "ItemIs",
          "Cancel",
          "click",
          "closing",
          "closed",
          "activated",
          "dialog_answer",
          "value_change",
          "selection_change",
          "resize",
          "moved",
          "shown",
          "mouse_move",
          "mouse_down",
          "mouse_up",
          "mouse_double_click",
          "mouse_wheel",
          "key_down",
          "key_up",
          "key_press",
          "timer_tick",
          "animation_control",
          "menu_item",
          "keyboard_focus",
          "before_paint",
          "paint",
          "after_paint",
          "scrolled",
          "command",
          "Pause",
          "Continue",
          "ClickOK" };
      public static String[] Localize_str = new String[] {
          "",
          "OK",
          "Yes",
          "No",
          "",
          "Please, wait...",
          "A long operation is started...",
          "event is",
          "item is",
          "Cancel",
          "click",
          "closing",
          "closed",
          "activated",
          "dialog answer",
          "value change",
          "selection change",
          "resize",
          "moved",
          "shown",
          "mouse move",
          "mouse down",
          "mouse up",
          "mouse double click",
          "mouse wheel",
          "key down",
          "key up",
          "key press",
          "timer tick",
          "animation control",
          "menu item",
          "keyboard focus",
          "before paint",
          "paint",
          "after paint",
          "scrolled",
          "command",
          "Pause",
          "Continue",
          "Click OK to continue" };

}

