//------------------------------- AL4.cpp version 2017-01-10 (a)

//---------------- class AL4_system implementation

#include "AL4.h"
#include "All_classes.h"
#include <algorithm>
#include <typeinfo>
#include <iostream>
#include <fstream>
#include <math.h>

int ArrayStr::total_ArrayStr;

int AL4_system::Recursion_counter;
long long AL4_system::Test_caller;

int AL4_object::total_obj_counter;
Ref_obj* AL4_object::None_obj;
Ref_obj* AL4_object::Dummy_obj;

AL4_object* AL4_object::NoneObj_AL4_object;
AL4_object* AL4_object::DummyObj_AL4_object;

void AL4_object::init_class_AL4_object()
{
    NoneObj_AL4_object = (AL4_object*)New_nofields()->Obj;
    NoneObj_AL4_object->hard_link->Count = -1;
    NoneObj_AL4_object->Is_NONE_object = true;
    None_obj = NoneObj_AL4_object->hard_link;
    DummyObj_AL4_object = (AL4_object*)New_nofields()->Obj;
    DummyObj_AL4_object->hard_link->Count = -1;
    Dummy_obj = DummyObj_AL4_object->hard_link;
    NoneObj_AL4_object->write = DummyObj_AL4_object;
    
    NoneObj_AL4_object->DO = (Methods_AL4_object*)
          new Noneclass_AL4_object
            ((AL4_object*)NoneObj_AL4_object);
    DummyObj_AL4_object->DO = (Methods_AL4_object*)
          new Noneclass_AL4_object
            ((AL4_object*)DummyObj_AL4_object);
}

Ref_obj* AL4_object::New_nofields()
{
    AL4_object* Result = new AL4_object();
    Result->hard_link = new Ref_obj(Result, 2);
    Result->weak_link = new Ref_obj(Result, 1);
    Result->write = Result;
    return Result->hard_link;
}

//-------------------------------------
// Safe operation S[i] (string S, int i)
std::string AL4_system::Single_symbol(std::string S, int Pos)
{
	int ch = utf8char(S, Pos);
	
	std::string Result = "_";
	if (Pos >= 0 && Pos < (int)S.length())
	{
		if (ch <= 127)
			Result[0] = (unsigned char)ch;
		else
			Result = utf8substr(S, Pos, 1);
	}
	else Result[0] = '\0';
	return Result;
}

//---------------------------
// Safe Substring() of string
std::string AL4_system::Substring(std::string S, int from, int to)
{
	return utf8substr(S, from, to+1-from);
}

//-----------------------------------------
// Operation S[i] = c (not available in C#)
std::string AL4_system::Replace_symbol(std::string S, int i, std::string r)
{
    if (i < 0 || i >= (int)S.length()) return S;
    return S.substr(0, i) + r + S.substr(i + 1, S.length() - i - 1);
}


#ifdef MSWINDOWS
std::string AL4_system::Wide_string_to_string(std::wstring W)
{
    std::string Result;
    int siz = (int)WideCharToMultiByte(
        CP_ACP, 0, &W[0], W.length(), NULL, 0, "?", NULL);
    Result.reserve(siz);
    for (int i = 0; i < siz; i++) Result += '\0';
    if (siz > 0)
       WideCharToMultiByte(
        CP_ACP, 0, &W[0], W.length(), &Result[0], siz, "?", NULL);
    return Result;    
}
#endif

const int count_nb_utf8[256] = {
	1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, //00
	1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, //20
	1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, //40
	1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, //60
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 80 
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 90 
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // A0 
	1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // B0 
	2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // C0
	2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // D0
	3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // E0
	4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 5, 5, 5, // F0 / F8 
  //4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 7, 7 
	};
	
int AL4_system::utf8len(std::string s)
{
	int Result = s.length();
	for (int i = 0; i < (int)s.length(); i++)
	{
		if (((byte)s[i] & 0x80) == 0x80) 
		{
			int nb = count_nb_utf8[(byte)s[i]] - 1;
			Result -= nb;
			i += nb;
		}
	}
	return Result;
}
	
