/*=============================================================================
 |
 |       Author:  William Kozma Jr
 |                wkozma@ntia.gov
 |                US Dept of Commerce, NTIA/ITS
 |                June 2021 : Geneva Study Group 3 Meetings
 |
 *===========================================================================*/

#include "LFMFDrvr.h"
#include "Tags.h"
#include "LFMF.h"
#include "Labels.h"

// LFMF DLL Functions
typedef int(__stdcall* lfmf_func)(double h_tx__meter, double h_rx__meter, 
    double f__mhz, double P_tx__watt, double N_s, double d__km, 
    double epsilon, double sigma, int pol, Result* result);

// Local Globals
lfmf_func LFMF_Func;

int dllVerMajor = NOT_SET;
int dllVerMinor = NOT_SET;
int drvrVerMajor = NOT_SET;
int drvrVerMinor = NOT_SET;
int drvrVerDrvr = NOT_SET;

wchar_t buf[TIME_SIZE];

/*=============================================================================
 |
 |  Description:  Main function of the LFMF driver executable
 |
 *===========================================================================*/
int main(int argc, char** argv) {
    int rtn;
    DrvrParams params;

    // Get the time
    time_t t = time(NULL);
    _wctime_s(buf, TIME_SIZE, &t);

    rtn = ParseArguments(argc, argv, &params);
    if (rtn == DRVR__RETURN_SUCCESS)
        return SUCCESS;
    if (rtn) {
        Help();
        return rtn;
    }

    // validate command line inputs
    rtn = ValidateDriverInputs(&params);
    if (rtn) {
        printf_s("\n");
        Help();
        return rtn;
    }

    rtn = LoadDLL();
    if (rtn)
        return rtn;

    LFMFParams lfmf_params;
    rtn = ParseInputFile(params.in_file, &lfmf_params);
    if (rtn != SUCCESS)
        return rtn;

    rtn = ValidateModelInputs(&lfmf_params, params.mode);
    if (rtn != SUCCESS)
        return rtn;

    vector<double> dists__km;
    vector<double> A_btl__db;
    vector<double> E_dBuVm;
    vector<double> P_rx__dbm;
    vector<int> method;
    Result result;
    if (params.mode == MODE_POINT) {
        rtn = LFMF_Func(
            lfmf_params.h_tx__meter,
            lfmf_params.h_rx__meter,
            lfmf_params.f__mhz,
            lfmf_params.P_tx__watt,
            lfmf_params.N_s,
            lfmf_params.d__km,
            lfmf_params.epsilon,
            lfmf_params.sigma,
            lfmf_params.pol,
            &result);

        A_btl__db.push_back(result.A_btl__db);
        E_dBuVm.push_back(result.E_dBuVm);
        P_rx__dbm.push_back(result.P_rx__dbm);
        method.push_back(result.method);
    }
    else { // Line mode
        // controls resolution per decade of data
        double inc_step = 0.1;

        double yVal = 0;
        for (int power = 0; power < 4; power++)
        {
            for (double increment = 1; increment < 10 - inc_step; increment += inc_step)
            {
                double d__km = increment * pow(10, power);

                rtn = LFMF_Func(
                    lfmf_params.h_tx__meter,
                    lfmf_params.h_rx__meter,
                    lfmf_params.f__mhz,
                    lfmf_params.P_tx__watt,
                    lfmf_params.N_s,
                    d__km,
                    lfmf_params.epsilon,
                    lfmf_params.sigma,
                    lfmf_params.pol,
                    &result);

                A_btl__db.push_back(result.A_btl__db);
                E_dBuVm.push_back(result.E_dBuVm);
                P_rx__dbm.push_back(result.P_rx__dbm);
                method.push_back(result.method);
                dists__km.push_back(d__km);
            }
        }
    }

    // print results to file
    FILE* fp;
    int err = fopen_s(&fp, params.out_file, "w");
    if (err != 0) {
        printf_s("Error opening output file.  Exiting.\n");
        return err;
    }
    else {
        fprintf_s(fp, "Data Generated by the ITS LFMF Driver\n");
        fprintf_s(fp, "lfmf.dll Version          v%i.%i\n", dllVerMajor, dllVerMinor);
        fprintf_s(fp, "LFMFDrvr.exe Version      v%i.%i.%i\n", drvrVerMajor, drvrVerMinor, drvrVerDrvr);
        fwprintf_s(fp, L"Date Generated            %s\n", buf);
        fprintf_s(fp, "Input Arguments          ");
        for (int i = 1; i < argc; i++) {
            fprintf_s(fp, "%s ", argv[i]);
        }
        fprintf_s(fp, "\n\n");
        fprintf_s(fp, "Inputs\n");
        fprintf_s(fp, "%-24s %-12.2g %s\n", TAG__HTX, lfmf_params.h_tx__meter, UNITS__METER);
        fprintf_s(fp, "%-24s %-12.2g %s\n", TAG__HRX, lfmf_params.h_rx__meter, UNITS__METER);
        fprintf_s(fp, "%-24s %-12.2f %s\n", TAG__NS, lfmf_params.N_s, UNITS__NUNIT);
        fprintf_s(fp, "%-24s %-12.2f %s\n", TAG__FREQ, lfmf_params.f__mhz, UNITS__MHZ);
        fprintf_s(fp, "%-24s %-12i ", TAG__POL, lfmf_params.pol);
        PrintPolarizationLabel(fp, lfmf_params.pol);
        fprintf_s(fp, "%-24s %.2g\n", TAG__EPSILON, lfmf_params.epsilon);
        fprintf_s(fp, "%-24s %-12.3f %s\n", TAG__SIGMA, lfmf_params.sigma, UNITS__SM);
        fprintf_s(fp, "%-24s %-12.0f %s\n", TAG__PTX, lfmf_params.P_tx__watt, UNITS__WATT);
        if (params.mode == MODE_POINT) {
            fprintf_s(fp, "%-24s %-12.2f %s\n", TAG__DKM, lfmf_params.d__km, UNITS__KM);
            fprintf_s(fp, "Mode                     Point\n");
        }
        else
            fprintf_s(fp, "Mode                     Line\n");
        fprintf_s(fp, "\n");
        if (rtn != SUCCESS) {
            fprintf_s(fp, "LFMF Error                %-12i ", rtn);
            PrintErrorMsgLabel(fp, rtn);
        }
        else {
            if (params.mode == MODE_POINT) {
                fprintf_s(fp, "Distance:                %12.3f %s\n", lfmf_params.d__km, UNITS__KM);
                fprintf_s(fp, "Basic Transmission Loss: %12.3f %s\n", A_btl__db.front(), UNITS__DB);
                fprintf_s(fp, "Field Strength:          %12.3f %s\n", E_dBuVm.front(), UNITS__DBUVM);
                fprintf_s(fp, "Received Power:          %12.3f %s\n", P_rx__dbm.front(), UNITS__DBM);
                if (params.method) {
                    fprintf_s(fp, "Solution Method:     %12i     ", method.front());
                    PrintMethodLabel(fp, method.front());
                }
            }
            else { // Line mode
                fprintf_s(fp, "Output Units\n");
                fprintf_s(fp, "Basic Transmission Loss:      %s\n", UNITS__DB);
                fprintf_s(fp, "Field Strength:               %s\n", UNITS__DBUVM);
                fprintf_s(fp, "Received Power:               %s\n", UNITS__DBM);
                fprintf_s(fp, "\n");
                fprintf_s(fp, "d__km,%s,%s,%s", UNITS__DB, UNITS__DBUVM, UNITS__DBM);
                if (params.method)
                    fprintf_s(fp, ",method");
                fprintf_s(fp, "\n");

                for (int i = 0; i < A_btl__db.size(); i++) {
                    fprintf_s(fp, "%.3f,%.3f,%.3f,%.3f", dists__km[i], A_btl__db[i], E_dBuVm[i], P_rx__dbm[i]);
                    if (params.method)
                        fprintf_s(fp, ",%i", method[i]);
                    fprintf_s(fp, "\n");
                }
            }
        }
    }

    printf_s("Successful Execution.\n");
    fclose(fp);

    return SUCCESS;
}

