#ifndef _Common_h_
#define _Common_h_

#include <iomanip>
#include <iostream>
#include <sstream>
#include "Excel.h"

/**
 * Ensures a directory path ends with the platform-specific directory separator.
 * If the path already ends with a separator, returns it unchanged.
 * 
 * @param path The directory path to process
 * @return Path with trailing directory separator
 */
String PutSepToEndPath(String path) {
	if (path.GetCount() < 2) return path;

	if (path[path.GetCount()] == DIR_SEP) return path;
	return path + DIR_SEP;
}

/**
 * Pads a string with spaces to achieve specified length.
 * Can pad on left or right side based on alignment parameter.
 * 
 * @param source The original string to pad
 * @param len The target length for the padded string
 * @param isRight Alignment: true for right-padding, false for left-padding
 * @return Padded string with specified length
 */
String MakeStringBlank(String source, int len, bool isRigth = false) {
	String ss = source;
	if (source.GetCount() < len) {
		for (int i = 0; i < (len - source.GetCount()); i++) {
			isRigth ? ss = ss + " " : ss = " " + ss;
		}
	}
	return ss;
}

/**
 * Loads text file content into a dropdown list control.
 * Each line becomes a separate item in the list.
 * 
 * @param filePath Path to the text file to load
 * @param dropList Reference to the DropList control to populate
 */
void LoadFileToDropList(const String& filePath, DropList& dropList) {
	FileIn file(filePath);
	if (!file) {
		PromptOK("Failed to open file: " + filePath);
		file.Close();
		return;
	}
	dropList.Clear();
	String line;
	while (!file.IsEof()) {
		line = TrimBoth(file.GetLine());
		dropList.Add(line);
	}
	file.Close();
}

/**
 * Converts coordinate string (DDMMSS format) to geographic Pointf structure.
 * Supports N/S/E/W directions for latitude/longitude.
 * 
 * @param source Coordinate string in "DDDMMSSXDDMMSSY" format
 * @return Pointf with x=longitude, y=latitude in decimal degrees
 */
Pointf ReadCoordinate(String source) {
	Pointf pnt;
	try {
		String tmp;
		tmp = source.Mid(0, 3);
		pnt.x = ScanDouble(tmp);
		tmp = source.Mid(4, 2);
		pnt.x += ScanDouble(tmp) / 60;
		tmp = source.Mid(6, 2);
		pnt.x += ScanDouble(tmp) / 3600;
		if (ToUpper(source.Mid(3, 1)) == "W") pnt.x = pnt.x * -1;
	} catch (...) {
		return Pointf(0, 0);
	}
	try {
		String tmp;
		tmp = source.Mid(8, 2);
		pnt.y = ScanDouble(tmp);
		tmp = source.Mid(11, 2);
		pnt.y += ScanDouble(tmp) / 60;
		tmp = source.Mid(13, 2);
		pnt.y += ScanDouble(tmp) / 3600;
		if (ToUpper(source.Mid(10, 1)) == "S") pnt.y = pnt.y * -1;
	} catch (...) {
		return Pointf(0, 0);
	}
	return pnt;
}

/**
 * Converts geographic coordinates to formatted DDMMSS string representation.
 * Handles degree/minute/second conversion and direction indicators.
 * 
 * @param point Geographic coordinates with x=longitude, y=latitude
 * @return Formatted coordinate string (15 characters)
 */
String CoordinateToString(Pointf point) {
	std::string coo = "               ";
	double lo = point.x;
	double la = point.y;

	if (lo >= 0.0) {
		coo[3] = 'E';
	} else {
		coo[3] = 'W';
		lo = -lo;
	}

	if (la >= 0.0) {
		coo[10] = 'N';
	} else {
		coo[10] = 'S';
		la = -la;
	}

	int i = static_cast<int>(lo);
	lo = (lo - i) * 60.0;
	int j = static_cast<int>(lo);
	lo = (lo - j) * 60.0;
	int k = std::round(lo);
	if (k >= 60) {
		k -= 60;
		j += 1;
		if (j >= 60) {
			j -= 60;
			i += 1;
		}
	}

	// Write the longitude part
	std::ostringstream oss;
	oss << std::setw(3) << std::setfill('0') << i;
	coo.replace(0, 3, oss.str());
	oss.str("");
	oss.clear();
	oss << std::setw(2) << std::setfill('0') << j;
	coo.replace(4, 2, oss.str());
	oss.str("");
	oss.clear();
	oss << std::setw(2) << std::setfill('0') << k;
	coo.replace(6, 2, oss.str());

	i = static_cast<int>(la);
	la = (la - i) * 60.0;
	j = static_cast<int>(la);
	la = (la - j) * 60.0;
	k = std::round(la);
	if (k >= 60) {
		k -= 60;
		j += 1;
		if (j >= 60) {
			j -= 60;
			i += 1;
		}
	}

	// Write the latitude part
	oss.str("");
	oss.clear();
	oss << std::setw(2) << std::setfill('0') << i;
	coo.replace(8, 2, oss.str());
	oss.str("");
	oss.clear();
	oss << std::setw(2) << std::setfill('0') << j;
	coo.replace(11, 2, oss.str());
	oss.str("");
	oss.clear();
	oss << std::setw(2) << std::setfill('0') << k;
	coo.replace(13, 2, oss.str());

	return coo;
}

/**
 * Parses frequency string with optional unit suffixes (K, M, G).
 * Converts to MHz equivalent for internal processing.
 * 
 * @param source Frequency string with optional unit suffix
 * @return Frequency value in MHz
 */
double ReadFreq(String source) {
	try {
		double koef = 1.0;
		String tmp = ToUpper(source);
		if (tmp.Find('M') >= 0) tmp.Replace("M", "");
		if (tmp.Find('K') >= 0) {
			tmp.Replace("K", "");
			koef = 0.001;
		}
		if (tmp.Find('G') >= 0) {
			tmp.Replace("G", "");
			koef = 1000;
		}
		tmp.Replace(" ", "");
		if (tmp.GetLength() < 1) return 0;

		return ScanDouble(tmp) * koef;
	} catch (...) {
		return 0;
	}
}

/**
 * Formats frequency value as standardized string with fixed width.
 * Handles empty values and error cases gracefully.
 * 
 * @param txt Frequency value as string
 * @return Formatted frequency string (11 characters wide)
 */
String SetFreqString(String txt) {
	txt = TrimBoth(txt);
	if (txt.GetLength() == 0) return MakeStringBlank("", 11);
	if (txt == "-") return MakeStringBlank("", 11);
	try {
		double dd = std::stod(txt.ToStd());
		return MakeStringBlank(Format("%.5mf", dd), 11);
	} catch (...) {
		return MakeStringBlank("", 11);
	}
}