int AL4_system::utf8char(const char *cur)
{
	if (((byte)*cur & 0x80) == 0) return (int)*cur;
	int nb = count_nb_utf8[(byte)*cur];
	int Result = (byte)*cur++;
	switch (nb)
	{
	case 2: Result &= 0x1F; Result <<= 6;
			Result |= (byte)*cur &0x3F;
			break;
	case 3: Result &= 0x0F; Result <<= 6;
			Result |= (byte)*cur++ &0x3F; Result <<= 6;
			Result |= (byte)*cur &0x3F;
			break;
	case 4: Result &= 0x07; Result <<= 6;
			Result |= (byte)*cur++ &0x3F; Result <<= 6;
			Result |= (byte)*cur++ &0x3F; Result <<= 6;
			Result |= (byte)*cur &0x3F;
			break;
	case 7:
	case 6:
	case 5: Result &= 0x07; Result <<= 6;
			Result |= (byte)*cur++ &0x3F; Result <<= 6;
			Result |= (byte)*cur++ &0x3F; Result <<= 6;
			Result |= (byte)*cur++ &0x3F; Result <<= 6;
			Result |= (byte)*cur &0x3F;
			break;
	}
	return Result;
}
	 
int AL4_system::utf8char(std::string s, int at_index)
{
	
	char *cur =
#ifdef _MSC_VER 
	s == "" ? "" :
#endif
					&s[0];

	char *cur0 = cur;
	int lens = s.length();
	for (int i = at_index; i > 0; i--)
	{
		if (cur - cur0 >= lens) return 0;
        //if (((byte)*cur & 0x80) == 0) cur++;
        //else
        //    while (((byte)*cur & 0x80) == 0x80) cur++;
		int nb = count_nb_utf8[(byte)*cur];
		cur += nb;
	}
	return utf8char(cur);
}

std::string AL4_system::utf8substr(std::string s, int start, int len)
{
	char *cur = 
#ifdef _MSC_VER
	s == "" ? "" :
#endif
					&s[0];
					
	char *cur0 = cur;
	int len1 = s.length();					
	for (int i = start; i > 0; i--)
	{
		if (cur - cur0 >= len1) return "";
		int nb = count_nb_utf8[(byte)*cur];
		cur += nb;
	}
	int beg = cur - cur0;
	
	for (int i = len; i > 0; i--)
	{
		if (cur - cur0 >= len1) break;
		int nb = count_nb_utf8[(byte)*cur];
		cur += nb;
	}
	int end = cur - cur0;
	std::string Result = s.substr(beg, end - beg);
	return Result;
}

std::string AL4_system::nullchar()
{
	std::string Result = "_";
	Result[0] = '\0';
	return Result;
}

//---------------------------
// Optimized comparison of two sub-strings
int AL4_system::CompareSubstrings(std::string S1, std::string S2, 
    int from1, int to1, int from2, int to2)
{
	char *cur1 = 
#ifdef _MSC_VER 
	S1 == "" ? "" :
#endif
					 &S1[0];

	char *cur0 = cur1;
	int len1 = S1.length();					 
	for (int i = from1; i > 0; i--)
	{
		if (cur1 - cur0 >= len1) break;
		int nb = count_nb_utf8[(byte)*cur1];
		cur1 += nb;
	}

	char *cur2 = 
#ifdef _MSC_VER 
	S2 == "" ? "" :
#endif
					 &S2[0];
					 
	cur0 = cur2;
	int len2 = S2.length();
	for (int i = from2; i > 0; i--)
	{
		if (cur2 - cur0 >= len2) break;
		int nb = count_nb_utf8[(byte)*cur2];
		cur2 += nb;
	}
	
	len1 = to1 + 1 - from1;
	len2 = to2 + 1 - from2;
	int n = len1;
	if (n > len2) n = len2;
	
	for (int i = from1; i > 0; i--)
	{
		int char1 = utf8char(cur1);
		int char2 = utf8char(cur2);
		int result = char1 - char2;
		if (result != 0) return result;
		if (char1 == 0 || char2 == 0) break;
		
		int nb = count_nb_utf8[(byte)*cur1];
		cur1 += nb;
		nb = count_nb_utf8[(byte)*cur2];
		cur2 += nb;
	}
	
	return len1 - len2; 
}