/*=============================================================================
 |
 |  Description:  Parse the command line arguments
 |
 |        Input:  argc      - Number of arguments
 |                argv      - Command line arguments
 |
 |       Output:  params    - Structure with user input params
 |
 |      Returns:  SUCCESS, or error code encountered
 |
 *===========================================================================*/
int ParseArguments(int argc, char** argv, DrvrParams* params) {
    for (int i = 1; i < argc; i++) {
        Lowercase(argv[i]);

        if (Match("-i", argv[i])) {
            sprintf_s(params->in_file, "%s", argv[i + 1]);
            i++;
        }
        else if (Match("-o", argv[i])) {
            sprintf_s(params->out_file, "%s", argv[i + 1]);
            i++;
        }
        else if (Match("-mode", argv[i]))
        {
            Lowercase(argv[i + 1]);

            if (Match("point", argv[i + 1]))
                params->mode = MODE_POINT;
            else if (Match("line", argv[i + 1]))
                params->mode = MODE_LINE;
            else
                return ParseErrorMsgHelper("-mode [mode]", DRVRERR__PARSE_MODE_VALUE);

            i++;
        }
        else if (Match("-method", argv[i])) {
            params->method = true;
        }
        else if (Match("-v", argv[i])) {
            Version();
            return DRVR__RETURN_SUCCESS;
        }
        else if (Match("-h", argv[i])) {
            Help();
            return DRVR__RETURN_SUCCESS;
        }
        else {
            printf_s("Unknown option: %s\n", argv[i]);
            return DRVRERR__INVALID_OPTION;
        }
    }

    return SUCCESS;
}

