#ifndef _Calculation_h_
#define _Calculation_h_

#include "Dll/ErrorCodes.h"
#include "Dll/Structures.h"
#include "Common.h"
#include <math.h>


typedef void (*PointToPointFunc)(PointToPoint_Input_Parameters&, PointToPoint_Output_Parameters&);
typedef void (*LineCalcFunc)(PointToPoint_Input_Parameters&, PointToPoint_Output_Parameters&);
typedef void (*CbrCalcFunc)(PointToPoint_Input_Parameters&, PointToPoint_Output_Parameters&);


PointToPointFunc pPointToPoint = nullptr;
LineCalcFunc pLineCalc = nullptr;
CbrCalcFunc pCbrCalc = nullptr;

/**
 * Loads DLL functions for calculation operations
 * @return bool - true if all functions were successfully loaded, false otherwise
 */
bool LoadDllFunctions() {
    HMODULE hDll = LoadLibrary("hcm4mfs.dll");
    if (!hDll) return false;
    
    pPointToPoint = (PointToPointFunc)GetProcAddress(hDll, "PointToPoint");
    pLineCalc = (LineCalcFunc)GetProcAddress(hDll, "LineCalc");
    pCbrCalc = (CbrCalcFunc)GetProcAddress(hDll, "CbrCalc");
    
    return (pPointToPoint != nullptr && pLineCalc != nullptr && pCbrCalc != nullptr);
}

const double RadToDeg = 180.0/3.14159265358979323846;
const double DegToRad = 3.14159265358979323846/180.0;
/**
 * Checks if a file exists at the given path
 * @param filePath - std::string containing the path to check
 * @return bool - true if file exists, false otherwise
 */
bool fileExists(const std::string& filePath) {
	return std::filesystem::exists(filePath);
}
/**
 * Trims whitespace characters from both ends of a string
 * @param s - const std::string& input string to trim
 * @return std::string - trimmed string
 */
std::string Trimmer(const std::string &s) {
	std::string ss;
	size_t start = s.find_first_not_of(" \n\r\t\f\v");
	ss = (start == std::string::npos) ? "" : s.substr(start);
	size_t end = ss.find_last_not_of(" \n\r\t\f\v");
	return (end == std::string::npos) ? "" : ss.substr(0, end + 1);
}

/**
 * Parses emission band string and converts to numeric value with multiplier
 * @param emissionClassIn - const std::string& emission class string (e.g., "10K", "5M")
 * @return int64_t - parsed numeric value with multiplier applied
 */
int64_t parseEmissionBand(const std::string& emissionClassIn) {
	std::string emissionClass = Trimmer(emissionClassIn);
	
	if (emissionClass.empty()) return 0;

	size_t numEnd = 0;
	while (numEnd < emissionClass.size() && isdigit(emissionClass[numEnd])) {
		numEnd++;
	}

	if (numEnd == 0) return 0;

	int64_t baseValue = std::stoull(emissionClass.substr(0, numEnd));

	if (numEnd >= emissionClass.size()) {
		return baseValue;
	}

	char multiplierChar = emissionClass[numEnd];
	int64_t multiplier;

	switch (multiplierChar) {
		case 'H': multiplier = 1; break;
		case 'K': multiplier = 1'000; break;
		case 'M': multiplier = 1'000'000; break;
		case 'G': multiplier = 1'000'000'000; break;
		default : multiplier = 1;
	}

	double preciseValue = baseValue;
	if (numEnd + 1 < emissionClass.size() && isdigit(emissionClass[numEnd + 1])) {
		preciseValue += (emissionClass[numEnd + 1] - '0') * 0.1;
	}

	return static_cast<int64_t>(preciseValue * multiplier);
}
/**
 * Retrieves border line data from binary files for visualization
 * @param BorderPath - String path to border files directory
 * @param LandFrom - String source country code
 * @param LandTo - String destination country code
 * @param Distance - int distance value
 * @param isCbr - bool indicates if CBR (Cross Border Reception) mode
 * @param Line - Vector<Pointf>& output vector to store border points
 * @return bool - true if border data was successfully loaded, false otherwise
 */
bool GetBorderLine(String BorderPath, String LandFrom, String LandTo, int Distance, bool isCbr, Vector<Pointf>& Line){
	Line.clear();
	String BorderFile = BorderPath + LandFrom + LandTo + ".";

	if (isCbr){
		BorderFile += "cbr";
	} else {
		String distS = AsString(Distance);
		if (distS.GetCount() == 1){ BorderFile += "00" + distS;};
		if (distS.GetCount() == 2){ BorderFile += "0" + distS;};
		if (distS.GetCount() == 3){ BorderFile += distS;};
	}


	if (!fileExists(BorderFile.ToStd())) return false;


	std::vector<double> nRecord(22), nFile(30800);
	int nAll = 0;
	std::ifstream file(BorderFile.ToStd(), std::ios::binary);
	if (!file) return false;

	while (nAll < 1400 && file.read(reinterpret_cast<char*>(&nFile[nAll * 22]), 176)) {
		nAll++;
	}
	file.close();
	for (int i=0; i<=nAll*22; ++i){
		nFile[i] = nFile[i] * RadToDeg;
	}
	for (int j = 0; j < nAll; ++j) {
		for (int k = 0; k < 20; k += 2) {
			Pointf pnt;
			pnt.x = nFile[22 * j + k];
			pnt.y = nFile[22 * j + k + 1];
			Line.Add(pnt);
		}
	}
	return true;
}

/**
 * Extracts transmitter parameters from station data file
 * @param fname - String filename of station data
 * @param param - PointToPoint_Input_Parameters& output parameter structure
 * @param stationName - String& output station name
 * @param index - int index of station in file
 * @return bool - true if parameters were successfully extracted, false otherwise
 */