//---------------------------
// Optimized comparison of a sub-string with a string
int AL4_system::CompareSubstrWithStr(std::string S1,  
    int from1, int to1, std::string S2)
{
	char *cur1 =
#ifdef _MSC_VER 
	S1 == "" ? "" :
#endif
					 &S1[0];
					 
	char *cur0 = cur1;
	int len1 = S1.length();
	for (int i = from1; i > 0; i--)
	{
		if (cur1 - cur0 >= len1) break;
		int nb = count_nb_utf8[(byte)*cur1];
		cur1 += nb;
	}

	char *cur2 = 
#ifdef _MSC_VER 
	S2 == "" ? "" :
#endif
					 &S2[0];
	
	int n = to1 + 1 - from1;
	if (n > (int)S2.length()) n = S2.length();
	
	for (int i = n; i > 0; i--)
	{
		int char1 = utf8char(cur1);
		int char2 = utf8char(cur2);
		int result = char1 - char2;
		if (result != 0) return result;
		if (char1 == 0 || char2 == 0) break;
		
		int nb = count_nb_utf8[(byte)*cur1];
		cur1 += nb;
		nb = count_nb_utf8[(byte)*cur2];
		cur2 += nb;
	}
	 
	return 0 - (int)(byte)*cur2;
}

//----------------------
// Convert int - > char to std::string
std::string AL4_system::AsCharStr(int c)
{
	std::string s = std::string("") + (char)c;
    return s;
}

//----------------------
// Concatenate 2 strings
std::string AL4_system::Concat2Strings(std::string S1, std::string S2)
{
    std::string Result = S1 + S2;
    return Result;
}

bool AL4_system::like_str(std::string S1, std::string S2)
{
#ifdef present_C_String_functions
	std::string s1 = C_String_functions::M__Upper(S1);
	std::string s2 = C_String_functions::M__Upper(S2);
#else
	std::string s1 = S1;
	std::string s2 = S2;
#endif
	bool result = s1 == s2;
	if (result) return true;
	if (!StrContains(s2, "%") && !StrContains(s2, "?")) return false;
#ifdef present_C_String_functions
	result = C_String_functions::M__Like(s1, s2);
#endif
	return result;
}

//----------------------
// Convert int to string
std::string AL4_system::StrFromInt(int x)
{
	return std::to_string((long long)x);
}

//-------------------------
// Convert double to string
std::string remove_ending_zeroes(const std::string s)
{
    std::string Result = s;
    int e_index = s.length();
    for (int i = (int)s.length()-1; i > 0; i--)
        if (s[i] == 'E')
        {
            e_index = i;
            break;
        }
    for (int i = e_index-1; i > 0; i--)
        if (s[i] == '0' && s[i-1] >= '0' && s[i-1] <= '9')
            Result = Result.substr(0, i) + Result.substr(i+1);
        else break;
    return Result;
}

std::string AL4_system::StrFromDouble(double x)
{
    //std ::string Result = std::to_string((double)x);
    char buf[40];
    snprintf(buf, sizeof(buf), "%.14f", x);
    std ::string Result = buf;
    std::replace(Result.begin(), Result.end(), ',', '.');
    std::replace(Result.begin(), Result.end(), 'e', 'E');
    Result = remove_ending_zeroes(Result);
    std::string s = Result;
	for (int i=(int)Result.length()-2; i > 0; i--)
		if (Result[i] == '-' || Result[i] == '+' || Result[i] == 'E')
			while (i < (int)Result.length()-1 && Result[i+1] == '0')
				Result = Result.substr(0, i+1) + Result.substr(i+2);
	if (((int)Result.find('E')) >= 0 
	|| ((int)Result.find('.')) < 0) return Result;
	for (int i=(int)Result.length()-1; i > 0; i--) {
		if (Result[i] == '0')
			Result = Result.substr(0, i);
		else break;
	}
	if (Result[Result.length()-1] == '.')
		Result = Result.substr(0, Result.length()-1);
    return Result;
}

//-------------------------
// Convert bool to string
std::string AL4_system::StrFromBool(bool x)
{
    if (x) return std::string("TRUE");
    return std::string("FALSE");
}