/*=============================================================================
 |
 |  Description:  Convert the char array to lower case
 |
 | Input/Output:  argv      - value
 |
 |      Returns:  [Void]
 |
 *===========================================================================*/
void Lowercase(char* argv) {
    for (int i = 0; i < strlen(argv); i++)
        argv[i] = tolower(argv[i]);
}

/*=============================================================================
 |
 |  Description:  Compare to strings to see if they match
 |
 |        Input:  opt       - Given char array
 |                arg       - Expected char array
 |
 |      Returns:  True/False
 |
 *===========================================================================*/
bool Match(const char* opt, char* arg) {
    if (strcmp(opt, arg) == 0)
        return true;
    return false;
}

/*=============================================================================
 |
 |  Description:  Print help instructions to the terminal
 |
 |        Input:  [Void]
 |
 |      Returns:  [Void]
 |
 *===========================================================================*/
void Help() {
    printf_s("\n");
    printf_s("Usage: LFMFDrvr.exe [Options]\n");
    printf_s("Options (not case sensitive)\n");
    printf_s("\t-i      :: Input file name\n");
    printf_s("\t-o      :: Output file name\n");
    printf_s("\t-mode   :: Mode of operation [POINT, LINE]\n");
    printf_s("\t-method :: Include the solution method [optional]\n");
    printf_s("\n");
    printf_s("Examples:\n");
    printf_s("\tLFMFDrvr.exe -i inputs.txt -o results.txt -mode LINE\n");
    printf_s("\n");
};

/*=============================================================================
 |
 |  Description:  Print version information to the terminal
 |
 |        Input:  [Void]
 |
 |      Returns:  [Void]
 |
 *===========================================================================*/
void Version() {
    printf_s("*******************************************************\n");
    printf_s("Institute for Telecommunications Sciences - Boulder, CO\n");
    printf_s("\tLFMF Driver Version: %i.%i\n", drvrVerMajor, drvrVerMinor);
    printf_s("\tLFMF DLL Version: %i.%i\n", dllVerMajor, dllVerMinor);
    wprintf_s(L"Time: %s", buf);
    printf_s("*******************************************************\n");
}