/**
 * Formats maximum power value with sign indicator and fixed width.
 * Preserves negative/positive sign and decimal precision.
 * 
 * @param txt Power value as string
 * @return Formatted power string with sign (6 characters wide)
 */
String SetMaxPowerString(String txt) {
	txt = TrimBoth(txt);
	if (txt.GetLength() == 0) return MakeStringBlank("", 6);
	if (txt == "-") return MakeStringBlank("", 6);
	try {
		double dd = std::stod(txt.ToStd());
		bool isNeg = dd<0;
		dd = std::abs(dd);
		txt = MakeStringBlank(Format("%.1mf", dd).Mid(0, 6), 5);
		isNeg ? txt = "-" + txt : txt = "+" + txt;
		return txt;
	} catch (...) {
		return MakeStringBlank("", 6);
	}
}

/**
 * Formats azimuth angle value with fixed width and decimal precision.
 * Handles empty values and formatting errors.
 * 
 * @param txt Azimuth value as string
 * @return Formatted azimuth string (5 characters wide)
 */
String SetAzimutString(String txt) {
	txt = TrimBoth(txt);
	if (txt.GetLength() == 0) return MakeStringBlank("", 5);
	if (txt == "-") return MakeStringBlank("", 5);
	try {
		double dd = std::stod(txt.ToStd());
		return MakeStringBlank(Format("%.2mf", dd).Mid(0, 5), 5);
	} catch (...) {
		return MakeStringBlank("", 5);
	}
}

/**
 * Formats elevation angle with sign handling and fixed width.
 * Negative values indicate below horizon.
 * 
 * @param txt Elevation value as string
 * @return Formatted elevation string with sign (5 characters wide)
 */
String SetElevationString(String txt) {
	txt = TrimBoth(txt);
	if (txt.GetLength() == 0) return MakeStringBlank("", 5);
	if (txt == "-") return MakeStringBlank("", 5);
	try {
		double dd = std::stod(txt.ToStd());
		txt = MakeStringBlank(Format("%.1mf", abs(dd)),5);
		if (dd < 0) txt = "-" + txt.Mid(1,4);
		return txt;
	} catch (...) {
		return MakeStringBlank("", 5);
	}
}

/**
 * Calculates number of station records in fixed-length format file.
 * Validates file structure based on 219-byte record size.
 * 
 * @param filename Path to the station data file
 * @return Number of station records, 0 if invalid format
 */
int GetCountStationsInFile(String filename) {
	if (!FileExists(filename)) return 0;
	FileIn f(filename);
	int fs = f.GetSize();
	f.Close();
	if ((fs % 219) != 0) return 0;
	return trunc(fs / 219) - 1;
}

/**
 * Reads specific station record from fixed-length format file by index.
 * Validates index bounds and file structure before reading.
 * 
 * @param filename Path to the station data file
 * @param index Zero-based index of station record to read
 * @return Station record as 219-character string, empty if error
 */
String ReadStationFromFile(String filename, int index) {
	String result = "";

	int cnt = GetCountStationsInFile(filename);
	if (cnt == 0) return result;
	if (index > (cnt - 1)) return result;

	FileIn f(filename);
	result = f.GetLine();
	f.Close();

	result = result.Mid(219 + 219 * index, 219);
	return result;
}

/**
 * Duplicates the last station record in the file by appending copy.
 * Useful for creating new stations based on existing templates.
 * 
 * @param filename Path to the station data file to modify
 */
void DuplicateLastStationToFile(String filename) {
	int cnt = GetCountStationsInFile(filename);
	if (cnt == 0) return;

	FileAppend f;
	f.Open(filename);
	f.Seek(0);
	String result = f.GetLine();
	f.Seek(f.GetSize());
	result = result.Mid(cnt * 219, 219);
	f.Put(result);
	f.Close();
}

/**
 * Modifies station name at specified index in the data file.
 * Preserves all other station data while updating name field.
 * 
 * @param filename Path to the station data file
 * @param index Zero-based index of station to rename
 * @param newName New name for the station
 */
void ChangeNameToStation(String filename, int index, String newName) {
	int cnt = GetCountStationsInFile(filename);
	if (cnt == 0) return;
	if (index > cnt) return;

	FileAppend f;
	f.Open(filename);
	f.Seek(0);
	String result = f.GetLine();

	String name = result.Mid((index) * 219 + 28, 20);
	String part1 = result.Mid(0, (index) * 219 + 28);
	String part2 = result.Mid((index) * 219 + 28 + 20, result.GetCount() - (index) * 219 + 28);
	newName = MakeStringBlank(newName + "_" + TrimBoth(name), 20).Mid(0, 20);
	result = part1 + newName + part2;

	f.Seek(0);
	f.Put(result);
	f.Close();
}


enum class FieldName {
	f13Z,
	f13Y
};

/**
 * Updates specific field in station record at given index.
 * Supports multiple field types with different positions and lengths.
 * 
 * @param fname Field identifier to update
 * @param filename Path to the station data file
 * @param index Zero-based station index to modify
 * @param value New value for the field
 */
void UpdateFeild(FieldName fname, const String& filename, const int& index, String Value ) {


	if (!FileExists(filename)) return;
	FileIn f(filename);
	int fs = f.GetSize();
	if ( ((fs % 219) != 0) && (fs > 219)) {
		f.Close();
		return;
	}
	f.Seek(0);
	String allString = f.GetLine();
	f.Close();

	fs = GetCountStationsInFile(filename);
	if (index>=fs){
		PromptOK("There is no such Station index = " + AsString(index) + " in file " + filename );
		return;

	}


	int lenOfFiled = 0;
	int positionOfFiled = 0;
	//  13Z len=50 offset=137
	//  13Y len=1 offset=187
	switch(fname) {
		case FieldName::f13Z:
			lenOfFiled = 50;
			positionOfFiled = 137;
			break;
		case FieldName::f13Y:
			lenOfFiled =
			positionOfFiled = 187;
			break;
		default:
			exit;
			break;
	}

	int offset = 219 * ( index + 1 ) + positionOfFiled;

	String st = allString.Left(offset);
	String md = allString.Mid(offset, lenOfFiled);
	md = MakeStringBlank(Value, lenOfFiled, true);
	String en = allString.Mid(offset + lenOfFiled, allString.GetLength());
	allString = st + md + en;

	FileOut fo(filename);
	fo.Seek(0);
	fo.Put(allString);
	fo.Close();
}