int AL4_system::Int(std::string s)
{
    int Result = 0;
    bool Neg = false;
    if (s.length() == 0) return Result;
    int i = 0;
    if (s[i] == '=')
    {
        i++;
        Neg = true;
    }
    else if (s[i] == '+') i++;
    int len = (int)s.length();
    for (int j = i; j < len; 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;
}

int AL4_system::RealAsInt(double X)
{
    return (int)X;
}

int AL4_system::EnumAsInt(byte X)
{
    return (int)X;
}

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

std::string AL4_system::StrHex16(std::string s)
{
    std::string Result = std::string("");
    int len = (int)s.length();
    for (int i = 0; i < len; i++)
        Result = Result + hex16((int)s[i]);
    return Result;
}



int AL4_system::ShiftLeft(int i, int n)
{
    return i << n;
}

int AL4_system::ShiftRight(int i, int n)
{
    return (unsigned)i >> n;
}

int AL4_system::RotateLeft(int i, int m)
{
	unsigned int mask = (0xFFFFffff << (m-1))<<1;
	unsigned int r = (mask & (unsigned int)i) 
		   | (((unsigned int)i << 1) & ~mask) 
		   | (((unsigned int)i & ~mask) >> (m-1));
	return (int)r;
}

int AL4_system::RotateRight(int i, int m)
{
	unsigned int mask = (0xFFFFffff << (m-1))<<1;
	unsigned int r = (mask & (unsigned int)i) 
		   | (((unsigned int)i << (m-1)) & ~mask) 
		   | (((unsigned int)i & ~mask) >> 1);
	return (int)r;
}

//------------------------------------------
// Find substring first position in a string
int AL4_system::Find_str(std::string S, std::string Pattern)
{
	int r = S.find(Pattern);
	if (r <= 0) return r;
	char *cur = &S[0];
	char *cur0 = cur;
	int result = 0;
	for (; cur - cur0 < r; result++)
	{
		int nb = count_nb_utf8[(byte)*cur];
		cur += nb;
	} 
    return result;
}

//--------------------------------------
// Check if string contains a substring:
bool AL4_system::StrContains(std::string S, std::string Pattern)
{
    if (Pattern.length() == 0) return false;
    return Find_str(S, Pattern) >= 0;
}

//--------------------------------------
// optimized check if an item contained in an array:
bool AL4_system::ArrayContainsInt(int* A, int A_count, int Value)
{
    for (int i = 0; i < A_count; i++)
        if (A[(unsigned int)i] == Value) return true;
    return false;
}

bool AL4_system::ArrayContainsByte(byte* A, int A_count, byte Value)
{
    for (int i = 0; i < A_count; i++)
        if (A[(unsigned int)i] == Value) return true;
    return false;
}

bool AL4_system::ArrayContainsStr(std::string A[], int A_count, std::string Value)
{
    for (int i = 0; i < A_count; i++)
    {
    	std::string a = A[(unsigned int)i]; 
        if (a == Value) return true;
        if (a == "")
        {
        	if (Value != "" && Value[0] == '\0') return true; 
        }
    }
    return false;
}

bool AL4_system::ArrayContainsChar(std::string A[], int A_count, int Value)
{
    for (int i = 0; i < A_count; i++)
    {
    	std::string a = A[(unsigned int)i];
    	if (a == "") 
    	{
    		if (Value == 0) return true;
    	} 
        if (utf8char(&a[0], 0) == Value) return true;
    }
    return false;
}



//--------------------------------------
// Is object variable referencing NONE ?
bool AL4_system::Is_none(Ref_obj* obj)
{
    return obj->Obj->Is_NONE_object;
}

//---------------------------------------------------------------
// Safe division of real numbers. Returns INF or NaN in bad cases

double value_NaN()
{
	double d = 1;
	memset(&d, 0xff, sizeof(d));
	return d;
}

double AL4_system::Safe_div(double X, double Y)
{
	#ifdef _MSC_VER
		if (Y == 0) return X > 0 ?
        	std::numeric_limits<double>::infinity() : 
       		-std::numeric_limits<double>::infinity();
	#else
    	if (Y == 0) return X > 0 ? INFINITY : -INFINITY;
	#endif
    try
    {
        return X / Y;
    }
    catch (std::exception& E) { 
        AL4_system::Any_exception(E);
        
        return value_NaN(); 
    }
}

//-------------------------------------------------
// Safe division of int numbers. Returns very large 
// or negative large integer number in bad cases.
int AL4_system::Safe_div_int(int X, int Y)
{
	#ifdef _MSC_VER
	    if (Y == 0) return X > 0 ?
	        2147483647 : -2147483648;
	#else
	    if (Y == 0) return X > 0 ?
	        INT32_MAX : INT32_MIN;
	#endif
    return X / Y;
}

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

//-----------------------
// STOP operation
void AL4_system::STOP(std::string message)
{
    throw new StopException(message);
}

Errors_store* AL4_system::Errors;

//-------------------------
// Should be called on any exception to collect errors:
// maximum 10 errors are stored
// If there were more then 10, middle errors are removed and 
// then Errors list contains:
// 0 to 4 - first 5 errors from Reset_errors
// 5 - empty (to signal that Errors count > 10)
// 6 TO 9 - last 4 errors
void AL4_system::Any_exception(std::exception E)
{
	if (Errors == nullptr) Errors = new Errors_store();
	std ::string e_name =
		#ifdef GCC_COMPILER
		typeid(E).name()
		#else 
		typeid(class E).name()
		#endif
	;
	if (Errors->Count_messages < 10)
	{
		Errors->Messages[Errors->Count_messages] = (char*)malloc(e_name.length() + 1);
		strcpy(Errors->Messages[Errors->Count_messages++], &e_name[0]);
		return;
	}
	if (Errors->Count_messages >= 10)
    {
		free(Errors->Messages[5]);
		for (int i = 5; i < 8; i++)
			Errors->Messages[(unsigned int)i] = Errors->Messages[(unsigned int)i+1];
    }
	Errors->Messages[9] = (char*)malloc(e_name.length() + 1);
	strcpy(Errors->Messages[9], &e_name[0]);
}

void AL4_system::Reset_errors()
{
    if (Errors == nullptr) return;
    int count = Errors->Count_messages;
	for (int i = count-1; i >= 0; i--)
	{
		free(Errors->Messages[i]);
		Errors->Messages[i] = NULL;
	}
	Errors->Count_messages = 0;
}

int AL4_system::Errors_count()
{
    if (Errors == nullptr) return 0;
    return Errors->Count_messages; 
}

std::string AL4_system::Errors_info(int index)
{
    if (Errors == nullptr || index < 0 || index >= Errors->Count_messages)
        return std::string("");
    return std::string(Errors->Messages[index]);
}

//------------------------------------------------------------
// Operation X.{Type}. Returns NONE is X is not of type {Type}. 
Ref_obj* AL4_system::As_obj(Ref_obj* X, Ref_obj* None_y)
{
    AL4_object* probe = X->Obj;
    while (probe != nullptr)
    {
        if (probe->NONE_obj_instance()->Obj == None_y->Obj)
            return X;
        probe = probe->NONE_base_obj();
    }
    return None_y;
}

//----------------------------------------------------------
// Application startup directory
std::string AL4_system::Start_dir()
{
    return Startup_path();
}

//----------------------------------------------------------
// Application startup directory
std::string AL4_system::Startup_path()
{
	char buf[MAX_PATH];
	std::string s = "";
	#ifdef MSWINDOWS
		GetModuleFileNameA(0, buf, sizeof(buf));
		s = std::string(buf);
	#else
		if (readlink("/proc/self/exe", buf, MAX_PATH) > 0)
			s = std::string(buf);
	#endif
	return s;
}

//---------------------------------------------------------
// Clear test data
void AL4_system::Clear_test_data(std::vector<long long> test_lines)
{
    int size = (int)test_lines.size();
    for (int i = 0; i < size; i++)
        if (test_lines[(unsigned int)i] != -1)
            test_lines[(unsigned int)i] = 0;
}

//---------------------------------------------------------
// Remove a range from test data
void AL4_system::Remove_test_data(std::vector<long long>& test_lines, int from, int to)
{
    for (int i = from; i <= to; i++)
    {
        if (i >= (int)test_lines.size()) return;
        test_lines[(unsigned int)i] = -9;
    }
}

//---------------------------------------------------------
// Output test data of class
void AL4_system::Output_test_data(const std::vector<long long>& test_lines,
    const std::vector<int>& test_assert,
    const std::vector<std::string>& test_assert_failed_text,
    const std::string& Class_test_file,
        const std::string& Class_path_source)
{
    std::string path = Class_test_file;
    std::ofstream fs(&path[0]);
	if (!fs.is_open()) return;

    if (test_lines.size() > 0)
    {
        std ::string class_path_source = Class_path_source;
        fs << "source: " + class_path_source + "\n";
    }
    
    int siz = (int)test_lines.size();
    std ::string s = "";
    for (int i = 0; i < siz; i++)
    {
        s = s + StrFromInt(test_lines[(unsigned int)i]) + " ";
        if (s.length() >= 75)
		{
                fs << s << "\n";
                s = "";
		}
	}
    if (s != "") fs << s << "\n";
    
	if (test_assert.size() > 0)
    {
            fs << "* assertions failed in lines:" << "\n";
            siz = (int)test_assert.size();
            for (int i = 0; i < siz; i++)
            {
                fs << StrFromInt(test_assert[(unsigned int)i]);
                if (test_assert_failed_text[(unsigned int)i].length() != 0)
                {
                   fs << " ";
                   fs << test_assert_failed_text[(unsigned int)i];
                }
                fs << "\n";
            }
    }
    fs.close();
}

// adding an assertion failed to list of failed assertions:
void AL4_system::Assertion_failed(int line, std::vector<int>& test_assert,
    std::vector<std::string>& test_assert_failed_text, 
    const std::string& assert_failed_text)
{
    test_assert.push_back(line);
    test_assert_failed_text.push_back(assert_failed_text);
}

bool AL4_system::Need_console = true;

//#define LOG_ALL_WRITE
#include <fstream>
#include <iostream>
#include <string>


#if defined(MSWINDOWS)
//    //#include <stdio.h>
//    //#include <fcntl.h>		// _O_U8TEXT
    #include <io.h>			// _setmode, _fileno 
	#include <windows.h>	// wchar
	#include <wchar.h>	// wchar

	static bool cp_set_utf = false; 
	#define _O_U8TEXT       0x40000
	#define _O_U16TEXT      0x20000
    #define CP_UTF16 1200	
#endif

void AL4_system::Write(const std::string S)
{
#ifdef DIRECT_WRITE
	Write_direct(S);
#else
	WriteRedirected(S);
#endif
}

void AL4_system::Write_direct(std::string S)
{
	#ifdef LOG_ALL_WRITE
		std::fstream log("log_output.txt", std::ios::app);
		log << S;
		log.close();
	#endif
    if (Need_console)
    {
        Need_console = false;
        #ifdef MSWINDOWS
	        AllocConsole();
	        freopen("CONOUT$", "w", stdout);
	        freopen("CONIN$", "r", stdin);
	        std::locale::global(std::locale(""));
        #endif
    }
    #if defined(MSWINDOWS)
        if (!cp_set_utf)
        {
            cp_set_utf = true;
            #ifdef _MSC_VER
	        	//setlocale(LC_ALL, "en_US.UTF-8");
                fflush(stdout);
                SetConsoleCP(CP_UTF16);
                SetConsoleOutputCP(CP_UTF16);
                
                //_setmode(_fileno(stdout), _O_U16TEXT);
                //_setmode(_fileno(stderr), _O_U16TEXT);
                //_setmode(_fileno(stdin), _O_U16TEXT);
            #else
	            SetConsoleCP(CP_UTF8); // utf-8
	            //SetConsoleOutputCP(CP_UTF8); // utf-8
	            SetConsoleOutputCP(65001); // utf-8
	            //system("chcp 65001 >nul");
                //_setmode(_fileno(stdout), _O_U8TEXT);
                //_setmode(_fileno(stderr), _O_U8TEXT);
                //_setmode(_fileno(stdin), _O_U8TEXT);
	            //setlocale(LC_ALL, "en_US.UTF-8");

                //DWORD written;
                //static bool BOM_written = false;
                //static const char UTF8_BOM[] = {(char)0xEF, (char)0xBB, (char)0xBF};
                //if (!BOM_written)
                //{
                //    BOM_written = true;
                //    WriteFile(stdout, (PVOID)&UTF8_BOM,
                //        sizeof(UTF8_BOM), &written, NULL);
                //}
                setvbuf(stdout, nullptr, _IOFBF, 1000);
            #endif
        }
    #endif
    if (S != "")
    {
    	#ifdef MSWINDOWS
    		#ifdef _MSC_VER
		        int rSize = MultiByteToWideChar(CP_UTF8, 0, &S[0], S.length(),
		        	NULL, 0);
		        if(rSize > 0)
		        {
		            std::vector<wchar_t> buffer(rSize);
		            MultiByteToWideChar(CP_UTF8, 0, &S[0], S.length(),
		        		&buffer[0], rSize);
		            DWORD written;
		            static bool BOM_written = false;
		            static const WCHAR UNICODE_BOM = 0xFEFF;
					if (!BOM_written)
		            {
		            	BOM_written = true;
		            	WriteFile(stdout, (PVOID)&UNICODE_BOM, 
		            		sizeof(UNICODE_BOM), &written, NULL);
		            }
		            //std::wstring wS(buffer.begin(), buffer.end());
		            //std::wcout << wS;
                    //DWORD slen = wS.length(); 
                    DWORD slen = buffer.size();
                    //WriteConsoleW(stdout, &wS[0], slen, &slen, NULL);
                    //WriteConsoleW(stdout, &buffer[0], slen, &written, NULL);
                    //std::wcout << &buffer[0];
                    std::wstring w(&buffer[0], slen);
                    std::string s((const char*)&w[0], sizeof(wchar_t)/sizeof(char)*w.size());
                    std::cout << s;
                    
                    //wprintf(L"%S", &buffer[0]);
                    //wprintf(L"%ls", &wS[0]);
                    //wprintf(&wS[0]);
                    //for (int j = 0; j < buffer.size(); j++)
                    //    putwchar(buffer[j]);
		        }
    		#else
	    		//DWORD slen = S.length();
	        	//WriteConsoleA(stdout, &S[0], slen, &slen, NULL);
	        	std::cout << S;
    		#endif
    		fflush(stdout);
    	#else
        	std::cout << S;
    	#endif
    }
}

void (*AL4_system::WriteRedirected)(std::string S);

void AL4_system::WriteLine(std::string S)
{
	Write(S);
    Write("\n");
}

std::string AL4_system::ReadLine()
{
	std :: string s_in;
#ifdef MSWINDOWS
	//std::cin.ignore();
#else
	//std::cin.sync();
	getline(std::cin, s_in);
	std::cin.ignore();
    //std::cin.clear();
    //std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n')
    //while (true)
    //{
    //    char a = std::getch();
    //    Write(a);
    //    std::cout.flush();
    //    if (a == '\n') break;
    //    s_in += a;
    //}
    //std::cin >> s_in;
#endif
	std::string RESULT = std::string(s_in);
	return RESULT;
}

void AL4_system::Save_strings(std::vector<std::string> L, std::string Path)
{
	std :: string path = std :: string(Path);
    std::fstream fs(&path[0],
    		 std::ios_base::out
           | std::ios_base::trunc
           | std::ios_base::binary);
    if (!fs.is_open()) return;
    int size = L.size();
    for(int i = 0; i < size; i++)
    {
	   std::string s = L[i];
       fs << s << "\r\n";
	}
    fs.close();
}

#localize2

std::string AL4_system::get_Localize_str(int n)
{
    if (AL4_system::Localize_name.size() == 0)
    {
        int siz = size_Localize_str;
        for (int i = 0; i < siz; i++)
        {
        	std::string lname;
        	lname.assign(init_Localize_name[(unsigned int)i], 
        		strlen(init_Localize_name[(unsigned int)i]));
            Localize_name.push_back(lname);
            std::string lstr;
            lstr.assign(init_Localize_str[(unsigned int)i],
            	strlen(init_Localize_str[(unsigned int)i]));
            Localize_str.push_back(lstr);
        }
    }
    
    //AL4_system::WriteLine("getting Localize_str[" + AL4_system::StrFromInt(
    //n) + "]=" + init_Localize_name[(unsigned int)n] + " --> " +
    //Localize_str[n]);
    
    return Localize_str[n];
}

static int CNT_AL4_Erase;

void Methods_AL4_object::Erase()
{
 	if (++CNT_AL4_Erase == 0)
 	{              	
 		int dummy = 888;
 	}
	
    //if (THIS_OBJ->TOTAL_obj_index == 37)
    //{ }

    // 1st, destroy children if those are present:
	if (THIS_OBJ->children.size() > 0)
    {
        int siz = (int)THIS_OBJ->children.size();
		for (int i = siz-1; i >= 0; i--)
		{
			Ref_obj* c = THIS_OBJ->children[i];
            c->Obj->DO->Erase();
		}
		THIS_OBJ->children.clear();
    }
    DESTRUCT();

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

    THIS_OBJ->weak_link->Obj = none->Obj;
    THIS_OBJ->hard_link->Obj = none->Obj;
    THIS_OBJ->write = none->Obj->write;

    THIS_OBJ->hard_link = none;
    THIS_OBJ->weak_link = none;
    
    THIS_OBJ = nullptr;
}

int Ref_obj::Total_Ref_obj_Count;

Ref_obj::Ref_obj(AL4_object* obj, int count)
{
    Obj = obj;
    Count = count;
    Total_Ref_obj_Count++;
}


void Ref_obj::Ref_dec_weak()
{
    if (Obj->weak_link->Obj == this->Obj)
    {
        Obj->weak_link->Count-=2;
        return;
    }
    //Obj->weak_link->
    Count -= 2;
    if (Count == 0)
    {
        // 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.
        
        Total_Ref_obj_Count--;
        delete this;
    }
}

void Ref_obj::Ref_dec_hard()
{
    Obj->hard_link->Count -= 2;
    if (Obj->hard_link->Count == 0)
    {
        Ref_obj* weak = Obj->weak_link;
        Obj->DO->Erase();

        Total_Ref_obj_Count--;
        
        weak->Count = (weak->Count | 1) + 1;
        weak->Ref_dec_weak();
        
        delete this;
        
    }
}

#localize3

int AL4_system::global_activity_counter;
std::vector<AL4_handler*> AL4_system::long_operation_handlers;
int ArrayObjHard::total_ArrayObj;

#ifdef LOG_METHODS
	
	int AL4_log_methods::call_count;
	
	std::vector<std::string> AL4_log_methods::buf_out;
	
	void AL4_log_methods::log_method(std::string fun_name)
	{
		if (buf_out.size() >= 10000 || fun_name == "")
		{
			std::string path = LOG_METHODS_PATH "log_methods.log"; 
			FILE * f = fopen(&path[0], "a");
			for (int i = 0; i < (int)buf_out.size(); i++)
			{
				std::string text = buf_out[i];
				fwrite(&text[0], 1, text.length(), f);
			}
			fclose(f);
			buf_out.clear();
		}
		if (disabled_logging > 0 || fun_name == "") return;
		
  		if (++call_count == 0)
  		{ int stop = 1; }
  
		buf_out.push_back(
			AL4_system::StrFromInt(call_count) + ": " + fun_name + "\n");
	}
	
	int AL4_log_methods::disabled_logging;
	
	void AL4_log_methods::disable_log(int d)
	{
		disabled_logging += d;
	}
	
	void AL4_log_methods::reset_logging()
	{
		AL4_log_methods::call_count = 0;
	 	AL4_log_methods::disabled_logging = 0;
		AL4_log_methods::buf_out.clear();
		std::string path = LOG_METHODS_PATH "log_methods.log";
        #ifdef MSWINDOWS
              DeleteFileA(&path[0u]);
     	#else
      		  inlink(&path[0]);
        #endif
		
	}
	
#endif