/*=============================================================================
 |
 |  Description:  Helper function to format and print error messages
 |                encountered during command argument parsing
 |
 |        Input:  opt       - Command flag in error
 |                err       - Error code
 |
 |      Returns:  Error code
 |
 *===========================================================================*/
int ParseErrorMsgHelper(const char* opt, int err) {
    printf_s("DrvrErr %i: Unable to parse %s value.\n", err, opt);
    return err;
}

/*=============================================================================
 |
 |  Description:  Validate that the required inputs are present for the
 |                mode specified by the user.  This function DOES NOT
 |                check the validity of the parameter values - only that
 |                required parameters have been specified by the user
 |
 |        Input:  params        - Structure with user input parameters
 |
 |      Returns:  SUCCESS, or error code encountered
 |
 *===========================================================================*/
int ValidateDriverInputs(DrvrParams* params) {
    if (params->mode == NOT_SET)
        return Validate_RequiredErrMsgHelper("-mode", DRVRERR__VALIDATION_MODE);

    if (strlen(params->in_file) == 0)
        return Validate_RequiredErrMsgHelper("-i", DRVRERR__VALIDATION_IN_FILE);

    if (strlen(params->out_file) == 0)
        return Validate_RequiredErrMsgHelper("-o", DRVRERR__VALIDATION_OUT_FILE);

    return SUCCESS;
}

/*=============================================================================
 |
 |  Description:  Helper function to format and print error messages
 |                encountered during validation of input parameters
 |
 |        Input:  opt       - Command flag in error
 |                err       - Error code
 |
 |      Returns:  Error code
 |
 *===========================================================================*/
int Validate_RequiredErrMsgHelper(const char* opt, int err) {
    printf_s("DrvrError %i: Option %s is required but was not provided\n", err, opt);
    return err;
}

/*=============================================================================
 |
 |  Description:  Loads the LFMF DLL
 |
 |        Input:  [void]
 |
 |      Returns:  [void]
 |
 *===========================================================================*/
int LoadDLL() {
    HINSTANCE hLib = LoadLibrary(TEXT("lfmf.dll"));

    if (hLib == NULL)
        return DRVRERR__DLL_LOADING;

    GetDLLVersionInfo();
    GetDrvrVersionInfo();
    if (dllVerMajor != drvrVerMajor)
        return DRVRERR__MAJOR_VERSION_MISMATCH;

    // Grab the function in the DLL
    LFMF_Func = (lfmf_func)GetProcAddress((HMODULE)hLib, "LFMF");
    if (LFMF_Func == nullptr)
        return DRVRERR__GET_LFMF_FUNC_LOADING;

    return SUCCESS;
}

/*=============================================================================
 |
 |  Description:  Get the version information of the LFMF DLL
 |
 |        Input:  [void]
 |
 |      Returns:  [void]
 |
 *===========================================================================*/
void GetDLLVersionInfo() {
    DWORD  verHandle = NULL;
    UINT   size = 0;
    LPBYTE lpBuffer = NULL;
    DWORD  verSize = GetFileVersionInfoSize(TEXT("lfmf.dll"), &verHandle);

    if (verSize != NULL)
    {
        LPSTR verData = new char[verSize];

        if (GetFileVersionInfo(TEXT("lfmf.dll"), verHandle, verSize, verData))
        {
            if (VerQueryValue(verData, TEXT("\\"), (VOID FAR * FAR*) & lpBuffer, &size))
            {
                if (size)
                {
                    VS_FIXEDFILEINFO* verInfo = (VS_FIXEDFILEINFO*)lpBuffer;
                    if (verInfo->dwSignature == 0xfeef04bd)
                    {
                        dllVerMajor = (verInfo->dwFileVersionMS >> 16) & 0xffff;
                        dllVerMinor = (verInfo->dwFileVersionMS >> 0) & 0xffff;
                    }
                }
            }
        }

        delete[] verData;
    }

    return;
}