/**
 * Saves tabular data to JSON format file with user-selected location.
 * Creates array of objects with header fields as keys.
 * 
 * @param data 2D vector containing table data rows
 * @param header Vector containing column header names
 */
void SaveToJson(const Vector<Vector<String>>& data, const Vector<String>& header) {
	FileSel fs;
	fs.ReadOnlyOption(true);
	fs.MkDirOption(false);
	fs.ActiveDir(GetExeFolder());
	fs.EditFileName(true);
	fs.Types("Json files\t*.json");

	if (!fs.ExecuteSaveAs()) return;

	String tmpAll, tmpS;
	tmpAll = "";

	for (int i = 0; i < data.GetCount(); ++i) {
		tmpS = "";
		for (int j = 0; j < header.GetCount(); ++j) {
			tmpS += "\"" + header[j] + "\":\"" + data[i][j] +"\"," ;
		}
		tmpAll += "{" + tmpS.Mid(0,tmpS.GetCount()-1) + "},";
	}
	tmpAll = "[" + tmpAll.Mid(0,tmpAll.GetCount()-1) + "]";

	try {
		FileOut file(fs.GetFile(0));
		file << tmpAll;
	} catch (...) {
	}
}

/**
 * Exports ArrayCtrl contents to JSON file format.
 * Preserves column headers and all visible data rows.
 * 
 * @param table Reference to the ArrayCtrl containing data
 * @param countColumsIn Number of columns to export (0 for all)
 */

void SaveTableToJson( Upp::ArrayCtrl& table, int countColumsIn = 0 ){
	Vector<Vector<String>> data;
	Vector<String> header;
	int countColums = 0;
	countColumsIn > 0 ? countColums = countColumsIn : countColums = table.GetColumnCount();

	header.SetCount(countColumsIn);
	for (int j=0; j<header.GetCount(); ++j){
		header[j] = table.HeaderTab(j).GetText();
	}

	data.SetCount(table.GetCount());
	for (int i=0; i<table.GetCount(); ++i){
		data[i].SetCount( countColumsIn);
		for (int j=0; j<countColumsIn; ++j){
			data[i][j] = AsString(table.Get(i,j));
		}
	}
	SaveToJson(data, header);
}

/**
 * Exports ArrayCtrl contents to Excel file format.
 * Preserves column widths and formatting from table display.
 * 
 * @param table Reference to the ArrayCtrl containing data
 * @param countColumsIn Number of columns to export (0 for all)
 */
void SaveTableToExcel( Upp::ArrayCtrl& table, int countColumsIn = 0 ){
	Vector<Vector<String>> data;
	Vector<String> header;
	std::vector<int> columnWidths;

	int countColums = 0;
	countColumsIn > 0 ? countColums = countColumsIn : countColums = table.GetColumnCount();

	columnWidths.resize(countColums);
	for (int j=0; j<countColums; ++j){
		columnWidths[j] = table.GetScreenCellRect(0,j).Width();
	};


	header.SetCount(countColums);
	for (int j=0; j<header.GetCount(); ++j){
		header[j] = table.HeaderTab(j).GetText();
	}

	data.SetCount(table.GetCount());
	for (int i=0; i<table.GetCount(); ++i){
		data[i].SetCount(countColums);
		for (int j=0; j<countColums; ++j){
			data[i][j] = AsString(table.Get(i,j));
		}
	}
	SaveToXLS(data, header);
}

/**
 * Duplicates specific station record identified by index.
 * Appends copy of the station to end of file.
 * 
 * @param filename Path to the station data file
 * @param index Zero-based index of station to duplicate
 */
void DuplicateIndexStationToFile(String filename, int index) {
	int cnt = GetCountStationsInFile(filename);
	if (cnt == 0) return;
	if (index >= cnt) return;

	FileAppend f;
	f.Open(filename);
	f.Seek(0);
	String result = f.GetLine();
	f.Seek(f.GetSize());
	result = result.Mid((index + 1) * 219, 219);
	f.Put(result);
	f.Close();
}

/**
 * Removes station record at specified index from data file.
 * Reconstructs file without the deleted station record.
 * 
 * @param filename Path to the station data file
 * @param index Zero-based index of station to delete
 */
void DeleteStationFromFile(String filename, int index) {
	int count = GetCountStationsInFile(filename);
	if (count == 0) return;
	if (index >= count) return;
	if ((index == 0) & (count == 1)) return;

	FileIn f(filename);
	f.Seek(0);
	String context = f.GetLine();
	f.Close();

	if (index + 1 == count) {
		context = context.Mid(0, 219 * (index + 1));
	} else {
		context = context.Mid(0, 219 * (index + 1)) + context.Mid(219 * (index + 2), 219 * (count - index - 1));
	}

	FileOut fo(filename);
	fo.Seek(0);
	fo.Put(context);
	fo.Close();
}

/**
 * Adds new empty station record with default template to file.
 * Creates file if it doesn't exist, validates structure.
 * 
 * @param filename Path to the station data file
 */
void AddEmptyStationToFile(String filename) {
	if (!FileExists(filename)) {
		FileOut fo(filename);
		fo.Close();
	}
	FileAppend f(filename);
	int fs = f.GetSize();
	f.Seek(fs);
	if (fs == 0){
		std::string dynamicString(219, ' ');
		f.Put(dynamicString);
	} else {
		if ((fs % 219) != 0) {
			f.Close();
			return;
		}
	}
	String newStation;// = "  598.00000M2FLOT L014032025 Change this name 01D__008E111753N0716    6      91K9G2W27.540I124.0 -8.0 M      40006KA14002EB29           M                                                  B14022025        D__250001010111";
	newStation = " 1000.00000M2FLOT L001012025         New StationETH 00E000000N0000    0              0.000I 0.00  0.0 M       0                         M                                                  B01012025        ETH            ";
	f.Put(newStation);
	fs = f.GetSize();
	f.Close();
}

/**
 * Merges station records from source file into destination file.
 * Appends all stations from addingFile to mainFile.
 * 
 * @param mainFile Destination file to receive stations
 * @param addingFile Source file containing stations to merge
 */
void MergeFilesWithStations(String mainFile, String addingFile) {
	int cnt = GetCountStationsInFile(addingFile);
	if (cnt == 0) return;

	String st = "";
	if (!FileExists(mainFile)) {
		for (int i = 0; i < 219; i++) st += " ";
		FileOut fi;
		fi.Open(mainFile);
		fi.Put(st);
		fi.Close();
	}

	FileAppend f;
	f.Open(mainFile);
	int fs = f.GetSize();
	if ((fs % 219) != 0) {
		f.Close();
		return;
	}
	f.Seek(fs);

	for (int i = 0; i < cnt; i++) {
		st = ReadStationFromFile(addingFile, i);
		f.Put(st);
	}
	f.Close();
}











