//------------------------------- AL4.cs version 2018-11-20 (A)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

using System.Runtime.InteropServices;

namespace AL4
{
    public static class AL4_system
    {
    
    	public static string[] Native_functions_declarations = new string[0];
        public static int Recursion_counter = 0;
        public const int Max_recursion_deep = 128;

        public static void Any_exception(Exception E)
        {
        }

        public static void Any_exception()
        {
        }
        
        public static List<string> System_parameters = new List<string>();

        //-------------------------------------
        // Safe operation S[i] (string S, int i)
        public static string Single_symbol(string S, int Pos)
        {
            if (Pos >= 0 && S.Length > Pos) return S[Pos] + "";
            return (char)0 + "";
        }

        //---------------------------
        // 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 - from + 1);
        }

        //-----------------------------------------
        // 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);
        }

        //----------------------
        // Convert int to string
        public static string Str(int x)
        {
            return x.ToString();
        }

        //-------------------------
        // Convert double to string
        public static string Str(double x)
        {
            return x.ToString().Replace(',', '.');
        }

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

        public static int Int(string s)
        {
            int Result = 0;
            bool Neg = false;
            if (s == "") return Result;
            int i = 0;
            if (s[i] == '=')
            {
                i++;
                Neg = true;
            }
            else if (s[i] == '+') i++;
            for (int j = i; j < s.Length; j++)
            {
                if (s[j] == '_') continue;
                if (s[j] >= '0' && s[j] <= '9')
                    Result = Result * 10 + (int)s[j] - (int)'0';
                else break;
            }
            if (Neg) return -Result;
            return Result;
        }

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

        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));
        }

        //------------------------------------------
        // Find substring first position in a string
        public static int Find_str(string S, string Pattern)
        {
            int r = S.IndexOf(Pattern);
            return r;
        }

        //--------------------------------------
        // Is object variable referencing NONE ?
        public static bool Is_none(Ref_obj obj)
        {
            return obj == null || 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.PositiveInfinity : double.NegativeInfinity;
            try
            {
                return X / Y;
            }
            catch { 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 ?
                int.MaxValue : int.MinValue;
            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)
        {
            throw new Stop(message);
        }

        //------------------------------------------------------------
        // 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)
        {
            if (X != null)
            {
                AL4_object probe = X.Obj;
                while (probe != null)
                {
                    if (probe.NONE_obj_instance().Obj == None_y.Obj)
                        return X;
                    probe = probe.NONE_base_obj();
                }
            }
            return None_y;
        }

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

        //----------------------------------------------------------
        // Application startup directory
        public static string Startup_path()
        {
            string s = Environment.CommandLine;
            if (s[0] == '\"')
            {
                s = s.Substring(1);
                int i = s.IndexOf('\"');
                s = s.Substring(0, i);
            }
            else
            {
                int j = s.IndexOf(' ');
                if (j > 0) s = s.Substring(0, j);
            }
            s = Path.GetDirectoryName(s);
            if (s[s.Length - 1] != '\\') s += '\\';
            return s;
        }

        //---------------------------------------------------------
        // 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] < 0) continue;
                test_lines[i] = 0;
            }
        }

        //---------------------------------------------------------
        // Output test data of class
        public static void Output_test_data(long[] test_lines,
            List<int> test_assert, //List<string> test_assert_filed_text,
            string Class_test_file)
        {
            string path = Class_test_file;
            string s = "";
            StreamWriter sw = new StreamWriter(path);
            try
            {
                for (int i = 0; i < test_lines.Length; i++)
                {
                    s = s + test_lines[i].ToString() + " ";
                    if (s.Length >= 75)
                    {
                        sw.WriteLine(s);
                        s = "";
                    }
                }
                if (s != "") sw.WriteLine(s);
                if (test_assert != null)
                {
                    sw.WriteLine("* assertions failed in lines:");
                    for (int i = 0; i < test_assert.Count; i++)
                    {
                        sw.WriteLine(test_assert[i].ToString());
                    }
                }
            }
            finally { sw.Close(); }
        }

        // adding an assertion failed to list of failed assertions:
        public static void Assertion_failed(int line, ref List<int> test_assert)
        {
            if (test_assert == null) test_assert = new List<int>();
            test_assert.Add(line);
        }

        [DllImport("kernel32.dll")]
        private static extern bool AllocConsole();

        public static bool Need_console = false;
        
		public delegate void RedirectWrite(string S);
        public static RedirectWrite Redirect_write = Write_direct;

        public static void Write_direct(string S)
        {
            Console.Write(S);
        }

        public static void Write(string S)
        {
            if (Need_console)
            {
                Need_console = false;
                AllocConsole();
            }
            Redirect_write(S);
        }
                
        //public static void Write(string S)
        //{
        //    if (Need_console)
        //    {
        //        Need_console = false;
        //        AllocConsole();
        //    }
        //    Console.Write(S);
        //}

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

    }

    public class AL4_object
    {
        //just for debugging:
        private static int total_obj_counter;
        public int TOTAL_obj_index;
        public AL4_object()
        {
            TOTAL_obj_index = ++total_obj_counter;
            if (TOTAL_obj_index == 488)
            { }
        }

        // Fastest way to check if a variable is referencing the NONE
        public bool Is_NONE_object = false;

        // Back reference to a Ref_obj, containing a hard link onto the object
        private Ref_obj h_link;
        public Ref_obj hard_link
        {
            get { return h_link; }
            set
            {
                if (TOTAL_obj_index == 197)
                { }

                h_link = value;
            }
        }

        // Back reference to a Ref_obj, containing a weak link to the object
        public Ref_obj weak_link;

        // Additional link onto the object itself to make changes in fields.
        // For the NONE object, it is referencing the dummy_obj, so NONE is never
        // changed (though it is not necessary to check value for NONE).
        public AL4_object write;

        // Object NONE instance (single for a class)
        protected static Ref_obj None_obj;

        // Object Dummy instance (single for a class)
        protected static Ref_obj dummy_obj;

        //------------- for OWNED BY children:
        protected List<Ref_obj> children = null;

        public virtual Ref_obj NONE_obj_instance()
        {
            return None_obj;
        }

        public static Ref_obj Dummy_obj
        {
            get { return dummy_obj; }
        }

        public virtual AL4_object NONE_base_obj()
        {
            return null;
        }

        public virtual void Erase()
        {
            if (TOTAL_obj_index == 37)
            { }

            // 1st, destroy children if those are present:
            if (children != null)
            {
                foreach (Ref_obj c in children)
                    c.Obj.Erase();
                children.Clear();
            }
            children = null;

            // destroy the object removeing all three references onto it:
            Ref_obj none = NONE_obj_instance();

            weak_link.Obj = none.Obj;
            hard_link.Obj = none.Obj;
            write = none.Obj.write;

            hard_link = none;
            weak_link = none;
        }

        public virtual Ref_obj Assign_hard(Ref_obj Dest)
        {
        	if (Dest == null) return hard_link;
            Ref_obj was_Dest = Dest;
            hard_link.Count += 2;
            was_Dest.Ref_dec_hard();
            return hard_link;
        }

        public virtual Ref_obj Assign_weak(Ref_obj Dest)
        {
        	if (Dest == null) return hard_link;
            Ref_obj was_Dest = Dest;
            weak_link.Count += 2;
            was_Dest.Ref_dec_weak();
            return hard_link;
        }

        // Is calling from New_owned() when an object is created with its 
        // exclusive owner but without a counter in its hard_link.
        public void Owned_by(Ref_obj owner)
        {
            if (owner.Obj.Is_NONE_object) return;
            if (owner.Obj.children == null)
                owner.Obj.children = new List<Ref_obj>();
            owner.Obj.children.Add(this.hard_link);
            this.hard_link.Count |= 1;
        }

        public virtual int Int_value(string Name_value)
        {
            return 0;
        }

        public virtual string Str_value(string Name_value)
        {
            return "";
        }

        public virtual bool Bool_value(string Name_value)
        {
            return false;
        }

    }

    public class Ref_obj
    {
        public AL4_object Obj;
        public int Count;

        public Ref_obj(AL4_object obj, int count)
        {
            Obj = obj;
            Count = count;
        }

        public void Ref_dec_weak()
        {
            Obj.weak_link.Count -= 2;
            //--if (Count <= 1 && Obj.Is_NONE_object)
            {
                // in platforms without garbage collector,
                // place here code to destroy this Ref_obj
                // representing Weak reference to the object
                // which was already destroyed

                // in case of DotNet platform, it will be
                // destroyed automatically since there are
                // no more references to it.
            }
        }

        public void Ref_dec_hard()
        {
            Obj.hard_link.Count -= 2;
            if (Obj.hard_link.Count == 0)
            {
                // destroy was_Dest object
                // Since there are only two links onto the 
                // object (from its Hard_link and Weak_link
                // small linking objects), it is sufficiently
                // to replace those with other values to
                // destroy the object.
                Obj.Erase();
            }
        }

    }

    public static class Handle_list<T>
    {
        public static int Count(List<T> list)
        {
            return list.Count;
        }
    }

    public class DynAObj : List<Ref_obj>
    {
        public Ref_obj none_obj;
        public DynAObj(Ref_obj none)
        {
            none_obj = none;
        }

        public new Ref_obj this[int index]
        {
            get
            {
                if (index < 0 || index >= Count)
                    return none_obj;
                return base[index];
            }
            set
            {
                if (index < 0 || index >= Count)
                    return;
                base[index] = value;
            }
        }

        public void Add_obj_weak(Ref_obj X)
        {
            Add(X);
            X.Obj.weak_link.Count += 2;
        }

        public void Add_obj_hard(Ref_obj X)
        {
            Add(X);
            X.Obj.hard_link.Count += 2;
        }

        // Used only in case when new oject is added to an array
        // of hard links, to provide correct usage Count value
        public void Ref_dec_last()
        {
            if (Count == 0) return;
            this[Count - 1].Ref_dec_hard();
        }

        public void Release_objects_hard()
        {
            foreach (Ref_obj obj in this)
            {
                obj.Ref_dec_hard();
            }
            Clear();
        }
        
        public void Delete(int IndexAt)
        {
            RemoveAt(IndexAt);
        }
    }

    public class DynAInt : List<int>
    {
        public new int this[int index]
        {
            get
            {
                if (index < 0 || index >= Count)
                    return 0;
                return base[index];
            }
            set
            {
                if (index < 0 || index >= Count)
                    return;
                base[index] = value;
            }
        }

        public void Dispose()
        {
            Clear();
        }

        public void Delete(int IndexAt)
        {
            RemoveAt(IndexAt);
        }
    }

    public class DynAReal : List<double>
    {
        public new double this[int index]
        {
            get
            {
                if (index < 0 || index >= Count)
                    return 0;
                return base[index];
            }
            set
            {
                if (index < 0 || index >= Count)
                    return;
                base[index] = value;
            }
        }

        public void Dispose()
        {
            Clear();
        }

    }

    public class DynABool : List<bool>
    {
        public new bool this[int index]
        {
            get
            {
                if (index < 0 || index >= Count)
                    return false;
                return base[index];
            }
            set
            {
                if (index < 0 || index >= Count)
                    return;
                base[index] = value;
            }
        }

        public void Dispose()
        {
            Clear();
        }
    }

    public class DynAByte : List<byte>
    {
        //dummy:
        public List<byte> BaseList = new List<byte>();

        public new byte this[int index]
        {
            get
            {
                if (index < 0 || index >= Count)
                    return 0;
                return base[index];
            }
            set
            {
                if (index < 0 || index >= Count)
                    return;
                base[index] = value;
            }
        }

        public void Dispose()
        {
            Clear();
        }
    }

    public class ArrayByte : DynAByte
    {
    }


    public class DynAStr : List<string>
    {
        public new string this[int index]
        {
            get
            {
                if (index < 0 || index >= Count)
                    return "";
                return base[index];
            }
            set
            {
                if (index < 0 || index >= Count)
                    return;
                base[index] = value;
            }
        }

        public void Dispose()
        {
            Clear();
        }
        
        public void Delete(int IndexAt)
        {
            RemoveAt(IndexAt);
        }
    }

    public class DynARec<T> : List<T> where T : struct
    {
        protected T none_rec;

        public DynARec()
        {
            none_rec = new T();
        }

        public new T this[int index]
        {
            get
            {
                if (index < 0 || index >= Count)
                    return none_rec;
                return base[index];
            }
            set
            {
                if (index < 0 || index >= Count)
                    return;
                base[index] = value;
            }
        }

        public virtual void Dispose()
        {
            Clear();
        }
    }



    public class FixARec<T> where T : struct
    {
        protected DynARec<T> BaseList;
        public int Starting;
        public int Count_items;
        public bool Is_slice;
        protected T none_rec;

        public T this[int index]
        {
            get
            {
                if (index < Starting || index >= Starting + Count_items)
                    return none_rec;
                return BaseList[Starting + index];
            }
            set
            {
                if (index < Starting || index >= Starting + Count_items)
                    return;
                BaseList[Starting + index] = value;
            }
        }

        public FixARec(T[] A)
        {
            BaseList = new DynARec<T>();
            BaseList.Capacity = A.Length;
            foreach (T t in A)
                BaseList.Add(t);
            Count_items = BaseList.Count;
        }

        public FixARec(DynARec<T> base_list, int From_idx, int To_idx)
        {
            Is_slice = true;
            BaseList = base_list;
            Count_items = To_idx + 1 - From_idx;
            Starting = From_idx;
        }

        ~FixARec()
        {
            if (!Is_slice) BaseList.Clear();
        }

        public int Count { get { return Count_items; } }

        public void Dispose()
        {
            if (!Is_slice)
                BaseList.Dispose();
            Count_items = 0;
        }
    }

    public class FixAObj
    {
        protected DynAObj BaseList;
        public int Starting;
        public int Count_items;
        public bool Is_slice;

        public FixAObj(Ref_obj none)
        {
            BaseList = new DynAObj(none);
        }

        public FixAObj(DynAObj base_list, int From_idx, int To_idx)
        {
            Is_slice = true;
            BaseList = base_list;
            Starting = From_idx;
            Count_items = To_idx + 1 - From_idx;
        }

        ~FixAObj()
        {
            if (!Is_slice) BaseList.Clear();
            BaseList = null;
        }

        public Ref_obj none_obj
        {
            get { return BaseList.none_obj; }
        }

        public Ref_obj this[int index]
        {
            get
            {
                if (index < Starting || index >= Starting + Count_items)
                    return none_obj;
                return BaseList[index + Starting];
            }
            set
            {
                if (index < Starting || index >= Starting + Count_items)
                    return;
                BaseList[index + Starting] = value;
            }
        }

        public int Count { get { return Count_items; } }

        public int IndexOf(Ref_obj Obj)
        {
            for (int i = Starting; i < Starting + Count_items; i++)
                if (BaseList[i] == Obj) return i;
            return -1;
        }

        public bool Contains(Ref_obj Obj)
        { return IndexOf(Obj) >= 0; }
    }

    public class FixAInt
    {
        protected DynAInt BaseList;
        public int Starting;
        public int Count_items;
        public bool Is_slice;

        public int this[int index]
        {
            get
            {
                if (index < Starting || index >= Starting + Count_items)
                    return 0;
                return BaseList[Starting + index];
            }
            set
            {
                if (index < Starting || index >= Starting + Count_items)
                    return;
                BaseList[Starting + index] = value;
            }
        }

        public FixAInt(int N)
        {
            BaseList = new DynAInt();
            BaseList.Capacity = N;
            for (int i = 0; i < N; i++)
                BaseList.Add(0);
            Count_items = BaseList.Count;
        }

        public FixAInt(int[] A)
        {
            BaseList = new DynAInt();
            BaseList.Capacity = A.Length;
            foreach (int i in A)
                BaseList.Add(i);
            Count_items = BaseList.Count;
        }

        public FixAInt(DynAInt base_list, int From_idx, int To_idx)
        {
            Is_slice = true;
            BaseList = base_list;
            Count_items = To_idx + 1 - From_idx;
            Starting = From_idx;
        }

        ~FixAInt()
        {
            if (!Is_slice) BaseList.Clear();
        }

        public int Count { get { return Count_items; } }

        public int IndexOf(int Value)
        {
            for (int i = Starting; i < Starting + Count_items; i++)
                if (BaseList[i] == Value) return i;
            return -1;
        }

        public bool Contains(int Value)
        { return IndexOf(Value) >= 0; }

    }

    public class FixAReal
    {
        protected DynAReal BaseList;
        public int Starting;
        public int Count_items;
        public bool Is_slice;

        public double this[int index]
        {
            get
            {
                if (index < Starting || index >= Starting + Count_items)
                    return 0;
                return BaseList[Starting + index];
            }
            set
            {
                if (index < Starting || index >= Starting + Count_items)
                    return;
                BaseList[Starting + index] = value;
            }
        }

        public FixAReal(int N)
        {
            BaseList = new DynAReal();
            BaseList.Capacity = N;
            for (int i = 0; i < N; i++)
                BaseList.Add(0);
            Count_items = BaseList.Count;
        }

        public FixAReal(double[] A)
        {
            BaseList = new DynAReal();
            BaseList.Capacity = A.Length;
            foreach (int i in A)
                BaseList.Add(i);
            Count_items = BaseList.Count;
        }

        public FixAReal(DynAReal base_list, int From_idx, int To_idx)
        {
            Is_slice = true;
            BaseList = base_list;
            Count_items = To_idx + 1 - From_idx;
            Starting = From_idx;
        }

        ~FixAReal()
        {
            if (!Is_slice) BaseList.Clear();
        }

        public int Count { get { return Count_items; } }

        public int IndexOf(double Value)
        {
            for (int i = Starting; i < Starting + Count_items; i++)
                if (BaseList[i] == Value) return i;
            return -1;
        }

        public bool Contains(double Value)
        { return IndexOf(Value) >= 0; }

    }

    public class FixAByte
    {
        protected DynAByte BaseList;
        public int Starting;
        public int Count_items;
        public bool Is_slice;

        public byte this[int index]
        {
            get
            {
                if (index < Starting || index >= Starting + Count_items)
                    return 0;
                return BaseList[Starting + index];
            }
            set
            {
                if (index < Starting || index >= Starting + Count_items)
                    return;
                BaseList[Starting + index] = value;
            }
        }

        public FixAByte(int N)
        {
            BaseList = new DynAByte();
            BaseList.Capacity = N;
            for (int i = 0; i < N; i++)
                BaseList.Add(0);
            Count_items = BaseList.Count;
        }

        public FixAByte(byte[] A)
        {
            BaseList = new DynAByte();
            BaseList.Capacity = A.Length;
            foreach (byte i in A)
                BaseList.Add(i);
            Count_items = BaseList.Count;
        }

        public FixAByte(DynAByte base_list, int From_idx, int To_idx)
        {
            Is_slice = true;
            BaseList = base_list;
            Count_items = To_idx + 1 - From_idx;
            Starting = From_idx;
        }

        ~FixAByte()
        {
            if (!Is_slice) BaseList.Clear();
        }

        public int Count { get { return Count_items; } }

        public int IndexOf(byte Value)
        {
            for (int i = Starting; i < Starting + Count_items; i++)
                if (BaseList[i] == Value) return i;
            return -1;
        }

        public bool Contains(byte Value)
        { return IndexOf(Value) >= 0; }
    }



    public class FixABool
    {
        protected DynABool BaseList;
        public int Starting;
        public int Count_items;
        public bool Is_slice;

        public bool this[int index]
        {
            get
            {
                if (index < Starting || index >= Starting + Count_items)
                    return false;
                return BaseList[Starting + index];
            }
            set
            {
                if (index < Starting || index >= Starting + Count_items)
                    return;
                BaseList[Starting + index] = value;
            }
        }

        public FixABool(int N)
        {
            BaseList = new DynABool();
            BaseList.Capacity = N;
            for (int i = 0; i < N; i++)
                BaseList.Add(false);
            Count_items = BaseList.Count;
        }

        public FixABool(bool[] A)
        {
            BaseList = new DynABool();
            BaseList.Capacity = A.Length;
            foreach (bool i in A)
                BaseList.Add(i);
            Count_items = BaseList.Count;
        }

        public FixABool(DynABool base_list, int From_idx, int To_idx)
        {
            Is_slice = true;
            BaseList = base_list;
            Count_items = To_idx + 1 - From_idx;
            Starting = From_idx;
        }

        ~FixABool()
        {
            if (!Is_slice) BaseList.Clear();
        }

        public int Count { get { return Count_items; } }

        public int IndexOf(bool Value)
        {
            for (int i = Starting; i < Starting + Count_items; i++)
                if (BaseList[i] == Value) return i;
            return -1;
        }

        public bool Contains(bool Value)
        { return IndexOf(Value) >= 0; }
    }

    public class FixAStr
    {
        protected DynAStr BaseList;
        public int Starting;
        public int Count_items;
        public bool Is_slice;

        public string this[int index]
        {
            get
            {
                if (index < Starting || index >= Starting + Count_items)
                    return "";
                return BaseList[Starting + index];
            }
            set
            {
                if (index < Starting || index >= Starting + Count_items)
                    return;
                BaseList[Starting + index] = value;
            }
        }

        public FixAStr(int N)
        {
            BaseList = new DynAStr();
            BaseList.Capacity = N;
            for (int i = 0; i < N; i++)
                BaseList.Add("");
            Count_items = BaseList.Count;
        }

        public FixAStr(string[] A)
        {
            BaseList = new DynAStr();
            BaseList.Capacity = A.Length;
            foreach (string i in A)
                BaseList.Add(i);
            Count_items = BaseList.Count;
        }

        public FixAStr(DynAStr base_list, int From_idx, int To_idx)
        {
            Is_slice = true;
            BaseList = base_list;
            Count_items = To_idx + 1 - From_idx;
            Starting = From_idx;
        }

        ~FixAStr()
        {
            if (!Is_slice) BaseList.Clear();
        }

        public int Count { get { return Count_items; } }

        public int IndexOf(string Value)
        {
            for (int i = Starting; i < Starting + Count_items; i++)
                if (BaseList[i] == Value) return i;
            return -1;
        }

        public bool Contains(string Value)
        { return IndexOf(Value) >= 0; }
    }



    public class Stop : Exception
    {
        public string Stop_message;

        public Stop(string message)
        {
            Stop_message = message;
        }
    }

    public class TooDeepRecursionException : Exception
    {
    }
    
//#localize
}