bool ExtractTxFromFile(String fname, PointToPoint_Input_Parameters& param, String &stationName, int index) {
	FileIn f(fname);
	int fs = f.GetSize();
	if (((fs % 219) != 0) && (fs > 219)) {
		f.Close();
		return false;
	}

	f.Seek(0);
	String allString = f.GetLine();
	String tmp = "";
	int tmp1, tmp2, tmp3 = 0;
	try {
		int offset = 219 + index * 219;

		if (TrimBoth(allString.Mid(offset + 0, 12)).GetCount() < 2) { // NO FREQ TX
			f.Close();
			return false;
		};

		stationName = TrimLeft(TrimRight(allString.Mid(offset + 28, 20)));

		param.ReadCoordinate(allString.Mid(offset + 51, 15).ToStd(), false);

		tmp = TrimBoth(allString.Mid(offset+71,4));
		param.Tx_H_Site_Ant = param.TryReadOrValue(tmp.ToStd());

		Upp::String hAnt = allString.Mid(offset + 111, 7);
		Upp::String vAnt = allString.Mid(offset + 118, 7);
		
		strncpy(param.Tx_Ant_typ_H, ~hAnt, 7);
		param.Tx_Ant_typ_H[7] = '\0';
		
		strncpy(param.Tx_Ant_typ_V, ~vAnt, 7);
		param.Tx_Ant_typ_V[7] = '\0';

		tmp = allString.Mid(offset + 91, 5);
		param.Tx_Azi = param.TryReadOrValue(tmp.ToStd());

		tmp = allString.Mid(offset + 96, 5);
		tmp.Replace(" ","");
		param.Tx_Elev = param.TryReadOrValue(tmp.ToStd());

		tmp = allString.Mid(offset + 107, 4);
		param.Tx_H_Ant = param.TryReadOrValue(tmp.ToStd());

		//[0,1]=[E/I]
		tmp = ToUpper(allString.Mid(offset + 90, 1));
		(tmp == "I") ? param.Tx_Type_of_Ant = 1 : param.Tx_Type_of_Ant = 0;

		tmp = allString.Mid(offset + 84, 6);
		param.Tx_Power = param.TryReadOrValue(tmp.ToStd());

		param.Tx_Freq = ReadFreq(allString.Mid(offset + 0, 12));

		tmp = ToUpper(allString.Mid(offset + 19, 1));
		(tmp == "1") ? param.Chan_occup = 1 : param.Chan_occup = 0;

		tmp = allString.Mid(offset + 66, 5);
		param.Tx_Serv_area = param.TryReadOrValue(tmp.ToStd());

		strncpy(param.Tx_Desig_of_emis, ~allString.Mid(offset + 75, 9), 9);
		param.Tx_Desig_of_emis[9] = '\0';
		
		strncpy(param.Land_from, ~allString.Mid(offset + 48, 3), 3);
		param.Land_from[3] = '\0';
	} catch (...) {
		f.Close();
		return false;
	}
	f.Close();
	return true;
}

/**
 * Extracts receiver parameters from station data file
 * @param fname - String filename of station data
 * @param param - PointToPoint_Input_Parameters& output parameter structure
 * @param stationName - String& output station name
 * @param index - int index of station in file
 * @return bool - true if parameters were successfully extracted, false otherwise
 */
bool ExtractRxFromFile(String fname, PointToPoint_Input_Parameters& param, String &stationName, int index) {
	FileIn f(fname);
	int fs = f.GetSize();
	if (((fs % 219) != 0) && (fs > 219)) {
		f.Close();
		return false;
	}
	f.Seek(0);
	String allString = f.GetLine();
	String tmp = "";
	int tmp1, tmp2, tmp3 = 0;
	try {
		int offset = 219 + index * 219;

		if (TrimBoth(allString.Mid(offset + 125, 12)).GetCount() < 2) { // NO FREQ RX
			f.Close();
			return false;
		};

		stationName = TrimBoth(allString.Mid(offset + 28, 20));

		param.ReadCoordinate(allString.Mid(offset + 51, 15).ToStd(), true);

		tmp = TrimBoth(allString.Mid(offset+71,4));
		param.Rx_H_Site_Ant = param.TryReadOrValue(tmp.ToStd());

		strncpy(param.Rx_Ant_typ_H, ~allString.Mid(offset + 111, 7), 7);
		param.Rx_Ant_typ_H[7] = '\0';
		
		strncpy(param.Rx_Ant_typ_V, ~allString.Mid(offset + 118, 7), 7);
		param.Rx_Ant_typ_V[7] = '\0';
		
		tmp = allString.Mid(offset + 91, 5);
		param.Rx_Azi = param.TryReadOrValue(tmp.ToStd());

		tmp = allString.Mid(offset + 96, 5);
		tmp.Replace(" ","");
		param.Rx_Elev = param.TryReadOrValue(tmp.ToStd());

		tmp = allString.Mid(offset + 107, 4);
		param.Rx_H_Ant = param.TryReadOrValue(tmp.ToStd());

		//[0,1]=[E/I]
		tmp = ToUpper(allString.Mid(offset + 90, 1));
		(tmp == "I") ? param.Rx_Type_of_Ant = 1 : param.Rx_Type_of_Ant = 0;

		param.Rx_Freq = ReadFreq(allString.Mid(offset + 125, 12));

		tmp = ToUpper(allString.Mid(offset + 19, 1));
		(tmp == "1") ? param.Chan_occup = 1 : param.Chan_occup = 0;

		tmp = allString.Mid(offset + 66, 5);
		param.Rx_Serv_area = param.TryReadOrValue(tmp.ToStd());
	
		strncpy(param.Rx_Desig_of_emis, ~allString.Mid(offset + 75, 9), 9);
		param.Rx_Desig_of_emis[9] = '\0';
		
		strncpy(param.Land_to, ~allString.Mid(offset + 48, 3), 3);
		param.Land_to[3] = '\0';

		tmp = allString.Mid(offset + 103, 4);
		param.Rx_Ant_gain = param.TryReadOrValue(tmp.ToStd());
	} catch (...) {
		f.Close();
		return false;
	}
	f.Close();
	return true;
}