struct FieldPos {
	int start;
	int length;
};

struct HeaderRecord : Moveable<HeaderRecord> {
	String H01; // File number on media
	String H02; // File contents
	String H03; // File contents code
	String H04; // Originating country
	String H05; // E-mail address
	String H06; // Phone
	String H07; // Telefax
	String H08; // Name of responsible person
	String H09; // Number of records
	String H10; // Writing date
	String H11; // Destination country
	String H12; // Unique file number
	String H13; // File version
	String H14; // Reserved for future use

	void Clear() {
		H01 = H02 = H03 = H04 = H05 = H06 = H07 = H08 = H09 = H10 =
		H11 = H12 = H13 = H14 = Null;
	}

	void Jsonize(JsonIO& json) {
		json
			("H01", H01)
			("H02", H02)
			("H03", H03)
			("H04", H04)
			("H05", H05)
			("H06", H06)
			("H07", H07)
			("H08", H08)
			("H09", H09)
			("H10", H10)
			("H11", H11)
			("H12", H12)
			("H13", H13)
			("H14", H14);
	}

	String ToJsonStr() const { return StoreAsJson(*this); }
	void FromJsonStr(const String& s) { LoadFromJson(*this, s); }
};


struct StationRecord : Moveable<StationRecord> {
	String _1A;  // Transmitting frequency Frequency unit
	String _1Z;  // Frequency category
	String _6A;  // Class of station
	String _6B;  // Nature of service
	String _6Z;  // Category of use
	String _10Z; // Channel occupation
	String _2C;  // Date of bringing into use
	String _4A;  // Name of station
	String _4B;  // Country where the station is located
	String _4C;  // Geographical coordinates
	String _4D;  // Radius of the operating area
	String _4Z;  // Height of the station site above sea level
	String _7A;  // Designation of emission
	String _8B1; // Maximal radiated power of the station
	String _8B2; // Type of reference antenna
	String _9A;  // Azimuth of maximum radiation
	String _9B;  // Mechanical elevation angle of the antenna
	String _9D;  // Polarization
	String _9G;  // Gain of the antenna
	String _9Y;  // Height of antenna above ground
	String _9XH; // Type of antenna horizontal
	String _9XV; // Type of antenna vertical
	String _1Y;  // Transmitting frequency of the corresponding receiving station
	String _13Z; // Remarks
	String _2W;  // Date of co-ordination request
	String _2Z;  // Final date of achieving coordination
	String _13Y; // Status of coordination
	String _13X; // Coordination reference

	void Clear() {
		_1A = _1Z = _2W = _2Z = _6A = _6B = _6Z = _10Z = _2C = _4A = _4B = _4C = _4D = _4Z =
		_7A = _8B1 = _8B2 = _9A = _9B = _9D = _9G = _9Y = _9XH = _9XV = _1Y = _13Z = _13Y = _13X = Null;
	}

	void Jsonize(JsonIO& io) {
		io
			("1A", _1A)
			("1Z", _1Z)
			("6A", _6A)
			("6B", _6B)
			("6Z", _6Z)
			("10Z", _10Z)
			("2C", _2C)
			("4A", _4A)
			("4B", _4B)
			("4C", _4C)
			("4D", _4D)
			("4Z", _4Z)
			("7A", _7A)
			("8B1", _8B1)
			("8B2", _8B2)
			("9A", _9A)
			("9B", _9B)
			("9D", _9D)
			("9G", _9G)
			("9Y", _9Y)
			("9XH", _9XH)
			("9XV", _9XV)
			("1Y", _1Y)
			("13Z", _13Z)
			("13Y", _13Y)
			("2W", _2W)
			("2Z", _2Z)
			("13X", _13X);
	}

	String ToJsonStr() const { return StoreAsJson(*this); }
	void FromJsonStr(const String& s) { LoadFromJson(*this, s); }
};


struct FileDataJs : Moveable<FileDataJs> {
	HeaderRecord header;
	Vector<StationRecord> stations;

	void Clear() {
		header.Clear();
		stations.Clear();
	}

	void Jsonize(JsonIO& io) {
		io
			("header", header)
			("stations", stations)
			;
	}

	String ToJsonStr() const { return StoreAsJson(*this); }
	void FromJsonStr(const String& s) { LoadFromJson(*this, s); }
};




/**
 * Parses fixed-length format station file into structured data object.
 * Validates file structure and extracts header + station records.
 * 
 * @param filePath Path to the fixed-length format station file
 * @param outData Output parameter receiving parsed file data
 * @return True if parsing successful, false on error
 */