/*=============================================================================
 |
 |  Description:  Get the version information of this driver
 |
 |        Input:  [void]
 |
 |      Returns:  [void]
 |
 *===========================================================================*/
void GetDrvrVersionInfo()
{
    DWORD  verHandle = NULL;
    UINT   size = 0;
    LPBYTE lpBuffer = NULL;
    WCHAR fileName[MAX_PATH];

    GetModuleFileName(NULL, fileName, MAX_PATH);
    DWORD  verSize = GetFileVersionInfoSize(fileName, &verHandle);

    if (verSize != NULL)
    {
        LPSTR verData = new char[verSize];

        if (GetFileVersionInfo(fileName, verHandle, verSize, verData))
        {
            if (VerQueryValue(verData, L"\\", (VOID FAR * FAR*) & lpBuffer, &size))
            {
                if (size)
                {
                    VS_FIXEDFILEINFO* verInfo = (VS_FIXEDFILEINFO*)lpBuffer;
                    if (verInfo->dwSignature == 0xfeef04bd)
                    {
                        drvrVerMajor = (verInfo->dwFileVersionMS >> 16) & 0xffff;
                        drvrVerMinor = (verInfo->dwFileVersionMS >> 0) & 0xffff;
                        drvrVerDrvr = (verInfo->dwFileVersionLS >> 16) & 0xffff;
                    }
                }
            }
        }

        delete[] verData;
    }

    return;
}

/*=============================================================================
 |
 |  Description:  Parse input parameter file
 |
 |        Input:  in_file       - Path to input parameter file
 |
 |       Output:  lfmf_params   - LFMF input parameter struct
 |
 |      Returns:  rtn           - Return error code
 |
 *===========================================================================*/
int ParseInputFile(const char* in_file, LFMFParams* lfmf_params) {
    ifstream file;
    file.open(in_file);
    string line;

    while (getline(file, line)) {
        int i = line.find(",");

        string key = line.substr(0, i);
        string value = line.substr(i + 1);

        transform(key.begin(), key.end(), key.begin(), ::tolower);

        if (key.compare(TAG__HTX) == 0) {
            if (ParseDouble(value, &lfmf_params->h_tx__meter) == ERROR) {
                return ParsingErrorHelper(DRVRERR__PARSE_HTX, TAG__HTX);
            }
        }
        else if (key.compare(TAG__HRX) == 0) {
            if (ParseDouble(value, &lfmf_params->h_rx__meter) == ERROR) {
                return ParsingErrorHelper(DRVRERR__PARSE_HRX, TAG__HRX);
            }
        }
        else if (key.compare(TAG__DKM) == 0) {
            if (ParseDouble(value, &lfmf_params->d__km) == ERROR) {
                return ParsingErrorHelper(DRVRERR__PARSE_DKM, TAG__DKM);
            }
        }
        else if (key.compare("n_s") == 0) {
            if (ParseDouble(value, &lfmf_params->N_s) == ERROR) {
                return ParsingErrorHelper(DRVRERR__PARSE_NS, TAG__NS);
            }
        }
        else if (key.compare("p_tx__watt") == 0) {
            if (ParseDouble(value, &lfmf_params->P_tx__watt) == ERROR) {
                return ParsingErrorHelper(DRVRERR__PARSE_PTX, TAG__PTX);
            }
        }
        else if (key.compare(TAG__FREQ) == 0) {
            if (ParseDouble(value, &lfmf_params->f__mhz) == ERROR) {
                return ParsingErrorHelper(DRVRERR__PARSE_FREQ, TAG__FREQ);
            }
        }
        else if (key.compare(TAG__POL) == 0) {
            if (ParseInteger(value, &lfmf_params->pol) == ERROR) {
                return ParsingErrorHelper(DRVRERR__PARSE_POL, TAG__POL);
            }
        }
        else if (key.compare(TAG__EPSILON) == 0) {
            if (ParseDouble(value, &lfmf_params->epsilon) == ERROR) {
                return ParsingErrorHelper(DRVRERR__PARSE_EPSILON, TAG__EPSILON);
            }
        }
        else if (key.compare(TAG__SIGMA) == 0) {
            if (ParseDouble(value, &lfmf_params->sigma) == ERROR) {
                return ParsingErrorHelper(DRVRERR__PARSE_SIGMA, TAG__SIGMA);
            }
        }
        else {
            printf("Unknown input parameter\n");
        }
    }

    file.close();

    return SUCCESS;
}