/**
 * Clears and initializes input parameters structure with default values
 * @param input - PointToPoint_Input_Parameters& parameter structure to clear
 */
void ClearInputParams( PointToPoint_Input_Parameters& input){
	input.Sea_temperature[0] = 'W',
	input.Tx_Ant_typ_H[0] = '\0';
	input.Tx_Ant_typ_V[0] = '\0';
	input.Rx_Ant_typ_H[0] = '\0';
	input.Rx_Ant_typ_V[0] = '\0';
	input.Tx_Desig_of_emis[0] = '\0';
	input.Rx_Desig_of_emis[0] = '\0';
	input.Land_from[0] = '\0';
	input.Land_to[0] = '\0';
	input.Debug_file[0] = '\0';
	input.Topo_path[0] = '\0';
	input.Morpho_path[0] = '\0';
	input.Border_path[0] = '\0';
	
	input.useAnnex1 = false; // for mode P2p and CBR

	input.Rx_Latitude = HCM4A_ERROR_VALUE_DOUBLE;
	input.Rx_Longitude = HCM4A_ERROR_VALUE_DOUBLE;
	input.Rx_Freq = HCM4A_ERROR_VALUE_DOUBLE;
	input.Rx_Serv_area = HCM4A_ERROR_VALUE_DOUBLE;
	input.Rx_Ant_gain = HCM4A_ERROR_VALUE_DOUBLE;
	input.Depol_loss = HCM4A_ERROR_VALUE_DOUBLE;
	input.Rx_Azi = HCM4A_ERROR_VALUE_DOUBLE;
	input.Rx_Elev = HCM4A_ERROR_VALUE_DOUBLE;
	input.Tx_Latitude = HCM4A_ERROR_VALUE_DOUBLE;
	input.Tx_Longitude = HCM4A_ERROR_VALUE_DOUBLE;
	input.Tx_Serv_area = HCM4A_ERROR_VALUE_DOUBLE;
	input.Tx_Freq = HCM4A_ERROR_VALUE_DOUBLE;
	input.Tx_Power = HCM4A_ERROR_VALUE_DOUBLE;
	input.Tx_Azi = HCM4A_ERROR_VALUE_DOUBLE;
	input.Tx_Elev = HCM4A_ERROR_VALUE_DOUBLE;
	input.Cor_fact_frequ_diff = HCM4A_ERROR_VALUE_DOUBLE,
	input.PointDistance = HCM4A_ERROR_VALUE_DOUBLE;
	input.Perm_FS_input = HCM4A_ERROR_VALUE_DOUBLE;

	input.C_Mode = HCM4A_ERROR_VALUE_INT;
	input.Rx_H_Ant = HCM4A_ERROR_VALUE_INT;
	input.Rx_H_Site_Ant = HCM4A_ERROR_VALUE_INT;
	input.Rx_Type_of_Ant = 0; //Rx_Type_of_Ant [0,1]=[E/I]
	input.Tx_H_Ant = HCM4A_ERROR_VALUE_INT;
	input.Tx_H_Site_Ant = HCM4A_ERROR_VALUE_INT;
	input.Tx_Type_of_Ant = 0; //Tx_Type_of_Ant [0,1]=[E/I]
	input.Chan_occup = 0;
	input.Max_CBR_D_input = HCM4A_ERROR_VALUE_INT;//Input value of maximum cross-border range
	//(if -999, the value is taken from the table in Annex 1)
	input.D_to_border = HCM4A_ERROR_VALUE_INT;
}

/**
 * Prepares results table with appropriate columns and widths
 * @param table - ArrayCtrl& table control to initialize
 */
void PrepareTable( ArrayCtrl& table){
	int wSmall = 12, wMiddle = 20, wLarge=44, wNote = 70, wSeparator = 1, wNo = 0;
	
	table.AddColumnAt(0, "№", wSmall);
	table.AddColumnAt(1, "Station name", wLarge);
	table.AddColumnAt(2, "Tx land", wMiddle);
	table.AddColumnAt(3, "Tx Antenna H", wMiddle);
	table.AddColumnAt(4, "Tx Azimut", wMiddle);
	table.AddColumnAt(5, "Tx max. power", wMiddle);
	table.AddColumnAt(6, "Tx radius", wMiddle);
	table.AddColumnAt(7, "Tx freq", wMiddle);
	table.AddColumnAt(8, "Station name", wLarge);
	table.AddColumnAt(9, "Rx land", wMiddle);
	table.AddColumnAt(10, "Rx Antenna H",  wMiddle);
	table.AddColumnAt(11, "Rx Azimut",  wMiddle);
	table.AddColumnAt(12, "Rx radius",  wMiddle);
	table.AddColumnAt(13, "Rx freq",  wMiddle);
	table.AddColumnAt(14, " ",  wSeparator);
	table.AddColumnAt(15, "Border",  wMiddle);
	table.AddColumnAt(16, "Distance",  wMiddle);
	table.AddColumnAt(17, "Azimut",  wMiddle);
	table.AddColumnAt(18, "Max f.s", wMiddle);
	table.AddColumnAt(19, "Permis.f.s", wMiddle);
	table.AddColumnAt(20, "Protect.magrin", wMiddle);
	table.AddColumnAt(21, "Comment",  wNote);
	
	table.AddColumnAt(22, "StationFile", wNo);
	table.AddColumnAt(23, "Index in file", wNo);
	table.AddColumnAt(24, "isEquvalentTx", wNo);
}