bool ParseFixedLengthFile(const String& filePath, FileDataJs& outData) {
	if (!FileExists(filePath)) {
		Cout() << "Файл не найден: " << filePath << "\n";
		return false;
	}

	FileIn in(filePath);
	if (!in) {
		Cout() << "Не удалось открыть файл: " << filePath << "\n";
		return false;
	}

	int fileSize = in.GetSize();
	if (fileSize % 219 != 0 || fileSize < 219) {
		Cout() << "Некорректный размер файла. Должен быть кратен 219 байт.\n";
		return false;
	}

	String fileData = in.GetLine();
	in.Close();

	outData.Clear();


	String headerStr = fileData.Mid(0, 219);

	outData.header.H01 = headerStr.Mid(0, 2);
	outData.header.H02 = headerStr.Mid(2, 80);
	outData.header.H03 = headerStr.Mid(82, 1);
	outData.header.H04 = headerStr.Mid(83, 3);
	outData.header.H05 = headerStr.Mid(86, 40);
	outData.header.H06 = headerStr.Mid(126, 20);
	outData.header.H07 = headerStr.Mid(146, 20);
	outData.header.H08 = headerStr.Mid(166, 20);
	outData.header.H09 = headerStr.Mid(186, 6);
	outData.header.H10 = headerStr.Mid(192, 8);
	outData.header.H11 = headerStr.Mid(200, 3);
	outData.header.H12 = headerStr.Mid(203, 6);
	outData.header.H13 = headerStr.Mid(209, 3);
	outData.header.H14 = headerStr.Mid(212, 7);


	int totalStations = (fileSize / 219) - 1;

	for (int i = 0; i < totalStations; ++i) {
		StationRecord rec;
		int offset = 219 + i * 219;
		String stationStr = fileData.Mid(offset, 219);

		rec._1A  = stationStr.Mid(0, 12);   // 1-11
		rec._1Z  = stationStr.Mid(12, 1);   // 13
		rec._6A  = stationStr.Mid(13, 2);   // 14-15
		rec._6B  = stationStr.Mid(15, 2);   // 16-17
		rec._6Z  = stationStr.Mid(17, 2);   // 18-19
		rec._10Z = stationStr.Mid(19, 1);   // 20
		rec._2C  = stationStr.Mid(20, 8);   // 21-28
		rec._4A  = stationStr.Mid(28, 20);  // 29-48
		rec._4B  = stationStr.Mid(48, 3);   // 49-51
		rec._4C  = stationStr.Mid(51, 15);  // 52-66
		rec._4D  = stationStr.Mid(66, 5);   // 67-71
		rec._4Z  = stationStr.Mid(71, 4);   // 72-75
		rec._7A  = stationStr.Mid(75, 9);   // 76-84
		rec._8B1 = stationStr.Mid(84, 6);   // 85-90
		rec._8B2 = stationStr.Mid(90, 1);   // 91
		rec._9A  = stationStr.Mid(91, 5);   // 92-96
		rec._9B  = stationStr.Mid(96, 5);   // 97-101
		rec._9D  = stationStr.Mid(101, 2);   // 102-103
		rec._9G  = stationStr.Mid(103, 4);  // 104-107
		rec._9Y  = stationStr.Mid(107, 4);  // 108-111
		rec._9XH = stationStr.Mid(111, 7);  // 112-118
		rec._9XV = stationStr.Mid(118, 7);  // 119-125
		rec._1Y  = stationStr.Mid(125, 12); // 126-137
		rec._13Z = stationStr.Mid(137, 50); // 138-187
		rec._13Y = stationStr.Mid(186, 1);  // 188
		rec._2W = stationStr.Mid(188, 8);  // 189-196
		rec._2Z = stationStr.Mid(196, 8);  // 197-204
		rec._13X = stationStr.Mid(204, 15); // 205-219

		outData.stations.Add(rec);
	}
	return true;
}

/**
 * Converts structured station data to JSON file format.
 * Creates JSON representation of header and all station records.
 * 
 * @param outputPath Destination path for JSON file
 * @param data Structured station data to convert
 * @return True if conversion successful, false on error
 */
bool ConvertStationFileToJSON(const String& outputPath, FileDataJs& data) {
	FileOut out(outputPath);
	if (!out) {
		Cout() << "Error cant open file to write: " << outputPath << "\n";
		return false;
	}

	out << data.ToJsonStr();
	out.Close();

	Cout() << "JSON saved to: " << outputPath << "\n";
	return true;
}

/**
 * Converts fixed-length station file directly to JSON format.
 * Combines parsing and JSON conversion in single operation.
 * 
 * @param inputPath Source fixed-length station file path
 * @param outputPath Destination JSON file path
 * @return True if conversion successful, false on error
 */
bool ConvertStationFileToJSON(const String& inputPath, const String& outputPath) {
	FileDataJs data;
	if (!ParseFixedLengthFile(inputPath, data))
		return false;

	return ConvertStationFileToJSON(outputPath, data);
}

/**
 * Converts structured station data back to fixed-length format file.
 * Handles field length padding and format validation.
 * 
 * @param outputPath Destination path for fixed-length station file
 * @param data Structured station data to convert
 * @return True if conversion successful, false on error
 */
bool ConvertJSONToStationFile(const String& outputPath, FileDataJs& data) {
	const auto SetLen = [](const String& s, int len) -> String {
		String res = s;
		if (res.GetLength() > len)
			return res.Left(len);
		else
			return res + String(' ', len - res.GetLength());
	};


	FileOut outFile(outputPath);
	if (!outFile) {
		PromptOK("Error cant open file to write: " << outputPath << "\n");
		return false;
	}

	String headerStr =
		SetLen(data.header.H01, 2) +
		SetLen(data.header.H02, 80) +
		SetLen(data.header.H03, 1) +
		SetLen(data.header.H04, 3) +
		SetLen(data.header.H05, 40) +
		SetLen(data.header.H06, 20) +
		SetLen(data.header.H07, 20) +
		SetLen(data.header.H08, 20) +
		SetLen(data.header.H09, 6) +
		SetLen(data.header.H10, 8) +
		SetLen(data.header.H11, 3) +
		SetLen(data.header.H12, 6) +
		SetLen(data.header.H13, 3) +
		SetLen(data.header.H14, 7);

	if (headerStr.GetLength() != 219) {
		PromptOK("Error header length it must be more 219 bytes. Now: " << AsString(headerStr.GetLength()) << "\n");
		return false;
	}

	outFile.Put(headerStr);

	for (const StationRecord& station : data.stations) {
		String stationStr =
			SetLen(station._1A, 12)   +
			SetLen(station._1Z, 1)    +
			SetLen(station._6A, 2)    +
			SetLen(station._6B, 2)    +
			SetLen(station._6Z, 2)    +
			SetLen(station._10Z, 1)   +
			SetLen(station._2C, 8)    +
			SetLen(station._4A, 20)   +
			SetLen(station._4B, 3)    +
			SetLen(station._4C, 15)   +
			SetLen(station._4D, 5)    +
			SetLen(station._4Z, 4)    +
			SetLen(station._7A, 9)    +
			SetLen(station._8B1, 6)   +
			SetLen(station._8B2, 1)   +
			SetLen(station._9A, 5)    +
			SetLen(station._9B, 5)    +
			SetLen(station._9D, 2)    +
			SetLen(station._9G, 4)    +
			SetLen(station._9Y, 4)    +
			SetLen(station._9XH, 7)   +
			SetLen(station._9XV, 7)   +
			SetLen(station._1Y, 12)   +
			SetLen(station._13Z, 50)  +
			SetLen(station._13Y, 1)   +
			SetLen(station._2W, 8)   +
			SetLen(station._2Z, 8)   +
			SetLen(station._13X, 15);

		if (stationStr.GetLength() != 219) {
			PromptOK("Wrong Station data length (219 symbols");
			return false;
		}

		outFile.Put(stationStr);
	}

	outFile.Close();
	return true;
}

/**
 * Converts JSON station file back to fixed-length format.
 * Parses JSON and reconstructs original binary format.
 * 
 * @param jsonPath Source JSON file path
 * @param outputPath Destination fixed-length station file path
 * @return True if conversion successful, false on error
 */