/*=============================================================================
 |
 |  Description:  Common error handling function
 |
 |        Input:  err           - Error parsing code
 |                msg           - Error message
 |
 |       Output:  [void]
 |
 |      Returns:  rtn           - Error code from input param
 |
 *===========================================================================*/
int ParsingErrorHelper(int err, const char* msg) {
    printf_s("DrvrErr %i: Unable to parse '%s' value.\n", err, msg);
    return err;
}

/*=============================================================================
 |
 |  Description:  Parse an integer value read from the input parameter file
 |
 |        Input:  str           - Input file value as string
 |
 |       Output:  value         - Input file value converted to int
 |
 |      Returns:  rtn           - Return error code
 |
 *===========================================================================*/
int ParseInteger(const string& str, int* value) {
    size_t t;

    try {
        *value = ::stoi(str, &t, 10);
    }
    catch (...) {
        // error parsing the input string value
        return ERROR;
    }

    // verify the entire string was parsed, and a trailing char wasn't omitted
    if (str.length() != t)
        return ERROR;

    return SUCCESS;
}

/*=============================================================================
 |
 |  Description:  Parse a double value read from the input parameter file
 |
 |        Input:  str           - Input file value as string
 |
 |       Output:  value         - Input file value converted to double
 |
 |      Returns:  rtn           - Return error code
 |
 *===========================================================================*/
int ParseDouble(const string& str, double* value) {
    try {
        *value = ::stod(str, nullptr);
    }
    catch (...) {
        // error parsing the input string value
        return ERROR;
    }

    return SUCCESS;
}

/*=============================================================================
 |
 |  Description:  Validate LFMF input parameters.  This function only validates
 |                that required parameters are present - not that they are
 |                valid values.  The LFMF DLL performs validation to ensure
 |                the parameters are within valid range.
 |
 |        Input:  lfmf_params   - LFMF input parameter struct
 |
 |       Output:  [void]
 |
 |      Returns:  rtn           - Return error code
 |
 *===========================================================================*/
int ValidateModelInputs(LFMFParams* lfmf_params, int mode) {
    if (lfmf_params->h_tx__meter == NOT_SET)
        return Validate_RequiredErrMsgHelper(TAG__HTX, DRVRERR__VALIDATION_HTX);

    if (lfmf_params->h_rx__meter == NOT_SET)
        return Validate_RequiredErrMsgHelper(TAG__HRX, DRVRERR__VALIDATION_HTX);

    if (lfmf_params->P_tx__watt == NOT_SET)
        return Validate_RequiredErrMsgHelper(TAG__PTX, DRVRERR__VALIDATION_PTX);

    if (lfmf_params->N_s == NOT_SET)
        return Validate_RequiredErrMsgHelper(TAG__NS, DRVRERR__VALIDATION_NS);

    if (lfmf_params->f__mhz == NOT_SET)
        return Validate_RequiredErrMsgHelper(TAG__FREQ, DRVRERR__VALIDATION_FMHZ);

    if (lfmf_params->pol == NOT_SET)
        return Validate_RequiredErrMsgHelper(TAG__POL, DRVRERR__VALIDATION_POL);

    if (lfmf_params->epsilon == NOT_SET)
        return Validate_RequiredErrMsgHelper(TAG__EPSILON, DRVRERR__VALIDATION_EPSILON);

    if (lfmf_params->sigma == NOT_SET)
        return Validate_RequiredErrMsgHelper(TAG__SIGMA, DRVRERR__VALIDATION_SIGMA);

    if (mode == MODE_POINT && lfmf_params->d__km == NOT_SET)
        return Validate_RequiredErrMsgHelper(TAG__DKM, DRVRERR__VALIDATION_DKM);

    return SUCCESS;
}