/**
 * Performs Point-to-Point calculation between transmitter and receiver stations
 * Processes all station combinations and populates results table
 */
void MobileService::CalculationP2p(){
	String tmpS;

	String stationName1, stationName2;
	Progress prg;
	prg.Delay(0);

	String country1, country2, debugPath, debugFile, infoString;
	Time time;
	double permissibleFiledStrength = 0;

	PointToPoint_Input_Parameters input, inputEq;
	PointToPoint_Output_Parameters output;


	std::vector<std::string> CbrAndLineOnMap;

	LineOnMap lop;
	LinesOnMap.clear();

	auto GetDebugFileName = [&]() {
		time = GetSysTime();
		debugFile = AsString(time) + "_" + AsString(Random(1000));
		debugFile.Replace("/", "_");
		debugFile.Replace(" ", "_");
		debugFile.Replace(":", "_");
		debugFile = debugPath + debugFile + ".txt";
		debugFiles.Add(debugFile);
		try{
			FileOut f;
			f.Open(debugFile);
			f.Close();
		} catch (...) {
		}
		strncpy(input.Debug_file, ~debugFile, sizeof(input.Debug_file) - 1);
		input.Debug_file[sizeof(input.Debug_file) - 1] = '\0'; 
	};

	auto contains = [](const std::vector<std::string>& vec, const std::string& str) {
		return std::find(vec.begin(), vec.end(), str) != vec.end();
	};

	tabResP2p.table.Clear();
	if (tabResP2p.table.GetColumnCount() == 0) PrepareTable(tabResP2p.table);
	
	
	LoadDllFunctions();
	if (!pPointToPoint) {
	    Prompt(Null, 0, "Function not found in DLL", CtrlImg::error(), 
	           "PointToPoint function not available", true, "Ok");
	    return;
	}
		
	
	debugPath = GetExeFolder() + "\\debug\\";
	RealizePath(debugPath);
	debugFiles.Clear();

	CbrAndLineOnMap.clear();

	flagP2pCalcDone = false;

	ClearInputParams(input);
	if (StationsList.GetCount() < 1) { ErrorOK("There are no stations"); return; };

	int index = 1;
	prg.Set(0, StationsList.GetCount());

	int idx = tabResP2p.drlPointToPoint.GetIndex();
	input.C_Mode = 0;
	if (idx == 1) input.C_Mode = 10;
	if (idx == 2) input.C_Mode = 11;
	if (idx == 3) input.C_Mode = 12;
	input.Perm_FS_input = input.TryReadOrValue(tabResP2p.p2pPermFS.GetData().ToString().ToStd(), 0);
	input.Depol_loss = input.TryReadOrValue(tabResP2p.p2pDepolLoss.GetData().ToString().ToStd(), 0);

	input.Sea_temperature[0] = 'W';
	input.PointDistance = 0.1;
	input.useAnnex1 = tabResP2p.ChBAnnex1PtoP;

	strncpy(input.Topo_path, ~cnfg.PathToTopo, 255);
	input.Topo_path[255] = '\0';
	
	strncpy(input.Morpho_path, ~cnfg.PathToMorpho, 255);
	input.Morpho_path[255] = '\0';
	
	strncpy(input.Border_path, ~cnfg.PathToBorders, 255);
	input.Border_path[255] = '\0';
	input.MaxCalcDistanceKm = Upp::max(Upp::min(cnfg.MaxCalcDistance,AcceptedMaxCalculateDistanceKm),AcceptedMinCalculateDistanceKm);


	for (int i = 0; i < StationsList.GetCount(); i++) {
		if (!StationsList[i].isRefList) continue;

		// Get Only Tx from TEST LIST
		if (!ExtractRxFromFile(StationsList[i].Filename, input, stationName2, StationsList[i].IndexInFile)) continue;
			
			
		for (int j = 0; j < StationsList.GetCount(); j++) {
			// ONLY REF LIST
			if (StationsList[j].isRefList) continue;

			if (ExtractTxFromFile(StationsList[j].Filename, input, stationName1, StationsList[j].IndexInFile)) {
				GetDebugFileName();

				try {
					pPointToPoint(input, output);

					(input.useAnnex1) ? infoString = "Annex1 used. " : infoString = "";
					infoString = infoString + AsString(output.HCM_error) + " " + getErrorMessageI(output.HCM_error);
					
						tabResP2p.table.Add(index, stationName1, input.Land_from, input.Tx_H_Ant, input.Tx_Azi,
							input.Tx_Power, input.Tx_Serv_area, input.Tx_Freq,
							stationName2, input.Land_to, input.Rx_H_Ant, input.Rx_Azi, input.Rx_Serv_area, input.Rx_Freq, "", "",
							Format("%.2mf", output.Distance),
							Format("%.1mf", output.Dir_Tx_Rx),
							Format("%.2mf", output.Calculated_FS),
							Format("%.2mf", output.Perm_FS),
							Format("%.2mf", output.Prot_margin),
							infoString,
							StationsList[j].Filename,
							StationsList[j].IndexInFile
						);

					lop.Name = stationName1 + "->" + stationName2;
					lop.Points.SetCount(2);
					lop.Points[0].x = output.Tx_Longitude_Calc;
					lop.Points[0].y = output.Tx_Latitude_Calc;
					lop.Points[1].x = output.Rx_Longitude_Calc;
					lop.Points[1].y = output.Rx_Latitude_Calc;
					lop.LineColor = LtBlue();
					LinesOnMap.Add(pick(lop));
					if (output.HCM_error == 0) flagP2pCalcDone = true;

				} catch (...) {
						tabResP2p.table.Add(index, stationName1, input.Land_from, input.Tx_H_Ant, input.Tx_Azi,
							input.Tx_Power, input.Tx_Serv_area, input.Tx_Freq,
							stationName2, input.Land_to, input.Rx_H_Ant, input.Rx_Azi, input.Rx_Serv_area, input.Rx_Freq, "", "",
							Format("%.2mf", output.Distance),
							Format("%.1mf", output.Dir_Tx_Rx),
							Format("%.2mf", output.Calculated_FS),
							Format("%.2mf", output.Perm_FS),
							Format("%.2mf", output.Prot_margin),
							"ERROR IN DLL",
							"",
							""
						);
				}
				index++;
			}
		}
	}
	~prg;
	RefreshMapTree();
	UpdateImageResultsCombineLayout();
}