bool ConvertJSONToStationFile(const String& jsonPath, const String& outputPath) {
	if (!FileExists(jsonPath)) {
		Cout() << "File JSON not found: " << jsonPath << "\n";
		return false;
	}


	FileIn jsonFile(jsonPath);
	String jsonData = jsonFile.GetLine();
	jsonFile.Close();

	FileDataJs data;
	try {
		data.FromJsonStr(jsonData);
	}
	catch (...) {
		Cout() << "Error while parsing JSON\n";
		return false;
	}
	return ConvertJSONToStationFile(outputPath, data);
}


/**
 * Exports structured station data to Excel spreadsheet format.
 * Creates formatted table with header and all station records.
 * 
 * @param outputPathXlsx Destination Excel file path
 * @param fileData Structured station data to export
 */
void ConvertStationFileToExcel(const String& outputPathXlsx, FileDataJs& fileData) {
	auto formatDataString = [](const String& input) -> String {
		if (TrimBoth(input).GetLength() != 8) {
			return "";
		}
		return Format("%s.%s.%s",
			input.Left(2),
			input.Mid(2, 2),
			input.Mid(4, 4));
	};


	Vector<Vector<String>> excelRows;
	Vector<String> headers = {};

	Vector<String> stationRow;

	stationRow << "H01" << TrimBoth(fileData.header.H01) << "File number on media";
	excelRows.Add(pick(stationRow)); stationRow.clear();
	stationRow << "H02" << TrimBoth(fileData.header.H02) << "File contents";
	excelRows.Add(pick(stationRow)); stationRow.clear();
	stationRow << "H03" << TrimBoth(fileData.header.H03) << "File contents code";
	excelRows.Add(pick(stationRow)); stationRow.clear();
	stationRow << "H04" << TrimBoth(fileData.header.H04) << "Originating Country";
	excelRows.Add(pick(stationRow)); stationRow.clear();
	stationRow << "H05" << TrimBoth(fileData.header.H05) << "E-mail address";
	excelRows.Add(pick(stationRow)); stationRow.clear();
	stationRow << "H06" << TrimBoth(fileData.header.H06) << "Phone";
	excelRows.Add(pick(stationRow)); stationRow.clear();
	stationRow << "H07" << TrimBoth(fileData.header.H07) << "Telefax";
	excelRows.Add(pick(stationRow)); stationRow.clear();
	stationRow << "H08" << TrimBoth(fileData.header.H08) << "Name of responsible person";
	excelRows.Add(pick(stationRow)); stationRow.clear();
	stationRow << "H09" << TrimBoth(fileData.header.H09) << "Number of records";
	excelRows.Add(pick(stationRow)); stationRow.clear();
	stationRow << "H10" << TrimBoth(formatDataString(fileData.header.H10)) << "Writing date";
	excelRows.Add(pick(stationRow)); stationRow.clear();
	stationRow << "H11" << TrimBoth(fileData.header.H11) << "Destination country";
	excelRows.Add(pick(stationRow)); stationRow.clear();
	stationRow << "H12" << TrimBoth(fileData.header.H12) << "Unique file number";
	excelRows.Add(pick(stationRow)); stationRow.clear();
	stationRow << "H13" << TrimBoth(fileData.header.H13) << "File version";
	excelRows.Add(pick(stationRow)); stationRow.clear();
	stationRow << "H14" << TrimBoth(fileData.header.H14) << "Reserved for future use";
	excelRows.Add(pick(stationRow)); stationRow.clear();

	stationRow << "1A1"  << "1A2" << "1Z" << "6A" << "6B" << "6Z" << "10Z" << "2C" << "4A" << "4B" << "4C1" << "4C2" << "4D" << "4Z"
				<< "7A" << "8B1" << "8B2" << "9A" << "9B" << "9D" << "9G" << "9Y" << "9XH" << "9XV" << "1Y1" << "1Y2" << "13Z" << "13Y" << "2W" << "2Z" << "13X";
	excelRows.Add(pick(stationRow)); stationRow.clear();

	for (const auto& station : fileData.stations) {

		stationRow << station._1A.Mid(0,station._1A.GetCount()-1)  << station._1A.Mid(station._1A.GetCount()-1,1)
			<< TrimBoth(station._1Z) << TrimBoth(station._6A) << TrimBoth(station._6B) << TrimBoth(station._6Z) << TrimBoth(station._10Z)
			<< formatDataString(station._2C) << TrimBoth(station._4A) << TrimBoth(station._4B) << station._4C.Mid(0,8) << station._4C.Mid(8,7)
			<< TrimBoth(station._4D) << TrimBoth(station._4Z) << TrimBoth(station._7A) << TrimBoth(station._8B1) << TrimBoth(station._8B2)
			<< TrimBoth(station._9A) << TrimBoth(station._9B) << TrimBoth(station._9D) << TrimBoth(station._9G) << TrimBoth(station._9Y)
			<< TrimBoth(station._9XH) << TrimBoth(station._9XV) << station._1Y.Mid(0,station._1Y.GetCount()-1)<< station._1Y.Mid(station._1Y.GetCount()-1,1)
			<< TrimBoth(station._13Z) << TrimBoth(station._13Y) << TrimBoth(formatDataString(station._2W)) << formatDataString(station._2Z) << TrimBoth(station._13X);
		excelRows.Add(pick(stationRow)); stationRow.clear();
	}

	SaveToXLStoFile(outputPathXlsx, excelRows, headers);
}

/**
 * Converts fixed-length station file directly to Excel format.
 * Combines parsing and Excel export in single operation.
 * 
 * @param inputPath Source fixed-length station file path
 * @param outputPathXlsx Destination Excel file path
 */
void ConvertStationFileToExcel(const String& inputPath, const String& outputPathXlsx) {
	FileDataJs fileData;
	if (!ParseFixedLengthFile(inputPath, fileData)) {
		PromptOK("Error while read Station File");
		return;
	}
	ConvertStationFileToExcel(outputPathXlsx, fileData );
}

/**
 * Converts Excel spreadsheet back to fixed-length station file format.
 * Parses Excel data and reconstructs binary station file.
 * 
 * @param excelPath Source Excel file path
 * @param outputPath Destination fixed-length station file path
 * @return True if conversion successful, false on error
 */