/*=============================================================================
 |
 |  Description:  Print text message corresponding to polarization value
 |
 |        Input:  fp        - File pointer
 |                pol       - Polarization
 |
 |       Output:  [void]
 |
 |      Returns:  [void]
 |
 *===========================================================================*/
void PrintPolarizationLabel(FILE* fp, int pol) {
    switch (pol) {
    case POLARIZATION__HORIZONTAL:
        PrintLabel(fp, LBL__POLARIZATION_HORIZONTAL);
        break;
    case POLARIZATION__VERTICAL:
        PrintLabel(fp, LBL__POLARIZATION_VERTICAL);
        break;
    default:
        PrintLabel(fp, LBL__ERROR_INVALID_VALUE);
        break;
    }
}

/*=============================================================================
 |
 |  Description:  Print text message corresponding error codes
 |
 |        Input:  fp        - File pointer
 |                err       - Error code
 |
 |       Output:  [void]
 |
 |      Returns:  [void]
 |
 *===========================================================================*/
void PrintErrorMsgLabel(FILE* fp, int err) {
    switch (err)
    {
    case SUCCESS:
        PrintLabel(fp, LBL__SUCCESS);
        break;
    case ERROR__TX_TERMINAL_HEIGHT:
        PrintLabel(fp, LBL__ERROR_TX_TERMINAL_HEIGHT);
        break;
    case ERROR__RX_TERMINAL_HEIGHT:
        PrintLabel(fp, LBL__ERROR_RX_TERMINAL_HEIGHT);
        break;
    case ERROR__SURFACE_REFRACTIVITY:
        PrintLabel(fp, LBL__ERROR_SURFACE_REFRACTIVITY);
        break;
    case ERROR__FREQUENCY:
        PrintLabel(fp, LBL__ERROR_FREQUENCY);
        break;
    case ERROR__POLARIZATION:
        PrintLabel(fp, LBL__ERROR_POLARIZATION);
        break;
    case ERROR__EPSILON:
        PrintLabel(fp, LBL__ERROR_EPSILON);
        break;
    case ERROR__SIGMA:
        PrintLabel(fp, LBL__ERROR_SIGMA);
        break;
    case ERROR__PATH_DISTANCE:
        PrintLabel(fp, LBL__ERROR_PATH_DISTANCE);
        break;
    case ERROR__TX_POWER:
        PrintLabel(fp, LBL__ERROR_TX_POWER);
        break;
    }
}

/*=============================================================================
|
|  Description:  Helper function to standardize printing of text labels to file
|
|        Input:  fp        - File pointer
|                lbl       - Text message
|
|       Output:  [void]
|
|      Returns:  [void]
|
*===========================================================================*/
void PrintLabel(FILE* fp, const char* lbl) {
    fprintf_s(fp, "[%s]\n", lbl);
}

/*=============================================================================
 |
 |  Description:  Print text message corresponding to polarization value
 |
 |        Input:  fp        - File pointer
 |                pol       - Polarization
 |
 |       Output:  [void]
 |
 |      Returns:  [void]
 |
 *===========================================================================*/
void PrintMethodLabel(FILE* fp, int method) {
    switch (method) {
    case METHOD__FLAT_EARTH_CURVE:
        PrintLabel(fp, LBL__METHOD_FLAT_EARTH_CURVE);
        break;
    case METHOD__RESIDUE_SERIES:
        PrintLabel(fp, LBL__METHOD_RESIUDE_SERIES);
        break;
    default:
        PrintLabel(fp, LBL__ERROR_INVALID_VALUE);
        break;
    }
}