/**
 * Performs Line calculation for cross-border analysis
 * Calculates field strength along specified border lines
 */
void MobileService::CalculationLine() {
	String tmpS;

	String stationName1, stationName2;
	Progress prg;
	prg.Delay(0);

	String country1, country2, debugPath, debugFile, infoString;
	Time time;
	double permissibleFiledStrength = 0;

	PointToPoint_Input_Parameters input, inputEq;
	PointToPoint_Output_Parameters output;


	std::vector<std::string> CbrAndLineOnMap;

	LineOnMap lop;
	LinesOnMap.clear();
	
	flagLineCalcDone = false;

	auto GetDebugFileName = [&]() {
		time = GetSysTime();
		debugFile = AsString(time) + "_" + AsString(Random(1000));
		debugFile.Replace("/", "_");
		debugFile.Replace(" ", "_");
		debugFile.Replace(":", "_");
		debugFile = debugPath + debugFile + ".txt";
		debugFiles.Add(debugFile);
		try{
			FileOut f;
			f.Open(debugFile);
			f.Close();
		} catch (...) {
		}
		strncpy(input.Debug_file, ~debugFile, sizeof(input.Debug_file) - 1);
		input.Debug_file[sizeof(input.Debug_file) - 1] = '\0';
	};

	auto contains = [](const std::vector<std::string>& vec, const std::string& str) {
		return std::find(vec.begin(), vec.end(), str) != vec.end();
	};

	tabResLine.table.Clear();
	if (tabResLine.table.GetColumnCount() == 0) PrepareTable(tabResLine.table);
	
	LoadDllFunctions();
	if (!pLineCalc) {
	    Prompt(Null, 0, "Function not found in DLL", CtrlImg::error(), 
	           "PointToPoint function not available", true, "Ok");
	    return;
	}
    
	debugPath = GetExeFolder() + "\\debug\\";
	RealizePath(debugPath);
	debugFiles.Clear();

	CbrAndLineOnMap.clear();

		ClearInputParams(input);

		int idx = tabResLine.drlLine.GetIndex();
		input.C_Mode = -1;
		if (idx == 0) input.C_Mode = -9;
		if (idx == 1) input.C_Mode = -10;
		if (idx == 2) input.C_Mode = -11;
		
		Upp::String landTo = tabResLine.lineLandTo.GetValue(tabResLine.lineLandTo.GetIndex()).ToString().Mid(0, 3);
		strncpy(input.Land_to, ~landTo, 3);   
		input.Land_to[3] = '\0';            
		input.Depol_loss = 0;

		input.Sea_temperature[0] = 'W';
		input.PointDistance = 0.1;
		input.useAnnex1 = false;

		strncpy(input.Topo_path, ~cnfg.PathToTopo, 255);
		input.Topo_path[255] = '\0';
		
		strncpy(input.Morpho_path, ~cnfg.PathToMorpho, 255);
		input.Morpho_path[255] = '\0';
		
		strncpy(input.Border_path, ~cnfg.PathToBorders, 255);
		input.Border_path[255] = '\0';
		input.MaxCalcDistanceKm = Upp::max(Upp::min(cnfg.MaxCalcDistance,AcceptedMaxCalculateDistanceKm),AcceptedMinCalculateDistanceKm);


		if (StationsList.GetCount() < 1) { ErrorOK("There are no stations"); return; };

		int count = 1;
		for (int i = 0; i < StationsList.GetCount(); i++) if (!StationsList[i].isRefList) count++;

		int index = 1;
		prg.Set(0, count);
		for (int j = 0; j < StationsList.GetCount(); j++) {
			if (StationsList[j].isRefList) continue;

			prg.Step();

			if (!ExtractTxFromFile(StationsList[j].Filename, input, stationName1, StationsList[j].IndexInFile)) {
				continue;
			}
			GetDebugFileName();

			input.D_to_border = input.TryReadOrValue(tabResLine.lineCross.GetText().ToString().ToStd(), 0);
			std::string key = std::string(input.Land_from) + 
                  std::string(input.Land_to) + 
                  std::to_string(input.D_to_border);
                  
			if (!contains(CbrAndLineOnMap, key)) {
				if (GetBorderLine(input.Border_path, input.Land_from, input.Land_to, input.D_to_border, false, lop.Points)){
					lop.Name = std::string("Line. ") + 
						std::string(input.Land_from) + "->" + 
						std::string(input.Land_to) + " " + 
						std::to_string(input.D_to_border) + " km.";
					lop.LineColor = LtRed();
					LinesOnMap.Add(pick(lop));
					CbrAndLineOnMap.push_back(
						std::string(input.Land_from) + 
						std::string(input.Land_to) + 
						std::to_string(input.D_to_border)
					);
				}
			}
			if (tabResLine.Line2CheckBox){
				input.D_to_border = input.TryReadOrValue(tabResLine.lineCross2.GetText().ToString().ToStd(), 0);
				std::string key = std::string(input.Land_from) + 
	                  std::string(input.Land_to) + 
	                  std::to_string(input.D_to_border);
				if (!contains(CbrAndLineOnMap, key)) {
					if (GetBorderLine(input.Border_path, input.Land_from, input.Land_to, input.D_to_border, false, lop.Points)){
						lop.Name = std::string("Line. ") + 
							std::string(input.Land_from) + "->" + 
							std::string(input.Land_to) + " " + 
							std::to_string(input.D_to_border) + " km.";
						lop.LineColor = LtRed();
						LinesOnMap.Add(pick(lop));
						CbrAndLineOnMap.push_back(
							std::string(input.Land_from) + 
							std::string(input.Land_to) + 
							std::to_string(input.D_to_border)
						);
					}
				}
			}



			try{
				input.D_to_border = input.TryReadOrValue(tabResLine.lineCross.GetText().ToString().ToStd(), 0);
				input.Perm_FS_input = input.TryReadOrValue(tabResLine.linePermFS.GetData().ToString().ToStd(), 0);

				pLineCalc(input, output);

				infoString = "";
				double dop5 = parseEmissionBand(input.Tx_Desig_of_emis);
				if (dop5 > 5000000 && tabResLine.CorrectionCheckBox){
					dop5 = 10*log10(dop5/5000000);
					infoString = "Added 5Mhz corr. ";
				}else{
					dop5 = 0;
				}
				infoString = infoString + AsString(output.HCM_error) + " " + getErrorMessageI(output.HCM_error);

				tabResLine.table.Add(index, stationName1, input.Land_from, input.Tx_H_Ant, input.Tx_Azi,
					input.Tx_Power, input.Tx_Serv_area, input.Tx_Freq,
					stationName2, input.Land_to, input.Rx_H_Ant, Format("%.2mf", output.Dir_Rx_Tx), input.Rx_Serv_area, input.Rx_Freq, "", AsString(input.D_to_border),
					Format("%.2mf", output.Distance),
					Format("%.1mf", output.Dir_Tx_Rx),
					Format("%.2mf", output.Calculated_FS),
					Format("%.2mf", output.Perm_FS + dop5),
					Format("%.2mf", output.Prot_margin - dop5),
					infoString,
					StationsList[j].Filename,
					StationsList[j].IndexInFile
				);

				if (output.HCM_error == 0){
					lop.Name = stationName1 + "->" + input.Land_to + " " + std::to_string(input.D_to_border) + " km.";
					lop.Points.SetCount(2);
					lop.Points[0].x = output.Tx_Longitude_Calc;
					lop.Points[0].y = output.Tx_Latitude_Calc;
					lop.Points[1].x = output.Rx_Longitude_Calc;
					lop.Points[1].y = output.Rx_Latitude_Calc;
					lop.LineColor = LtBlue();
					LinesOnMap.Add(pick(lop));
					flagLineCalcDone = true;
				}

			} catch (...) {
				tabResLine.table.Add(index, stationName1, input.Land_from, input.Tx_H_Ant, input.Tx_Azi,
					input.Tx_Power, input.Tx_Serv_area, input.Tx_Freq,
					stationName2, input.Land_to, input.Rx_H_Ant, Format("%.2mf", output.Dir_Rx_Tx), input.Rx_Serv_area, input.Rx_Freq, "", AsString(input.D_to_border),
					Format("%.2mf", output.Distance),
					Format("%.1mf", output.Dir_Tx_Rx),
					Format("%.2mf", output.Calculated_FS),
					Format("%.2mf", output.Perm_FS),
					Format("%.2mf", output.Prot_margin),
					"ERROR IN DLL",
					"",""
				);
			}
			index++;


			if (tabResLine.Line2CheckBox){
				GetDebugFileName();
				try{

					input.D_to_border = input.TryReadOrValue(tabResLine.lineCross2.GetText().ToString().ToStd(), 0);
					input.Perm_FS_input = input.TryReadOrValue(tabResLine.linePermFS2.GetData().ToString().ToStd(), 0);

					pLineCalc(input, output);

					infoString = "";
					double dop5 = parseEmissionBand(input.Tx_Desig_of_emis);
					if (dop5 > 5000000 && tabResLine.CorrectionCheckBox){
						dop5 = 10*log10(dop5/5000000);
						infoString = "Added 5Mhz corr. ";
					}else{
						dop5 = 0;
					}
					infoString = infoString + AsString(output.HCM_error) + " " + getErrorMessageI(output.HCM_error);

					tabResLine.table.Add(index, stationName1, input.Land_from, input.Tx_H_Ant, input.Tx_Azi,
						input.Tx_Power, input.Tx_Serv_area, input.Tx_Freq,
						stationName2, input.Land_to, input.Rx_H_Ant, Format("%.2mf", output.Dir_Rx_Tx), input.Rx_Serv_area, input.Rx_Freq, "", AsString(input.D_to_border),
						Format("%.2mf", output.Distance),
						Format("%.1mf", output.Dir_Tx_Rx),
						Format("%.2mf", output.Calculated_FS),
						Format("%.2mf", output.Perm_FS + dop5),
						Format("%.2mf", output.Prot_margin - dop5),
						infoString,
						StationsList[j].Filename,
						StationsList[j].IndexInFile
					);

					if (output.HCM_error == 0){
						lop.Name = stationName1 + "->" + input.Land_to + " " + std::to_string(input.D_to_border) + " km.";
						lop.Points.SetCount(2);
						lop.Points[0].x = output.Tx_Longitude_Calc;
						lop.Points[0].y = output.Tx_Latitude_Calc;
						lop.Points[1].x = output.Rx_Longitude_Calc;
						lop.Points[1].y = output.Rx_Latitude_Calc;
						lop.LineColor = LtBlue();
						LinesOnMap.Add(pick(lop));
					}

				} catch (...) {
					tabResLine.table.Add(index, stationName1, input.Land_from, input.Tx_H_Ant, input.Tx_Azi,
						input.Tx_Power, input.Tx_Serv_area, input.Tx_Freq,
						stationName2, input.Land_to, input.Rx_H_Ant, Format("%.2mf", output.Dir_Rx_Tx), input.Rx_Serv_area, input.Rx_Freq, "", AsString(input.D_to_border),
						Format("%.2mf", output.Distance),
						Format("%.1mf", output.Dir_Tx_Rx),
						Format("%.2mf", output.Calculated_FS),
						Format("%.2mf", output.Perm_FS),
						Format("%.2mf", output.Prot_margin),
						"ERROR IN DLL", "", ""
					);
				}
				index++;
			}
		}
	~prg;
	RefreshMapTree();
	UpdateImageResultsCombineLayout();

}