bool ConvertExcelToStationFile(const String& excelPath, const String& outputPath) {
	if (!FileExists(excelPath)) {
		PromptOK("There is no excel file");
		return false;
	}

	xlnt::workbook wb;
	try {
		wb.load(excelPath.ToStd());
	} catch (...) {
		PromptOK("Error while open excel file");
		return false;
	}

	auto ws = wb.active_sheet();

	FileDataJs fileData;
	fileData.Clear();

	// Read header data
	int rowIndex = 0;
	for (int i = 0; i < 14; ++i) {
		auto value = ws.cell(xlnt::cell_reference(2, i + 1));
		String cellValue = value.has_value() ? String(value.to_string()) : "";
		switch (i) {
		case 0: fileData.header.H01 = cellValue; break;
		case 1: fileData.header.H02 = cellValue; break;
		case 2: fileData.header.H03 = cellValue; break;
		case 3: fileData.header.H04 = cellValue; break;
		case 4: fileData.header.H05 = cellValue; break;
		case 5: fileData.header.H06 = cellValue; break;
		case 6: fileData.header.H07 = cellValue; break;
		case 7: fileData.header.H08 = cellValue; break;
		case 8: fileData.header.H09 = cellValue; break;
		case 9: cellValue.Replace(".",""); fileData.header.H10 = cellValue; break;
		case 10: fileData.header.H11 = cellValue; break;
		case 11: fileData.header.H12 = cellValue; break;
		case 12: fileData.header.H13 = cellValue; break;
		case 13: fileData.header.H14 = cellValue; break;
		}
		rowIndex++;
	}


	rowIndex++;
	while (true) {
		String ss = String(ws.cell(xlnt::cell_reference(1, rowIndex + 1)).to_string() +
				ws.cell(xlnt::cell_reference(2, rowIndex + 1)).to_string() +
				ws.cell(xlnt::cell_reference(3, rowIndex + 1)).to_string());
		if (ss.IsEmpty()) break;

		StationRecord station;
		for (int col = 1; col <=31; ++col) {
			auto value = ws.cell(xlnt::cell_reference(col, rowIndex + 1));
			String cellValue = value.has_value() ? String(value.to_string()) : "";

			switch (col) {
			case 1: station._1A = cellValue; break;
			case 2: station._1A = station._1A + cellValue; break;
			case 3: station._1Z = cellValue; break;
			case 4: station._6A = cellValue; break;
			case 5: station._6B = cellValue; break;
			case 6: station._6Z = cellValue; break;
			case 7: station._10Z = cellValue; break;
			case 8: cellValue.Replace(".","");  station._2C = cellValue; break;
			case 9: station._4A = cellValue; break;
			case 10: station._4B = cellValue; break;
			case 11: station._4C = cellValue; break;
			case 12: station._4C = station._4C + cellValue; break;
			case 13: station._4D = cellValue; break;
			case 14: station._4Z = cellValue; break;
			case 15: station._7A = cellValue; break;
			case 16: station._8B1 = cellValue; break;
			case 17: station._8B2 = cellValue; break;
			case 18: station._9A = cellValue; break;
			case 19: station._9B = cellValue; break;
			case 20: station._9D = cellValue; break;
			case 21: station._9G = cellValue; break;
			case 22: station._9Y = cellValue; break;
			case 23: station._9XH = cellValue; break;
			case 24: station._9XV = cellValue; break;
			case 25: station._1Y = cellValue; break;
			case 26: station._1Y = station._1Y + cellValue; break;
			case 27: station._13Z = cellValue; break;
			case 28: station._13Y = cellValue; break;
			case 29: cellValue.Replace(".",""); station._2W = cellValue; break;
			case 30: cellValue.Replace(".",""); station._2Z = cellValue; break;
			case 31: station._13X = cellValue; break;
			}
		}
		fileData.stations.Add(station);
		rowIndex++;
	}

	// Write to the station file
	FileOut outFile(outputPath);
	if (!outFile) {
		PromptOK("Не удалось открыть файл для записи: " + outputPath);
		return false;
	}

	const auto SetLen = [](const String& s, int len) -> String {
		String res = s;
		if (res.GetLength() > len)
			return res.Left(len);
		else
			return String(' ', len - res.GetLength()) + res;
	};

	String headerStr =
		SetLen(fileData.header.H01, 2) +
		SetLen(fileData.header.H02, 80) +
		SetLen(fileData.header.H03, 1) +
		SetLen(fileData.header.H04, 3) +
		SetLen(fileData.header.H05, 40) +
		SetLen(fileData.header.H06, 20) +
		SetLen(fileData.header.H07, 20) +
		SetLen(fileData.header.H08, 20) +
		SetLen(fileData.header.H09, 6) +
		SetLen(fileData.header.H10, 8) +
		SetLen(fileData.header.H11, 3) +
		SetLen(fileData.header.H12, 6) +
		SetLen(fileData.header.H13, 3) +
		SetLen(fileData.header.H14, 7);

	if (headerStr.GetLength() != 219) {
		PromptOK("Ошибка: заголовок должен быть 219 байт, сейчас: " + AsString(headerStr.GetLength()));
		return false;
	}

	outFile.Put(headerStr);

	// Write stations
	for (const StationRecord& station : fileData.stations) {
		String stationStr =
			SetLen(station._1A, 12)   +
			SetLen(station._1Z, 1)    +
			SetLen(station._6A, 2)    +
			SetLen(station._6B, 2)    +
			SetLen(station._6Z, 2)    +
			SetLen(station._10Z, 1)   +
			SetLen(station._2C, 8)    +
			SetLen(station._4A, 20)   +
			SetLen(station._4B, 3)    +
			SetLen(station._4C, 15)   +
			SetLen(station._4D, 5)    +
			SetLen(station._4Z, 4)    +
			SetLen(station._7A, 9)    +
			SetLen(station._8B1, 6)   +
			SetLen(station._8B2, 1)   +
			SetLen(station._9A, 5)    +
			SetLen(station._9B, 5)    +
			SetLen(station._9D, 2)    +
			SetLen(station._9G, 4)    +
			SetLen(station._9Y, 4)    +
			SetLen(station._9XH, 7)   +
			SetLen(station._9XV, 7)   +
			SetLen(station._1Y, 12)   +
			SetLen(station._13Z, 50)  +
			SetLen(station._13Y, 1)   +
			SetLen(station._2W, 8)    +
			SetLen(station._2Z, 8)    +
			SetLen(station._13X, 15);

		if (stationStr.GetLength() != 219) {
			PromptOK("Wrong Station data length (219 symbols)");
			return false;
		}

		outFile.Put(stationStr);
	}

	outFile.Close();
	PromptOK("Station saved to: " + outputPath);
	return true;
}

/**
 * Legacy implementation for Excel to station file conversion.
 * Uses different column mapping and parsing logic.
 * 
 * @param excelPath Source Excel file path
 * @param outputPath Destination fixed-length station file path
 * @return True if conversion successful, false on error
 */