/**
 * Performs Cross Border Reception (CBR) calculation
 * Analyzes interference from transmitters to receivers across borders
 */
void MobileService::CalculationCbr() {
	String tmpS;

	String stationName1, stationName2;
	Progress prg;
	prg.Delay(0);

	String country1, country2, debugPath, debugFile, infoString;
	Time time;
	double permissibleFiledStrength = 0;

	PointToPoint_Input_Parameters input, inputEq;
	PointToPoint_Output_Parameters output;

	flagCbrCalcDone = false;

	std::vector<std::string> CbrAndLineOnMap;

	LineOnMap lop;
	LinesOnMap.clear();

	auto GetDebugFileName = [&]() {
		time = GetSysTime();
		debugFile = AsString(time) + "_" + AsString(Random(1000));
		debugFile.Replace("/", "_");
		debugFile.Replace(" ", "_");
		debugFile.Replace(":", "_");
		debugFile = debugPath + debugFile + ".txt";
		debugFiles.Add(debugFile);
		try{
			FileOut f;
			f.Open(debugFile);
			f.Close();
		} catch (...) {
		}
		strncpy(input.Debug_file, ~debugFile, sizeof(input.Debug_file) - 1);
		input.Debug_file[sizeof(input.Debug_file) - 1] = '\0';
	};

	auto contains = [](const std::vector<std::string>& vec, const std::string& str) {
		return std::find(vec.begin(), vec.end(), str) != vec.end();
	};


	tabResCbr.table.Clear();
	if (tabResCbr.table.GetColumnCount() == 0) PrepareTable(tabResCbr.table);

	LoadDllFunctions();
	if (!pCbrCalc) {
	    Prompt(Null, 0, "Function not found in DLL", CtrlImg::error(), 
	           "PointToPoint function not available", true, "Ok");
	    return;
	};
    
	debugPath = GetExeFolder() + "\\debug\\";
	RealizePath(debugPath);
	debugFiles.Clear();

	CbrAndLineOnMap.clear();


		ClearInputParams(input);
		int idx = tabResCbr.drlCbr.GetIndex();
		input.C_Mode = -1;
		if (idx == 1) input.C_Mode = -9;
		if (idx == 2) input.C_Mode = -10;
		if (idx == 3) input.C_Mode = -11;

		Upp::String landTo = tabResCbr.cbrLandTo.GetValue(tabResCbr.cbrLandTo.GetIndex()).ToString().Mid(0, 3);
			strncpy(input.Land_to, ~landTo, 3);   
			input.Land_to[3] = '\0';            
		input.D_to_border = input.TryReadOrValue(tabResCbr.cbrCross.GetText().ToString().ToStd(), 0);
		input.Perm_FS_input = input.TryReadOrValue(tabResCbr.cbrPermFS.GetData().ToString().ToStd(), 0);
		input.Depol_loss = 0;

		input.Sea_temperature[0] = 'W';
		input.PointDistance = 0.1;
		input.useAnnex1 = tabResCbr.ChBAnnex1Cbr;

		strncpy(input.Topo_path, ~cnfg.PathToTopo, 255);
		input.Topo_path[255] = '\0';
		
		strncpy(input.Morpho_path, ~cnfg.PathToMorpho, 255);
		input.Morpho_path[255] = '\0';
		
		strncpy(input.Border_path, ~cnfg.PathToBorders, 255);
		input.Border_path[255] = '\0';
		input.MaxCalcDistanceKm = Upp::max(Upp::min(cnfg.MaxCalcDistance,AcceptedMaxCalculateDistanceKm),AcceptedMinCalculateDistanceKm);


		if (StationsList.GetCount() < 1) { ErrorOK("There are no stations"); return; };

		int count = 0;
		for (int i = 0; i < StationsList.GetCount(); i++) if (!StationsList[i].isRefList) count++;
		int index = 1;

		prg.SetTotal(count);
		for (int j = 0; j < StationsList.GetCount(); j++) {
			if (StationsList[j].isRefList) continue;
			prg.Step();

			// If Got Tx
			if (ExtractTxFromFile(StationsList[j].Filename, input, stationName1, StationsList[j].IndexInFile)){
				GetDebugFileName();

				try {
					pCbrCalc(input, output);

					(input.useAnnex1) ? infoString = "Annex1 used. " : infoString = "";
					infoString = infoString + AsString(output.HCM_error) + " " + getErrorMessageI(output.HCM_error);

						tabResCbr.table.Add(index, stationName1, input.Land_from, input.Tx_H_Ant, input.Tx_Azi,
							input.Tx_Power, input.Tx_Serv_area, input.Tx_Freq,
							stationName2, input.Land_to, input.Rx_H_Ant, Format("%.2mf", output.Dir_Rx_Tx), input.Rx_Serv_area, input.Rx_Freq, "", AsString(input.D_to_border),
							Format("%.2mf", output.Distance),
							Format("%.1mf", output.Dir_Tx_Rx),
							Format("%.2mf", output.Calculated_FS),
							Format("%.2mf", output.Perm_FS),
							Format("%.2mf", output.Prot_margin),
							infoString,
							StationsList[j].Filename,
							StationsList[j].IndexInFile
						);

						if (output.HCM_error == 0){
							lop.Name = stationName1 + "-> CBR line";
							lop.LineColor = LtBlue();
							lop.Points.SetCount(2);
							lop.Points[0].x = output.Tx_Longitude_Calc;
							lop.Points[0].y = output.Tx_Latitude_Calc;
							lop.Points[1].x = output.Rx_Longitude_Calc;
							lop.Points[1].y = output.Rx_Latitude_Calc;
							LinesOnMap.Add(pick(lop));
							flagCbrCalcDone = true;
						}
				} catch (...) {
						tabResCbr.table.Add(index, stationName1, input.Land_from, input.Tx_H_Ant, input.Tx_Azi,
							input.Tx_Power, input.Tx_Serv_area, input.Tx_Freq,
							stationName2, input.Land_to, input.Rx_H_Ant, Format("%.2mf", output.Dir_Rx_Tx), input.Rx_Serv_area, input.Rx_Freq, "", AsString(input.D_to_border),
							Format("%.2mf", output.Distance),
							Format("%.1mf", output.Dir_Tx_Rx),
							Format("%.2mf", output.Calculated_FS),
							Format("%.2mf", output.Perm_FS),
							Format("%.2mf", output.Prot_margin),
							"ERROR IN DLL", "", ""
						);
				}
				index++;
			}
			// If Equivalent Checked
			if (tabResCbr.ChBAddEqTx) {
				if (ExtractRxFromFile(StationsList[j].Filename, inputEq, stationName1, StationsList[j].IndexInFile)){
					GetDebugFileName();

					input.Tx_Longitude = inputEq.Rx_Longitude;
					input.Tx_Latitude = inputEq.Rx_Latitude;
					
					input.Tx_Freq = inputEq.Rx_Freq;
					
					input.Chan_occup = 0;
					if (inputEq.Rx_Type_of_Ant == 0){//Rx_Type_of_Ant [0,1]=[E/I]
						input.Tx_Power = 0 + inputEq.Rx_Ant_gain;
					} else {
						input.Tx_Power = 0 + inputEq.Rx_Ant_gain - 2.15;
					}
					input.Tx_Type_of_Ant = 0; // ALWAYS E in this mode
					input.Rx_Ant_gain = 0;
	
					input.Tx_H_Site_Ant = inputEq.Rx_H_Site_Ant;
	
					std::strcpy(input.Tx_Ant_typ_H, inputEq.Rx_Ant_typ_H);
					std::strcpy(input.Tx_Ant_typ_V, inputEq.Rx_Ant_typ_V);
	
					input.Tx_Azi = inputEq.Rx_Azi;
	
					input.Tx_Elev = inputEq.Rx_Elev;
	
					input.Tx_H_Ant = inputEq.Rx_H_Ant;
	
					input.Tx_Serv_area = inputEq.Rx_Serv_area;
	
					std::strcpy(input.Tx_Desig_of_emis, inputEq.Rx_Desig_of_emis);
				
					try {
						pCbrCalc(input, output);
				
						(input.useAnnex1) ? infoString = "Equivalent Tx. Annex1 used. " : infoString = "Equivalent Tx. ";
						infoString = infoString + AsString(output.HCM_error) + " " + getErrorMessageI(output.HCM_error);

							tabResCbr.table.Add(index, stationName1, input.Land_from, input.Tx_H_Ant, input.Tx_Azi,
								input.Tx_Power, input.Tx_Serv_area, input.Tx_Freq,
								stationName2, input.Land_to, input.Rx_H_Ant, Format("%.2mf", output.Dir_Rx_Tx), input.Rx_Serv_area, input.Rx_Freq, "", AsString(input.D_to_border),
								Format("%.2mf", output.Distance),
								Format("%.1mf", output.Dir_Tx_Rx),
								Format("%.2mf", output.Calculated_FS),
								Format("%.2mf", output.Perm_FS),
								Format("%.2mf", output.Prot_margin),
								infoString,
								StationsList[j].Filename,
								StationsList[j].IndexInFile
							);

							if (output.HCM_error == 0){
								lop.Name = stationName1 + "-> CBR line";
								lop.LineColor = LtBlue();
								lop.Points.SetCount(2);
								lop.Points[0].x = output.Tx_Longitude_Calc;
								lop.Points[0].y = output.Tx_Latitude_Calc;
								lop.Points[1].x = output.Rx_Longitude_Calc;
								lop.Points[1].y = output.Rx_Latitude_Calc;
								LinesOnMap.Add(pick(lop));
								flagCbrCalcDone = true;
							}
					} catch (...) {
							tabResCbr.table.Add(index, stationName1, input.Land_from, input.Tx_H_Ant, input.Tx_Azi,
								input.Tx_Power, input.Tx_Serv_area, input.Tx_Freq,
								stationName2, input.Land_to, input.Rx_H_Ant, Format("%.2mf", output.Dir_Rx_Tx), input.Rx_Serv_area, input.Rx_Freq, "", AsString(input.D_to_border),
								Format("%.2mf", output.Distance),
								Format("%.1mf", output.Dir_Tx_Rx),
								Format("%.2mf", output.Calculated_FS),
								Format("%.2mf", output.Perm_FS),
								Format("%.2mf", output.Prot_margin),
								"ERROR IN DLL", "", ""
							);
					}
					index++;
				}

			}


		}

	~prg;
	RefreshMapTree();
	UpdateImageResultsCombineLayout();

}

#endif