bool ConvertExcelToStationFile_OLD(const String& excelPath, const String& outputPath) {
	if (!FileExists(excelPath)) {
		PromptOK("There is no excel file");
		return false;
	}

	xlnt::workbook wb;
	try {
		wb.load(excelPath.ToStd());
	} catch (...) {
		PromptOK("Error while open excel file");
		return false;
	}

	auto ws = wb.active_sheet();
	FileDataJs fileData;
	fileData.Clear();


	Vector<String> expectedHeaders = {
		"H01", "H02", "H03", "H04", "H05", "H06", "H07", "H08", "H09", "H10",
		"H11", "H12", "H13", "H14", "1A", "1Z", "6A", "6B", "6Z", "10Z", "2C",
		"4A", "4B", "4C", "4D", "4Z", "7A", "8B1", "8B2", "9A", "9B", "9D", "9G",
		"9Y", "9XH", "9XV", "1Y", "13Z", "13Y", "2W", "2Z", "13X"
	};


	for (int i = 0; i < expectedHeaders.GetCount(); ++i) {
		auto cell = ws.cell(xlnt::cell_reference(i + 1, 1));
		if (!cell.has_value()) continue;

		String headerName = String(cell.to_string());
		if (headerName != expectedHeaders[i]) {
			PromptOK("There is error in column order");
			return false;
		}
	}

	for (int col = 0; col < 14; ++col) {
		auto cell = ws.cell(xlnt::cell_reference(col + 1, 2));
		String value = cell.has_value() ? String(cell.to_string()) : Null;
		switch (col) {
			case 0: fileData.header.H01 = value; break;
			case 1: fileData.header.H02 = value; break;
			case 2: fileData.header.H03 = value; break;
			case 3: fileData.header.H04 = value; break;
			case 4: fileData.header.H05 = value; break;
			case 5: fileData.header.H06 = value; break;
			case 6: fileData.header.H07 = value; break;
			case 7: fileData.header.H08 = value; break;
			case 8: fileData.header.H09 = value; break;
			case 9: fileData.header.H10 = value; break;
			case 10: fileData.header.H11 = value; break;
			case 11: fileData.header.H12 = value; break;
			case 12: fileData.header.H13 = value; break;
			case 13: fileData.header.H14 = value; break;
		}
	}

	int row = 3;
	while (true) {
		StationRecord station;
		bool hasAnyValue = false;

		for (int col = 14; col < expectedHeaders.GetCount(); ++col) {
			auto cell = ws.cell(xlnt::cell_reference(col + 1, row));
			String value = cell.has_value() ? String(cell.to_string()) : Null;
			if (!value.IsEmpty()) hasAnyValue = true;

			switch (col) {
				case 14: station._1A = value; break;
				case 15: station._1Z = value; break;
				case 16: station._6A = value; break;
				case 17: station._6B = value; break;
				case 18: station._6Z = value; break;
				case 19: station._10Z = value; break;
				case 20: station._2C = value; break;
				case 21: station._4A = value; break;
				case 22: station._4B = value; break;
				case 23: station._4C = value; break;
				case 24: station._4D = value; break;
				case 25: station._4Z = value; break;
				case 26: station._7A = value; break;
				case 27: station._8B1 = value; break;
				case 28: station._8B2 = value; break;
				case 29: station._9A = value; break;
				case 30: station._9B = value; break;
				case 31: station._9D = value; break;
				case 32: station._9G = value; break;
				case 33: station._9Y = value; break;
				case 34: station._9XH = value; break;
				case 35: station._9XV = value; break;
				case 36: station._1Y = value; break;
				case 37: station._13Z = value; break;
				case 38: station._13Y = value; break;
				case 39: station._2W = value; break;
				case 40: station._2Z = value; break;
				case 41: station._13X = value; break;
			}
		}

		if (!hasAnyValue) break;

		fileData.stations.Add(station);
		row++;
	}

	if (fileData.stations.IsEmpty()) {
		Cout() << "There is no data in file\n";
		return false;
	}

	fileData.header.H09 = Format("%06d", fileData.stations.GetCount());

	FileOut out(outputPath);
	if (!out) {
		Cout() << "Error. Cant open file to write: " << outputPath << "\n";
		return false;
	}

	const auto SetLen = [](const String& s, int len) -> String {
		String res = s;
		if (res.GetLength() > len)
			return res.Left(len);
		else
			return String(' ', len - res.GetLength()) + res;
	};

	String headerStr =
		SetLen(fileData.header.H01, 2) +
		SetLen(fileData.header.H02, 80) +
		SetLen(fileData.header.H03, 1) +
		SetLen(fileData.header.H04, 3) +
		SetLen(fileData.header.H05, 40) +
		SetLen(fileData.header.H06, 20) +
		SetLen(fileData.header.H07, 20) +
		SetLen(fileData.header.H08, 20) +
		SetLen(fileData.header.H09, 6) +
		SetLen(fileData.header.H10, 8) +
		SetLen(fileData.header.H11, 3) +
		SetLen(fileData.header.H12, 6) +
		SetLen(fileData.header.H13, 3) +
		SetLen(fileData.header.H14, 7);

	if (headerStr.GetLength() != 219) {
		PromptOK("Wrong Station data length (219 symbols");
		return false;
	}

	out.Put(headerStr);

	for (const auto& station : fileData.stations) {
		String stationStr =
			SetLen(station._1A, 12)   +
			SetLen(station._1Z, 1)    +
			SetLen(station._6A, 2)    +
			SetLen(station._6B, 2)    +
			SetLen(station._6Z, 2)    +
			SetLen(station._10Z, 1)   +
			SetLen(station._2C, 8)    +
			SetLen(station._4A, 20)   +
			SetLen(station._4B, 3)    +
			SetLen(station._4C, 15)   +
			SetLen(station._4D, 5)    +
			SetLen(station._4Z, 4)    +
			SetLen(station._7A, 9)    +
			SetLen(station._8B1, 6)   +
			SetLen(station._8B2, 1)   +
			SetLen(station._9A, 5)    +
			SetLen(station._9B, 5)    +
			SetLen(station._9D, 2)    +
			SetLen(station._9G, 4)    +
			SetLen(station._9Y, 4)    +
			SetLen(station._9XH, 7)   +
			SetLen(station._9XV, 7)   +
			SetLen(station._1Y, 12)   +
			SetLen(station._13Z, 50)  +
			SetLen(station._13Y, 1)   +
			SetLen(station._2W, 8)    +
			SetLen(station._2Z, 8)    +
			SetLen(station._13X, 15);

		if (stationStr.GetLength() != 219) {
			PromptOK("Wrong Station data length (219 symbols");
			return false;
		}

		out.Put(stationStr);
	}

	out.Close();
	return true;
}


#endif