// GridTracker Copyright © 2020 GridTracker.org
// All rights reserved.
// See LICENSE for more information.

var selectStartupLink = null;

function dragOverHandler(ev) {
  // Prevent default behavior (Prevent file from being opened)
  ev.preventDefault();
}

function dropHandler(ev) {
  // Prevent default behavior (Prevent file from being opened)
  ev.preventDefault();
  if (ev.dataTransfer.items) {
    // Use DataTransferItemList interface to access the file(s)
    for (var i = 0; i < ev.dataTransfer.items.length; i++) {
      // If dropped items aren't files, reject them
      Entry = ev.dataTransfer.items[i].webkitGetAsEntry();
      if (Entry && typeof Entry.isFile != "undefined" && Entry.isFile == true) {
        var filename = ev.dataTransfer.items[i].getAsFile().path;
        var test = filename.toLowerCase();
        var valid = test.endsWith(".adi")
          ? true
          : test.endsWith(".adif")
          ? true
          : test.endsWith(".log")
          ? true
          : test.endsWith(".txt")
          ? true
          : false;
        if (valid && fs.existsSync(filename)) {
          onAdiLoadComplete(fs.readFileSync(filename, "UTF-8"), false);
        }
      }
    }
  }
}

function findAdiField(row, field) {
  var value = "";
  var regex = new RegExp("<" + field + ":", "i");
  var firstSplitArray = row.split(regex);
  if (firstSplitArray && firstSplitArray.length == 2) {
    var secondSplitArray = firstSplitArray[1].split(">");
    if (secondSplitArray.length > 1) {
      var newLenSearch = secondSplitArray[0].split(":");
      var newLen = newLenSearch[0];
      value = secondSplitArray[1].slice(0, newLen);
    }
    delete secondSplitArray;
  }
  return value;
}

function onAdiLoadComplete(adiBuffer, saveAdifFile, adifFileName, newFile) {
  let rawAdiBuffer = "";
  if (typeof adiBuffer == "object") rawAdiBuffer = String(adiBuffer);
  else rawAdiBuffer = adiBuffer;

  let activeAdifArray = Array();
  let activeAdifLogMode = true;

  if (rawAdiBuffer.indexOf("PSKReporter") > -1) activeAdifLogMode = false;

  if (rawAdiBuffer.length > 1) {
    let regex = new RegExp("<EOR>", "i");
    activeAdifArray = rawAdiBuffer.split(regex);
  }

  let dateTime = new Date();
  let dupes = 0;

  for (let x = 0; x < activeAdifArray.length; x++) {
    let finalMode = "";

    if (activeAdifArray[x].length > 3) {
      if (activeAdifLogMode) {
        let confirmed = false;
        let finalGrid = findAdiField(
          activeAdifArray[x],
          "GRIDSQUARE"
        ).toUpperCase();
        let vuccGrids = findAdiField(
          activeAdifArray[x],
          "VUCC_GRIDS"
        ).toUpperCase();
        let finalVucc = [];
        let finalDXcall = findAdiField(activeAdifArray[x], "CALL").replace(
          "_",
          "/"
        );
        let finalDEcall = findAdiField(
          activeAdifArray[x],
          "STATION_CALLSIGN"
        ).replace("_", "/");

        if (finalDEcall == "") finalDEcall = myDEcall;
        if (
          g_appSettings.workingCallsignEnable &&
          !(finalDEcall in g_appSettings.workingCallsigns)
        )
          continue;
        let finalRSTsent = findAdiField(activeAdifArray[x], "RST_SENT");
        let finalRSTrecv = findAdiField(activeAdifArray[x], "RST_RCVD");
        let finalBand = findAdiField(activeAdifArray[x], "BAND").toLowerCase();
        let dateVal = findAdiField(activeAdifArray[x], "QSO_DATE");
        let timeVal = findAdiField(activeAdifArray[x], "TIME_ON");
        let finalState = findAdiField(
          activeAdifArray[x],
          "STATE"
        ).toUpperCase();
        if (finalState.length == 0) finalState = null;
        let finalPropMode = findAdiField(
          activeAdifArray[x],
          "PROP_MODE"
        ).toUpperCase();
        let finalSatName = findAdiField(
          activeAdifArray[x],
          "SAT_NAME"
        ).toUpperCase();
        let finalCont = findAdiField(activeAdifArray[x], "CONT").toUpperCase();
        if (finalCont.length == 0) finalCont = null;
        let finalCnty = findAdiField(activeAdifArray[x], "CNTY").toUpperCase();
        if (finalCnty.length == 0) finalCnty = null;
        else {
          finalCnty = finalCnty.replaceAll(" ", "");
        }
        let finalMode = findAdiField(activeAdifArray[x], "MODE").toUpperCase();
        let subMode = findAdiField(activeAdifArray[x], "SUBMODE");
        if (subMode == "FT4" && (finalMode == "MFSK" || finalMode == "DATA"))
          finalMode = "FT4";
        if (subMode == "JS8" && finalMode == "MFSK") finalMode = "JS8";

        if (finalBand == "oob" || finalBand == "") {
          finalBand = Number(
            findAdiField(activeAdifArray[x], "FREQ")
          ).formatBand();
        }

        let finalMsg = findAdiField(activeAdifArray[x], "COMMENT");
        let finalQslMsg = findAdiField(activeAdifArray[x], "QSLMSG");
        let finalQslMsgIntl = findAdiField(activeAdifArray[x], "QSLMSG_INTL");
        if (finalQslMsg.length > 1) finalMsg = finalQslMsg;
        if (finalQslMsgIntl.length > 1 && finalMsg == "")
          finalMsg = finalQslMsgIntl;

        let finalDxcc = Number(findAdiField(activeAdifArray[x], "DXCC"));
        if (finalDxcc == 0) finalDxcc = Number(callsignToDxcc(finalDXcall));

        if (!(finalDxcc in g_dxccToGeoData))
          finalDxcc = Number(callsignToDxcc(finalDXcall));

        // If my callsign isn't present, it must be for me anyway

        let finalCqZone = findAdiField(activeAdifArray[x], "CQZ");
        if (finalCqZone.length == 1) finalCqZone = "0" + finalCqZone;

        if (parseInt(finalCqZone) < 1 || parseInt(finalCqZone) > 40)
          finalCqZone = "";
        finalCqZone = String(finalCqZone);
        let finalItuZone = findAdiField(activeAdifArray[x], "ITUZ");
        if (finalItuZone.length == 1) finalItuZone = "0" + finalItuZone;

        if (parseInt(finalItuZone) < 1 || parseInt(finalItuZone) > 90)
          finalItuZone = "";
        finalItuZone = String(finalItuZone);

        let finalIOTA = findAdiField(activeAdifArray[x], "IOTA").toUpperCase();

        let qrzConfirmed = findAdiField(
          activeAdifArray[x],
          "APP_QRZLOG_STATUS"
        ).toUpperCase();
        let lotwConfirmed1 = findAdiField(
          activeAdifArray[x],
          "QSL_RCVD"
        ).toUpperCase();
        let lotw_qsl_rcvd = findAdiField(
          activeAdifArray[x],
          "LOTW_QSL_RCVD"
        ).toUpperCase();

        if (
          qrzConfirmed == "C" ||
          lotw_qsl_rcvd == "Y" ||
          lotw_qsl_rcvd == "V" ||
          lotwConfirmed1 == "Y"
        )
          confirmed = true;

        let dateTime = new Date(
          Date.UTC(
            dateVal.substr(0, 4),
            parseInt(dateVal.substr(4, 2)) - 1,
            dateVal.substr(6, 2),
            timeVal.substr(0, 2),
            timeVal.substr(2, 2),
            timeVal.substr(4, 2)
          )
        );

        let finalTime = parseInt(dateTime.getTime() / 1000);

        if (
          g_appSettings.workingDateEnable &&
          finalTime < g_appSettings.workingDate
        )
          continue;

        finalGrid = finalGrid.substr(0, 6);
        if (!validateGridFromString(finalGrid)) finalGrid = "";
        if (finalGrid == "" && vuccGrids != "") {
          finalVucc = vuccGrids.split(",");
          finalGrid = finalVucc[0];
          finalVucc.shift();
        }
        let isDigital = false;
        let isPhone = false;
        if (finalMode in g_modes) {
          isDigital = g_modes[finalMode];
        }
        if (finalMode in g_modes_phone) {
          isPhone = g_modes_phone[finalMode];
        }
        if (finalDXcall != "")
          addDeDx(
            finalGrid,
            finalDXcall,
            false,
            false,
            false,
            finalDEcall,
            finalRSTsent,
            finalTime,
            finalMsg,
            finalMode,
            finalBand,
            confirmed,
            false,
            finalRSTrecv,
            finalDxcc,
            finalState,
            finalCont,
            finalCnty,
            finalCqZone,
            finalItuZone,
            finalVucc,
            finalPropMode,
            isDigital,
            isPhone,
            finalIOTA,
            finalSatName
          );
      } else {
        let finalMyGrid = findAdiField(
          activeAdifArray[x],
          "MY_GRIDSQUARE"
        ).toUpperCase();
        let finalGrid = findAdiField(
          activeAdifArray[x],
          "GRIDSQUARE"
        ).toUpperCase();
        let finalDXcall = findAdiField(activeAdifArray[x], "CALL");
        let finalDEcall = findAdiField(activeAdifArray[x], "OPERATOR");
        let finalRSTsent = findAdiField(activeAdifArray[x], "APP_PSKREP_SNR");
        let dateVal = findAdiField(activeAdifArray[x], "QSO_DATE");
        let timeVal = findAdiField(activeAdifArray[x], "TIME_ON");
        let finalMode = findAdiField(activeAdifArray[x], "MODE");
        let finalBand = Number(
          findAdiField(activeAdifArray[x], "FREQ")
        ).formatBand();
        let finalMsg = "-";
        let finalDxcc = Number(findAdiField(activeAdifArray[x], "DXCC"));
        if (finalDxcc == 0) {
          if (finalDXcall == myDEcall) finalDxcc = callsignToDxcc(finalDEcall);
          else finalDxcc = callsignToDxcc(finalDXcall);
        }

        finalGrid = finalGrid.substr(0, 6);

        let dateTime = new Date(
          Date.UTC(
            dateVal.substr(0, 4),
            parseInt(dateVal.substr(4, 2)) - 1,
            dateVal.substr(6, 2),
            timeVal.substr(0, 2),
            timeVal.substr(2, 2),
            timeVal.substr(4, 2)
          )
        );
        let finalTime = parseInt(dateTime.getTime() / 1000);
        if (
          finalGrid != "" &&
          finalDXcall != "" &&
          validateGridFromString(finalGrid)
        ) {
          if (finalDXcall == myDEcall)
            addDeDx(
              finalMyGrid,
              finalDEcall,
              false,
              false,
              false,
              finalDXcall,
              null,
              finalTime,
              finalMsg,
              finalMode,
              finalBand,
              false,
              true,
              finalRSTsent,
              finalDxcc,
              null,
              null,
              null,
              null,
              null
            );
          else if (finalDEcall == myDEcall)
            addDeDx(
              finalGrid,
              finalDXcall,
              false,
              false,
              false,
              "-",
              finalRSTsent,
              finalTime,
              finalMsg,
              finalMode,
              finalBand,
              false,
              true,
              null,
              finalDxcc,
              null,
              null,
              null,
              null,
              null
            );
          else
            addDeDx(
              finalGrid,
              finalDXcall,
              false,
              false,
              false,
              finalDEcall,
              finalRSTsent,
              finalTime,
              finalMsg,
              finalMode,
              finalBand,
              false,
              true,
              null,
              finalDxcc,
              null,
              null,
              null,
              null,
              null
            );
        }
      }
    }
  }

  delete rawAdiBuffer;

  delete activeAdifArray;

  redrawGrids();
  updateCountStats();
  updateLogbook();

  if (g_fromDirectCallNoFileDialog == false) {
    fileSelector.setAttribute("type", "");
    fileSelector.setAttribute("type", "file");
    fileSelector.setAttribute("accept", ".adi,");
    fileSelector.value = null;
  }
  g_fromDirectCallNoFileDialog = false;

  updateRosterWorked();
  goProcessRoster();
}

function clubLogCallback(buffer, flag, cookie) {
  var rawAdiBuffer = String(buffer);
  if (rawAdiBuffer.indexOf("Invalid login") > -1) {
    if (flag) clubTestResult.innerHTML = "Invalid";
  } else if (buffer == null) {
    if (flag) clubTestResult.innerHTML = "Unknown Error";
  } else {
    if (flag) clubTestResult.innerHTML = "Passed";
    else {
      g_fromDirectCallNoFileDialog = true;

      rawAdiBuffer = cleanAndPrepADIF("clublog.adif", rawAdiBuffer);

      tryToWriteAdifToDocFolder("clublog.adif", rawAdiBuffer);

      onAdiLoadComplete(rawAdiBuffer, true, "clublog.adif", true);
    }
  }
}

var g_isGettingClub = false;
function grabClubLog(test) {
  if (g_isGettingClub == false) {
    if (test) clubTestResult.innerHTML = "Testing";

    var postData = {
      email: clubEmail.value,
      password: clubPassword.value,
      call: clubCall.value,
    };
    getAPostBuffer(
      "https://clublog.org/getadif.php",
      clubLogCallback,
      test,
      "https",
      443,
      postData,
      ClubLogImg,
      "g_isGettingClub"
    );
  }
}

function tryToWriteAdifToDocFolder(filename, buffer, append = false) {
  var finalFile = g_appData + g_dirSeperator + filename;
  try {
    if (append == false) {
      fs.writeFileSync(finalFile, buffer);
      return buffer;
    } else {
      fs.appendFileSync(finalFile, buffer);
      return fs.readFileSync(finalFile);
    }
  } catch (e) {
    return false;
  }
}

function cleanAndPrepADIF(name, adiBuffer, reverse = false, noheader = false) {
  var rawAdiBuffer = adiBuffer;
  var regex = new RegExp("<APP_LOTW_EOF>", "i");
  rawAdiBuffer = rawAdiBuffer.replace(regex, "");
  regex = new RegExp("<EOH>", "i");
  var adiArray = rawAdiBuffer.split(regex);
  var activeAdifArray = Array();
  var activeAdifLogMode = true;
  var finalBuffer = "";

  if (noheader == false) finalBuffer = name + "<EOH>\r\n";

  if (adiArray.length > 1) {
    regex = new RegExp("<EOR>", "i");
    activeAdifArray = adiArray[1].split(regex);

    if (reverse == false) {
      for (var x = 0; x < activeAdifArray.length - 1; x++) {
        var row = activeAdifArray[x].replace(/[\n\r]/g, "");
        if (row.length > 0) finalBuffer += row + "<EOR>\r\n";
      }
    } else {
      for (var x = activeAdifArray.length - 1; x > -1; x--) {
        var row = activeAdifArray[x].replace(/[\n\r]/g, "");
        if (row.length > 0) finalBuffer += row + "<EOR>\r\n";
      }
    }
  }

  return finalBuffer;
}

function addZero(i) {
  if (i < 10) {
    i = "0" + i;
  }
  return i;
}

function getUTCString(d) {
  var Y = d.getUTCFullYear();
  var M = addZero(d.getUTCMonth() + 1);
  var D = addZero(d.getUTCDate());
  var h = addZero(d.getUTCHours());
  var m = addZero(d.getUTCMinutes());
  var s = addZero(d.getUTCSeconds());
  return Y + "-" + M + "-" + D + " " + h + ":" + m + ":" + s;
}

function lotwCallback(buffer, flag) {
  var rawAdiBuffer = String(buffer);
  if (rawAdiBuffer.indexOf("password incorrect") > -1) {
    if (flag) lotwTestResult.innerHTML = "Invalid";
  } else {
    if (flag) lotwTestResult.innerHTML = "Passed";
    else {
      var shouldAppend = false;
      g_fromDirectCallNoFileDialog = true;

      rawAdiBuffer = cleanAndPrepADIF(
        "lotw.adif",
        rawAdiBuffer,
        true,
        shouldAppend
      );

      rawAdiBuffer = tryToWriteAdifToDocFolder(
        "lotw.adif",
        rawAdiBuffer,
        shouldAppend
      );

      onAdiLoadComplete(rawAdiBuffer, true, "lotw.adif", true);
    }
  }
}

function shouldWeAppendInsteadOfCreate(filename) {
  var finalFile = g_appData + g_dirSeperator + filename;
  try {
    if (fs.existsSync(finalFile)) return true;
    else return false;
  } catch (e) {
    return false;
  }
}

function tryToDeleteLog(filename) {
  var finalFile = g_appData + g_dirSeperator + filename;
  try {
    if (fs.existsSync(finalFile)) {
      fs.unlinkSync(finalFile);
    }
  } catch (e) {}
}

var g_lotwCount = 0;

var g_isGettingLOTW = false;

function grabLOtWLog(test) {
  if (g_isGettingLOTW == false) {
    var lastQSLDateString =
      "&qso_qsorxsince=1945-01-01&qso_qslsince=1945-01-01";
    if (test == true) {
      lotwTestResult.innerHTML = "Testing";
      lastQSLDateString = "&qso_qsosince=2100-01-01";
    }

    getABuffer(
      "https://lotw.arrl.org/lotwuser/lotwreport.adi?login=" +
        lotwLogin.value +
        "&password=" +
        encodeURIComponent(lotwPassword.value) +
        "&qso_query=1&qso_qsl=no&qso_qsldetail=yes&qso_withown=yes" +
        lastQSLDateString,
      lotwCallback,
      test,
      "https",
      443,
      lotwLogImg,
      "g_isGettingLOTW",
      120000
    );
  }
}

function qrzCallback(buffer, flag) {
  if (buffer.indexOf("invalid api key") > -1) {
    if (flag) qrzTestResult.innerHTML = "Invalid";
  } else {
    if (flag) {
      qrzTestResult.innerHTML = "Passed";
    } else {
      g_fromDirectCallNoFileDialog = true;
      var htmlString = String(buffer).replace(/&lt;/g, "<");
      htmlString = htmlString.replace(/&gt;/g, ">");
      htmlString = htmlString.replace("ADIF=", "QRZ<EOH>\r\n");

      htmlString = cleanAndPrepADIF("qrz.adif", htmlString);

      tryToWriteAdifToDocFolder("qrz.adif", htmlString);

      onAdiLoadComplete(htmlString, true, "qrz.adif", true);
      delete htmlString;
    }
  }
}

var g_isGettingQRZCom = false;
function grabQrzComLog(test) {
  if (g_isGettingQRZCom == false) {
    var action = "FETCH";
    if (test) {
      qrzTestResult.innerHTML = "Testing";
      action = "STATUS";
    }

    getABuffer(
      "https://logbook.qrz.com/api?KEY=" +
        qrzApiKey.value +
        "&ACTION=" +
        action,
      qrzCallback,
      test,
      "https",
      443,
      qrzLogImg,
      "g_isGettingQRZCom",
      null
    );
  }
}

function ValidateQrzApi(inputText) {
  inputText.value = inputText.value.toUpperCase();
  if (inputText.value.length == 19) {
    var passed = false;
    var dashcount = 0;
    for (var i = 0; i < inputText.value.length; i++) {
      if (inputText.value[i] == "-") dashcount++;
    }
    if (dashcount == 3) {
      passed = true;
    }
    if (passed) {
      inputText.style.color = "#FF0";
      inputText.style.backgroundColor = "green";

      return true;
    } else {
      inputText.style.color = "white";
      inputText.style.backgroundColor = "red";
      return false;
    }
  } else {
    inputText.style.color = "white";
    inputText.style.backgroundColor = "red";

    return false;
  }
}

function ValidateText(inputText) {
  if (inputText.value.length > 0) {
    inputText.style.color = "#FF0";
    inputText.style.backgroundColor = "green";
    return true;
  } else {
    inputText.style.color = "white";
    inputText.style.backgroundColor = "red";
    return false;
  }
}

function pskCallback(buffer, flag) {
  g_fromDirectCallNoFileDialog = true;
  var rawAdiBuffer = String(buffer);

  onAdiLoadComplete(rawAdiBuffer, false);
}

var g_isGettingPsk = false;

function grabPsk24() {
  if (g_isGettingPsk == true) return;

  if (myDEcall.length > 0 && myDEcall != "NOCALL") {
    var days = 1;
    if (pskImg.src == 1) days = 7;
    getABuffer(
      "https://pskreporter.info/cgi-bin/pskdata.pl?adif=1&days=" +
        days +
        "&receiverCallsign=" +
        myDEcall.toLowerCase(),
      pskCallback,
      null,
      "https",
      443,
      pskImg,
      "g_isGettingPsk"
    );
  }
}

function adifMenuCheckBoxChanged(what) {
  g_adifLogSettings["menu"][what.id] = what.checked;
  var menuItem = what.id + "Div";
  if (what.checked == true) {
    document.getElementById(menuItem).style.display = "inline-block";
  } else {
    document.getElementById(menuItem).style.display = "none";
  }

  localStorage.adifLogSettings = JSON.stringify(g_adifLogSettings);

  if (what == buttonAdifCheckBox) setAdifStartup(loadAdifCheckBox);
}

function adifStartupCheckBoxChanged(what) {
  g_adifLogSettings["startup"][what.id] = what.checked;
  localStorage.adifLogSettings = JSON.stringify(g_adifLogSettings);

  if (what == loadAdifCheckBox) setAdifStartup(loadAdifCheckBox);
}

function adifLogQsoCheckBoxChanged(what) {
  g_adifLogSettings["qsolog"][what.id] = what.checked;
  if (what.id == "logLOTWqsoCheckBox") {
    if (what.checked == true) {
      lotwUpload.style.display = "inline-block";
      trustedTestButton.style.display = "inline-block";
    } else {
      lotwUpload.style.display = "none";
      trustedTestButton.style.display = "none";
    }
  }
  localStorage.adifLogSettings = JSON.stringify(g_adifLogSettings);
}

function adifNicknameCheckBoxChanged(what) {
  g_adifLogSettings["nickname"][what.id] = what.checked;
  if (what.id == "nicknameeQSLCheckBox") {
    if (what.checked == true) {
      eQSLNickname.style.display = "inline-block";
    } else {
      eQSLNickname.style.display = "none";
    }
  }
  localStorage.adifLogSettings = JSON.stringify(g_adifLogSettings);
}

function adifTextValueChange(what) {
  g_adifLogSettings["text"][what.id] = what.value;
  localStorage.adifLogSettings = JSON.stringify(g_adifLogSettings);
}

var fileSelector = document.createElement("input");
fileSelector.setAttribute("type", "file");
fileSelector.setAttribute("accept", ".adi,.adif");
fileSelector.onchange = function () {
  if (this.files && this.files[0]) {
    path = this.value.replace(this.files[0].name, "");
    fileSelector.setAttribute("nwworkingdir", path);

    var reader = new FileReader();
    reader.onload = function (e) {
      if (e.target.error == null) {
        onAdiLoadComplete(e.target.result, false);
      }
    };

    reader.readAsText(this.files[0]);
  }
};

function adifLoadDialog() {
  var exists = fileSelector.getAttribute("nwworkingdir");

  fileSelector.setAttribute("nwworkingdir", g_appData);

  fileSelector.click();

  return false;
}

var startupFileSelector = document.createElement("input");
startupFileSelector.setAttribute("type", "file");
startupFileSelector.setAttribute("accept", ".adi,.adif");
startupFileSelector.onchange = function () {
  if (this.files && this.files[0]) {
    for (var i in g_startupLogs) {
      if (this.value == g_startupLogs[i].file) return;
    }
    var newObject = Object();
    newObject.name = this.files[0].name;
    newObject.file = this.value;
    g_startupLogs.push(newObject);
    localStorage.startupLogs = JSON.stringify(g_startupLogs);

    path = this.value.replace(this.files[0].name, "");
    startupFileSelector.setAttribute("nwworkingdir", path);

    setAdifStartup(loadAdifCheckBox);
  }
};

function start_and_end(str) {
  if (str.length > 31) {
    return (
      str.substr(0, 16) + " ... " + str.substr(str.length - 15, str.length)
    );
  }
  return str;
}

function setFileSelectors() {
  selectStartupLink = document.getElementById("selectAdifButton");
  selectStartupLink.onclick = function () {
    var exists = startupFileSelector.getAttribute("nwworkingdir");
    if (exists == null) {
      if (g_workingIniPath.length > 1)
        startupFileSelector.setAttribute("nwworkingdir", g_appData);
    }

    startupFileSelector.click();
    return false;
  };

  selectTqsl = document.getElementById("selectTQSLButton");
  selectTqsl.onclick = function () {
    tqslFileSelector.click();
    return false;
  };
  lotwUpload.prepend(selectTqsl);
}

var tqslFileSelector = document.createElement("input");
tqslFileSelector.setAttribute("type", "file");
tqslFileSelector.setAttribute("accept", "*");
tqslFileSelector.onchange = function () {
  if (this.files && this.files[0]) {
    g_trustedQslSettings.binaryFile = this.files[0].path;
    var fs = require("fs");

    if (
      fs.existsSync(g_trustedQslSettings.binaryFile) &&
      (g_trustedQslSettings.binaryFile.endsWith("tqsl.exe") ||
        g_trustedQslSettings.binaryFile.endsWith("tqsl"))
    ) {
      g_trustedQslSettings.binaryFileValid = true;
    } else g_trustedQslSettings.binaryFileValid = false;

    if (g_trustedQslSettings.binaryFileValid == true) {
      tqslFileDiv.style.backgroundColor = "blue";
    } else {
      tqslFileDiv.style.backgroundColor = "red";
    }

    tqslFileDiv.innerHTML = "<b>" + start_and_end(this.files[0].path) + "</b>";
    localStorage.trustedQslSettings = JSON.stringify(g_trustedQslSettings);
  }
};

function loadGtQSOLogFile() {
  var fs = require("fs");

  if (fs.existsSync(g_qsoLogFile)) {
    var rawAdiBuffer = fs.readFileSync(g_qsoLogFile);

    g_fromDirectCallNoFileDialog = true;

    onAdiLoadComplete(rawAdiBuffer, false);
  }
}

function loadWsjtLogFile() {
  var fs = require("fs");
  if (fs.existsSync(g_workingIniPath + "wsjtx_log.adi")) {
    var rawAdiBuffer = fs.readFileSync(g_workingIniPath + "wsjtx_log.adi");
    g_fromDirectCallNoFileDialog = true;
    onAdiLoadComplete(rawAdiBuffer, false);
  }
}

function findTrustedQSLPaths() {
  var process = require("process");
  var base = null;

  if (g_trustedQslSettings.stationFileValid == true) {
    // double check the presence of the station_data;
    if (!fs.existsSync(g_trustedQslSettings.stationFile)) {
      g_trustedQslSettings.stationFileValid = false;
    }
  }
  if (g_trustedQslSettings.stationFileValid == false) {
    if (g_platform == "windows") {
      base = process.env.APPDATA + "\\TrustedQSL\\station_data";
      if (fs.existsSync(base)) {
        g_trustedQslSettings.stationFile = base;
        g_trustedQslSettings.stationFileValid = true;
      } else {
        base = process.env.LOCALAPPDATA + "\\TrustedQSL\\station_data";
        if (fs.existsSync(base)) {
          g_trustedQslSettings.stationFile = base;
          g_trustedQslSettings.stationFileValid = true;
        }
      }
    } else {
      base = process.env.HOME + "/.tqsl/station_data";
      if (fs.existsSync(base)) {
        g_trustedQslSettings.stationFile = base;
        g_trustedQslSettings.stationFileValid = true;
      }
    }
  }
  if (g_trustedQslSettings.stationFileValid == true) {
    var validate = false;
    var option = document.createElement("option");
    option.value = "";
    option.text = "Select a Station";
    lotwStation.appendChild(option);

    var buffer = fs.readFileSync(g_trustedQslSettings.stationFile, "UTF-8");
    parser = new DOMParser();
    xmlDoc = parser.parseFromString(buffer, "text/xml");
    var x = xmlDoc.getElementsByTagName("StationData");
    for (var i = 0; i < x.length; i++) {
      option = document.createElement("option");
      option.value = x[i].getAttribute("name");
      option.text = x[i].getAttribute("name");
      if (option.value == g_adifLogSettings.text.lotwStation) {
        option.selected = true;
        validate = true;
      }
      lotwStation.appendChild(option);
    }
    if (validate) {
      ValidateText(lotwStation);
    }
  }

  if (g_trustedQslSettings.binaryFileValid == true) {
    // double check the presence of the TrustedQSL binary;
    if (!fs.existsSync(g_trustedQslSettings.binaryFile)) {
      g_trustedQslSettings.binaryFileValid = false;
    }
  }
  if (g_trustedQslSettings.binaryFileValid == false || g_platform == "mac") {
    if (g_platform == "windows") {
      base = process.env["ProgramFiles(x86)"] + "\\TrustedQSL\\tqsl.exe";
      if (fs.existsSync(base)) {
        g_trustedQslSettings.binaryFile = base;
        g_trustedQslSettings.binaryFileValid = true;
      }
    } else if (g_platform == "mac") {
      base = "/Applications/TrustedQSL/tqsl.app/Contents/MacOS/tqsl";
      if (fs.existsSync(base)) {
        g_trustedQslSettings.binaryFile = base;
        g_trustedQslSettings.binaryFileValid = true;
      } else {
        base =
          process.env.HOME +
          "/Applications/TrustedQSL/tqsl.app/Contents/MacOS/tqsl";
        if (fs.existsSync(base)) {
          g_trustedQslSettings.binaryFile = base;
          g_trustedQslSettings.binaryFileValid = true;
        } else {
          base =
            process.env.HOME + "/Applications/tqsl.app/Contents/MacOS/tqsl";
          if (fs.existsSync(base)) {
            g_trustedQslSettings.binaryFile = base;
            g_trustedQslSettings.binaryFileValid = true;
          } else {
            base = "/Applications/tqsl.app/Contents/MacOS/tqsl";
            if (fs.existsSync(base)) {
              g_trustedQslSettings.binaryFile = base;
              g_trustedQslSettings.binaryFileValid = true;
            } else {
              base =
                process.env.HOME +
                "/Desktop/TrustedQSL/tqsl.app/Contents/MacOS/tqsl";
              if (fs.existsSync(base)) {
                g_trustedQslSettings.binaryFile = base;
                g_trustedQslSettings.binaryFileValid = true;
              } else {
                base =
                  process.env.HOME +
                  "/Applications/Ham Radio/tqsl.app/Contents/MacOS/tqsl";
                if (fs.existsSync(base)) {
                  g_trustedQslSettings.binaryFile = base;
                  g_trustedQslSettings.binaryFileValid = true;
                }
              }
            }
          }
        }
      }
    } else if (g_platform == "linux") {
      base = "/usr/bin/tqsl";
      if (fs.existsSync(base)) {
        g_trustedQslSettings.binaryFile = base;
        g_trustedQslSettings.binaryFileValid = true;
      } else {
        base = "/usr/local/bin/tqsl";
        if (fs.existsSync(base)) {
          g_trustedQslSettings.binaryFile = base;
          g_trustedQslSettings.binaryFileValid = true;
        }
      }
    }
  }
  localStorage.trustedQslSettings = JSON.stringify(g_trustedQslSettings);
}

function startupAdifLoadFunction() {
  var fs = require("fs");

  for (var i in g_startupLogs) {
    try {
      if (fs.existsSync(g_startupLogs[i].file)) {
        var rawAdiBuffer = fs.readFileSync(g_startupLogs[i].file);
        g_fromDirectCallNoFileDialog = true;
        onAdiLoadComplete(rawAdiBuffer, false);
      }
    } catch (e) {}
  }
}

function setAdifStartup(checkbox) {
  if (g_trustedQslSettings.binaryFile == null)
    g_trustedQslSettings.binaryFile = "";

  if (
    g_trustedQslSettings.binaryFile.endsWith("tqsl.exe") ||
    g_trustedQslSettings.binaryFile.endsWith("tqsl")
  ) {
    g_trustedQslSettings.binaryFileValid = true;
  } else g_trustedQslSettings.binaryFileValid = false;

  if (g_trustedQslSettings.binaryFileValid == true) {
    tqslFileDiv.style.backgroundColor = "blue";
  } else {
    tqslFileDiv.style.backgroundColor = "red";
  }
  tqslFileDiv.innerHTML =
    "<b>" + start_and_end(g_trustedQslSettings.binaryFile) + "</b>";

  if (buttonAdifCheckBox.checked || loadAdifCheckBox.checked) {
    var worker = "";
    if (g_startupLogs.length > 0) {
      worker += "<table class='darkTable'>";
      for (var i in g_startupLogs) {
        worker +=
          "<tr title='" +
          g_startupLogs[i].file +
          "'><td>" +
          g_startupLogs[i].name +
          "</td><td onclick='removeStartupLog(" +
          i +
          ")'><img src='/img/trash_24x48.png' style='height:17px;margin:-1px;margin-bottom:-3px;padding:0px;cursor:pointer'></td></tr>";
      }
      worker += "</table>";
    } else {
      worker = "No file(s) selected";
    }
    startupLogFileDiv.innerHTML = worker;
    selectFileOnStartupDiv.style.display = "block";
  } else {
    startupLogFileDiv.innerHTML = "No file(s) selected";
    startupFileSelector.setAttribute("type", "");
    startupFileSelector.setAttribute("type", "file");
    startupFileSelector.setAttribute("accept", ".adi*");
    startupFileSelector.value = null;
    selectFileOnStartupDiv.style.display = "none";
  }
}

function removeStartupLog(i) {
  if (i in g_startupLogs) {
    g_startupLogs.splice(i, 1);
    localStorage.startupLogs = JSON.stringify(g_startupLogs);
    setAdifStartup(loadAdifCheckBox);
  }
}

function startupAdifLoadCheck() {
  logEventMedia.value = g_alertSettings.logEventMedia;

  loadWsjtLogFile();

  if (loadGTCheckBox.checked == true) loadGtQSOLogFile();

  if (loadAdifCheckBox.checked == true && g_startupLogs.length > 0)
    startupAdifLoadFunction();

  if (g_mapSettings.offlineMode == false) {
    if (g_appSettings.gtFlagImgSrc == 1) showGtFlags();

    if (loadLOTWCheckBox.checked == true) grabLOtWLog(false);

    if (loadQRZCheckBox.checked == true) grabQrzComLog(false);

    if (loadClubCheckBox.checked == true) grabClubLog(false);

    if (loadPsk24CheckBox.checked == true) grabPsk24();
  }
}

function getABuffer(
  file_url,
  callback,
  flag,
  mode,
  port,
  imgToGray,
  stringOfFlag,
  timeoutX
) {
  let url = require("url");
  let http = require(mode);
  let fileBuffer = null;
  let options = null;

  {
    options = {
      host: url.parse(file_url).host,
      port: port,
      path: url.parse(file_url).path,
      method: "get",
    };
  }
  if (typeof stringOfFlag != "undefined") window[stringOfFlag] = true;
  if (typeof imgToGray != "undefined") {
    imgToGray.parentNode.style.background =
      "linear-gradient(grey 0%, black 0% 100% )";
    imgToGray.style.webkitFilter = "invert(100%) grayscale(1)";
  }

  let req = http.request(options, function (res) {
    let fsize = res.headers["content-length"];
    let cookies = null;
    if (typeof res.headers["set-cookie"] != "undefined")
      cookies = res.headers["set-cookie"];

    res
      .on("data", function (data) {
        if (fileBuffer == null) fileBuffer = data;
        else fileBuffer += data;

        if (typeof imgToGray != "undefined") {
          let percent = 0;
          if (fsize > 0) percent = parseInt((fileBuffer.length / fsize) * 100);
          else percent = parseInt(((fileBuffer.length / 100000) * 100) % 100);
          imgToGray.parentNode.style.background =
            "linear-gradient(grey " +
            percent +
            "%, black " +
            Number(percent + 10) +
            "% 100% )";
        }
      })
      .on("end", function () {
        if (typeof callback === "function") {
          // Call it, since we have confirmed it is callable
          callback(fileBuffer, flag, cookies);
        }
        if (typeof stringOfFlag != "undefined") {
          window[stringOfFlag] = false;
        }
        if (typeof imgToGray != "undefined") {
          imgToGray.parentNode.style.background = "";
          imgToGray.style.webkitFilter = "";
        }
      })
      .on("error", function () {
        if (typeof stringOfFlag != "undefined") {
          window[stringOfFlag] = false;
        }
        if (typeof imgToGray != "undefined") {
          imgToGray.parentNode.style.background = "";
          imgToGray.style.webkitFilter = "";
        }
      });
  });

  req.on("socket", function (socket) {
    socket.on("timeout", function () {
      req.abort();
    });
  });

  req.on("error", function () {
    if (typeof stringOfFlag != "undefined") {
      window[stringOfFlag] = false;
    }
    if (typeof imgToGray != "undefined") {
      imgToGray.parentNode.style.background = "";
      imgToGray.style.webkitFilter = "";
    }
  });

  req.end();
}

function getAPostBuffer(
  file_url,
  callback,
  flag,
  mode,
  port,
  theData,
  imgToGray,
  stringOfFlag
) {
  var querystring = require("querystring");
  var postData = querystring.stringify(theData);
  var url = require("url");
  var http = require(mode);
  var fileBuffer = null;

  var options = {
    host: url.parse(file_url).host,
    port: port,
    path: url.parse(file_url).path,
    method: "post",
    headers: {
      "Content-Type": "application/x-www-form-urlencoded",
      "Content-Length": postData.length,
    },
  };

  window[stringOfFlag] = true;

  if (typeof imgToGray != "undefined") {
    imgToGray.parentNode.style.background =
      "linear-gradient(grey 0%, black 0% 100% )";
    imgToGray.style.webkitFilter = "invert(100%) grayscale(1)";
  }

  var req = http.request(options, function (res) {
    var fsize = res.headers["content-length"];
    var cookies = null;
    if (typeof res.headers["set-cookie"] != "undefined")
      cookies = res.headers["set-cookie"];

    res
      .on("data", function (data) {
        if (fileBuffer == null) fileBuffer = data;
        else fileBuffer += data;

        if (typeof imgToGray != "undefined") {
          var percent = 0;
          if (fsize > 0) percent = parseInt((fileBuffer.length / fsize) * 100);
          else percent = parseInt(((fileBuffer.length / 100000) * 100) % 100);

          imgToGray.parentNode.style.background =
            "linear-gradient(grey " +
            percent +
            "%, black " +
            Number(percent + 10) +
            "% 100% )";
        }
      })
      .on("end", function () {
        if (typeof callback === "function") {
          // Call it, since we have confirmed it is callable
          callback(fileBuffer, flag, cookies);
          window[stringOfFlag] = false;
          if (typeof imgToGray != "undefined") {
            imgToGray.parentNode.style.background = "";
            imgToGray.style.webkitFilter = "";
          }
        }
      })
      .on("error", function () {
        window[stringOfFlag] = false;
        if (typeof imgToGray != "undefined") {
          imgToGray.parentNode.style.background = "";
          imgToGray.style.webkitFilter = "";
        }
      });
  });

  req.on("socket", function (socket) {
    socket.on("timeout", function () {
      req.abort();
    });
  });

  req.on("error", function (err) {
    window[stringOfFlag] = false;
    if (typeof imgToGray != "undefined") {
      imgToGray.parentNode.style.background = "";
      imgToGray.style.webkitFilter = "";
    }
  });

  req.write(postData);
  req.end();
}

function sendUdpMessage(msg, length, port, address) {
  var dgram = require("dgram");
  var socket = dgram.createSocket({ type: "udp4", reuseAddr: true });
  socket.send(msg, 0, length, port, address, (err) => {
    socket.close();
  });
}

function sendTcpMessage(msg, length, port, address) {
  var net = require("net");
  var client = new net.Socket();
  client.setTimeout(30000);
  client.connect(port, address, function () {
    client.write(msg);
  });

  client.on("close", function () {});
}

function valueToAdiField(field, value) {
  var adi = "<" + field + ":";
  adi += String(value).length + ">";
  adi += String(value) + " ";
  return adi;
}

function pad(value) {
  if (value < 10) {
    return "0" + value;
  } else {
    return value;
  }
}

function HMSfromMilli(milli) {
  var seconds = parseInt(milli / 1000);
  var days = Math.floor(seconds / (3600 * 24));
  seconds -= days * 3600 * 24;
  var hrs = Math.floor(seconds / 3600);
  seconds -= hrs * 3600;
  var mnts = Math.floor(seconds / 60);
  seconds -= mnts * 60;

  val = String(pad(hrs)) + String(pad(mnts)) + String(pad(seconds));
  return String(val);
}

function colonHMSfromMilli(milli) {
  var seconds = parseInt(milli / 1000);
  var days = Math.floor(seconds / (3600 * 24));
  seconds -= days * 3600 * 24;
  var hrs = Math.floor(seconds / 3600);
  seconds -= hrs * 3600;
  var mnts = Math.floor(seconds / 60);
  seconds -= mnts * 60;

  val = String(pad(hrs)) + ":" + String(pad(mnts)) + ":" + String(pad(seconds));
  return String(val);
}

function colonHMSfromSeconds(secondsIn) {
  var seconds = secondsIn;
  var days = Math.floor(seconds / (3600 * 24));
  seconds -= days * 3600 * 24;
  var hrs = Math.floor(seconds / 3600);
  seconds -= hrs * 3600;
  var mnts = Math.floor(seconds / 60);
  seconds -= mnts * 60;

  val = String(pad(hrs)) + ":" + String(pad(mnts)) + ":" + String(pad(seconds));
  return String(val);
}

function convertToDate(julian) {
  var DAY = 86400000;
  var HALF_DAY = DAY / 2;
  var UNIX_EPOCH_JULIAN_DATE = 2440587.5;
  var UNIX_EPOCH_JULIAN_DAY = 2440587;
  return new Date((Number(julian) - UNIX_EPOCH_JULIAN_DATE) * DAY);
}

var CLk = "25bc718451a71954cb6d0d1b50541dd45d4ba148";

var lastReportHash = null;

var g_oldStyleLogMessage = null;

function oldSendToLogger() {
  var newMessage = Object.assign({}, g_oldStyleLogMessage);

  var band = Number(newMessage.Frequency / 1000000).formatBand();

  if (
    newMessage.DXGrid.length == 0 &&
    newMessage.DXCall + band + newMessage.MO in g_liveCallsigns
  ) {
    newMessage.DXGrid = g_liveCallsigns[
      newMessage.DXCall + band + newMessage.MO
    ].grid.substr(0, 4);
  }

  var report = "<EOH>";

  report += valueToAdiField(
    "BAND",
    Number(newMessage.Frequency / 1000000).formatBand()
  );
  report += valueToAdiField("CALL", newMessage.DXCall.toUpperCase());
  report += valueToAdiField(
    "FREQ",
    Number(newMessage.Frequency / 1000000).toFixed(6)
  );
  report += valueToAdiField("MODE", newMessage.MO.toUpperCase());
  var date = convertToDate(parseInt(newMessage.DateOn));
  var dataString =
    date.getUTCFullYear() +
    ("0" + (date.getUTCMonth() + 1)).slice(-2) +
    ("0" + date.getUTCDate()).slice(-2);
  report += valueToAdiField("QSO_DATE", dataString);
  report += valueToAdiField("TIME_ON", HMSfromMilli(newMessage.TimeOn));

  date = convertToDate(parseInt(newMessage.DateOff));
  dataString =
    date.getUTCFullYear() +
    ("0" + (date.getUTCMonth() + 1)).slice(-2) +
    ("0" + date.getUTCDate()).slice(-2);
  report += valueToAdiField("QSO_DATE_OFF", dataString);
  report += valueToAdiField("TIME_OFF", HMSfromMilli(newMessage.TimeOff));

  report += valueToAdiField("RST_RCVD", newMessage.ReportRecieved);
  report += valueToAdiField("RST_SENT", newMessage.ReportSend);
  report += valueToAdiField("TX_PWR", parseInt(newMessage.TXPower));
  report += valueToAdiField("GRIDSQUARE", newMessage.DXGrid);

  if (newMessage.Comments.length > 0)
    report += valueToAdiField("COMMENT", newMessage.Comments);

  if (newMessage.Name.length > 0)
    report += valueToAdiField("NAME", newMessage.Name);

  if (newMessage.Operatorcall.length > 0) {
    report += valueToAdiField("OPERATOR", newMessage.Operatorcall);
  }

  if (newMessage.Mycall.length > 0) {
    report += valueToAdiField("STATION_CALLSIGN", newMessage.Mycall);
  } else if (myDEcall != "NOCALL" && myDEcall.length > 0)
    report += valueToAdiField("STATION_CALLSIGN", myDEcall);

  if (newMessage.Mygrid.length > 0) {
    report += valueToAdiField("MY_GRIDSQUARE", newMessage.Mygrid);
  } else if (myDEGrid.length > 1)
    report += valueToAdiField("MY_GRIDSQUARE", myDEGrid);

  report += "<EOR>";

  sendToLogger(report);
}

var g_adifLookupMap = {
  name: "NAME",
  iota: "IOTA",
  sota: "SOTA_REF",
  continent: "CONT",
  cqzone: "CQZ",
  ituzone: "ITUZ",
  email: "EMAIL",
  county: "CNTY",
};

function sendToLogger(ADIF) {
  let regex = new RegExp("<EOH>", "i");
  let record = parseADIFRecord(ADIF.split(regex)[1]);
  let localMode = record["MODE"];

  if (localMode == "MFSK" && "SUBMODE" in record) {
    localMode = record["SUBMODE"];
  }

  if (
    (!("GRIDSQUARE" in record) || record["GRIDSQUARE"].length == 0) &&
    record["CALL"] + record["BAND"] + localMode in g_liveCallsigns
  ) {
    record["GRIDSQUARE"] = g_liveCallsigns[
      record["CALL"] + record["BAND"] + localMode
    ].grid.substr(0, 4);
  }

  if ("TX_PWR" in record) {
    record["TX_PWR"] = String(parseInt(record["TX_PWR"]));
  }

  if (
    (!("STATION_CALLSIGN" in record) ||
      record["STATION_CALLSIGN"].length == 0) &&
    myDEcall != "NOCALL" &&
    myDEcall.length > 0
  ) {
    record["STATION_CALLSIGN"] = myDEcall;
  }

  if (
    (!("MY_GRIDSQUARE" in record) || record["MY_GRIDSQUARE"].length == 0) &&
    myDEGrid.length > 1
  ) {
    record["MY_GRIDSQUARE"] = myDEGrid;
  }

  if (!("DXCC" in record)) {
    let dxcc = callsignToDxcc(record["CALL"]);
    if (dxcc == -1) dxcc = 0;
    record["DXCC"] = String(dxcc);
  }

  if (!("COUNTRY" in record) && Number(record["DXCC"]) > 0) {
    record["COUNTRY"] = g_dxccToADIFName[Number(record["DXCC"])];
  }

  if (g_appSettings.lookupMerge == true) {
    let request = g_Idb
      .transaction(["lookups"], "readwrite")
      .objectStore("lookups")
      .get(record["CALL"]);

    request.onsuccess = function (event) {
      if (request.result) {
        let lookup = request.result;
        for (let key in lookup) {
          if (key in g_adifLookupMap) {
            record[g_adifLookupMap[key]] = lookup[key];
          }
        }
        if ("GRIDSQUARE" in record && "grid" in lookup) {
          if (
            record["GRIDSQUARE"].substr(0, 4) == lookup["grid"].substr(0, 4)
          ) {
            record["GRIDSQUARE"] = lookup["grid"];
          }
        }
        if (
          g_appSettings.lookupMissingGrid &&
          "grid" in lookup &&
          (!("GRIDSQUARE" in record) || record["GRIDSQUARE"].length == 0)
        ) {
          record["GRIDSQUARE"] = lookup["grid"];
        }
      }
      finishSendingReport(record, localMode);
    };

    request.onerror = function (event) {
      finishSendingReport(record, localMode);
    };
  } else {
    finishSendingReport(record, localMode);
  }
}

function finishSendingReport(record, localMode) {
  let report = "";

  let reportHash = record["CALL"] + record["MODE"] + localMode;

  for (let key in record) {
    report += "<" + key + ":" + record[key].length + ">" + record[key] + " ";
  }
  report += "<EOR>";

  if (reportHash != lastReportHash) {
    lastReportHash = reportHash;

    if (
      g_N1MMSettings.enable == true &&
      g_N1MMSettings.port > 1024 &&
      g_N1MMSettings.ip.length > 4
    ) {
      sendUdpMessage(
        report,
        report.length,
        parseInt(g_N1MMSettings.port),
        g_N1MMSettings.ip
      );
      addLastTraffic("<font style='color:white'>Logged to N1MM</font>");
    }

    if (
      g_log4OMSettings.enable == true &&
      g_log4OMSettings.port > 1024 &&
      g_log4OMSettings.ip.length > 4
    ) {
      sendUdpMessage(
        "ADD " + report,
        report.length + 4,
        parseInt(g_log4OMSettings.port),
        g_log4OMSettings.ip
      );
      addLastTraffic("<font style='color:white'>Logged to Log4OM</font>");
    }

    try {
      onAdiLoadComplete("GT<EOH>" + report);
    } catch (e) {
      addLastTraffic("<font style='color:red'>Exception Internal Log</font>");
    }
    try {
      // Log worthy
      if (logGTqsoCheckBox.checked == true) {
        var fs = require("fs");
        fs.appendFileSync(g_qsoLogFile, report + "\r\n");
        addLastTraffic(
          "<font style='color:white'>Logged to GridTracker backup</font>"
        );
      }
    } catch (e) {
      addLastTraffic(
        "<font style='color:red'>Exception GridTracker backup</font>"
      );
    }

    try {
      sendQrzLogEntry(report);
    } catch (e) {
      addLastTraffic("<font style='color:red'>Exception QRZ Log</font>");
    }

    try {
      sendClubLogEntry(report);
    } catch (e) {
      addLastTraffic("<font style='color:red'>Exception ClubLog Log</font>");
    }

    try {
      sendHrdLogEntry(report);
    } catch (e) {
      addLastTraffic("<font style='color:red'>Exception HrdLog.net Log</font>");
    }

    try {
      sendCloudlogEntry(report);
    } catch (e) {
      addLastTraffic("<font style='color:red'>Exception Cloudlog Log</font>");
    }

    if (
      g_acLogSettings.enable == true &&
      g_acLogSettings.port > 0 &&
      g_acLogSettings.ip.length > 4
    ) {
      try {
        sendACLogMessage(record, g_acLogSettings.port, g_acLogSettings.ip);
        addLastTraffic("<font style='color:white'>Logged to N3FJP</font>");
      } catch (e) {
        addLastTraffic("<font style='color:red'>Exception N3FJP Log</font>");
      }
    }

    if (
      g_dxkLogSettings.enable == true &&
      g_dxkLogSettings.port > 0 &&
      g_dxkLogSettings.ip.length > 4
    ) {
      try {
        sendDXKeeperLogMessage(
          report,
          g_dxkLogSettings.port,
          g_dxkLogSettings.ip
        );
        addLastTraffic("<font style='color:white'>Logged to DXKeeper</font>");
      } catch (e) {
        addLastTraffic("<font style='color:red'>Exception DXKeeper Log</font>");
      }
    }

    if (
      g_HRDLogbookLogSettings.enable == true &&
      g_HRDLogbookLogSettings.port > 0 &&
      g_HRDLogbookLogSettings.ip.length > 4
    ) {
      try {
        sendHRDLogbookEntry(
          record,
          g_HRDLogbookLogSettings.port,
          g_HRDLogbookLogSettings.ip
        );
        addLastTraffic(
          "<font style='color:white'>Logged to HRD Logbook</font>"
        );
      } catch (e) {
        addLastTraffic("<font style='color:red'>Exception HRD Log</font>");
      }
    }

    try {
      sendLotwLogEntry(report);
    } catch (e) {
      addLastTraffic("<font style='color:red'>Exception LoTW Log</font>");
    }

    if (
      logeQSLQSOCheckBox.checked == true &&
      nicknameeQSLCheckBox.checked == true &&
      eQSLNickname.value.trim().length > 0
    ) {
      record["APP_EQSL_QTH_NICKNAME"] = eQSLNickname.value.trim();
      report = "";
      for (var key in record) {
        report +=
          "<" + key + ":" + record[key].length + ">" + record[key] + " ";
      }
      report += "<EOR>";
    }

    try {
      sendeQSLEntry(report);
    } catch (e) {
      addLastTraffic("<font style='color:red'>Exception LoTW Log</font>");
    }

    try {
      alertLogMessage();
    } catch (e) {
      addLastTraffic("<font style='color:red'>Exception Alert Log</font>");
    }

    if (lookupCloseLog.checked == true) {
      try {
        openLookupWindow(false);
      } catch (e) {
        addLastTraffic("<font style='color:red'>Exception Hide Lookup</font>");
      }
    }
  }

  return report;
}

function alertLogMessage() {
  if (logEventMedia.value != "none") {
    playAlertMediaFile(logEventMedia.value);
  }
}

function eqslCallback(buffer, flag) {
  var result = String(buffer);
  if (flag) {
    if (result.indexOf("No such Username/Password found") != -1) {
      eQSLTestResult.innerHTML = "Bad<br/>Password<br/>or<br/>Nickname";
      logeQSLQSOCheckBox.checked = false;
      adifLogQsoCheckBoxChanged(logeQSLQSOCheckBox);
    } else if (result.indexOf("No such Callsign found") != -1) {
      eQSLTestResult.innerHTML = "Unknown<br/>Callsign";
      logeQSLQSOCheckBox.checked = false;
      adifLogQsoCheckBoxChanged(logeQSLQSOCheckBox);
    } else if (result.indexOf("Your ADIF log file has been built") != -1) {
      eQSLTestResult.innerHTML = "Passed";
    } else if (
      result.indexOf("specify the desired User by using the QTHNickname") != -1
    ) {
      eQSLTestResult.innerHTML = "QTH Nickname<br/>Needed";
    } else {
      eQSLTestResult.innerHTML = "Unknown<br/>Error";
      logeQSLQSOCheckBox.checked = false;
      adifLogQsoCheckBoxChanged(logeQSLQSOCheckBox);
    }
  } else {
    if (result.indexOf("Error: No match on eQSL_User/eQSL_Pswd") != -1) {
      addLastTraffic(
        "<font style='color:red'>Fail log eQSL.cc (credentials)</font>"
      );
    }
    if (
      result.indexOf("specify the desired User by using the QTHNickname") != -1
    ) {
      addLastTraffic(
        "<font style='color:red'>Fail log eQSL.cc (nickname)</font>"
      );
    } else if (result.indexOf("Result: 0 out of 1 records") != -1) {
      addLastTraffic("<font style='color:red'>Fail log eQSL.cc (dupe)</font>");
    } else if (result.indexOf("Result: 1 out of 1 records added") != -1) {
      addLastTraffic("<font style='color:white'>Logged to eQSL.cc</font>");
    } else {
      addLastTraffic("<font style='color:red'>Fail log eQSL.cc (?)</font>");
    }
  }
}

function eQSLTest(test) {
  if (g_mapSettings.offlineMode == true) return;

  eQSLTestResult.innerHTML = "Testing";

  var fUrl =
    "https://www.eQSL.cc/qslcard/DownloadInBox.cfm?UserName=" +
    encodeURIComponent(eQSLUser.value) +
    "&Password=" +
    encodeURIComponent(eQSLPassword.value) +
    "&RcvdSince=2020101";

  if (nicknameeQSLCheckBox.checked == true)
    fUrl += "&QTHNickname=" + encodeURIComponent(eQSLNickname.value);
  getABuffer(fUrl, eqslCallback, true, "https", 443);
}

function sendeQSLEntry(report) {
  if (g_mapSettings.offlineMode == true) return;

  if (logeQSLQSOCheckBox.checked == true) {
    var pid = "GridTracker";
    var pver = String(gtVersion);
    var header = "<PROGRAMID:" + pid.length + ">" + pid + "\r\n";
    header += "<PROGRAMVERSION:" + pver.length + ">" + pver + "\r\n";
    header += "<EOH>\r\n";
    var eReport = encodeURIComponent(header + report);
    var fUrl =
      "https://www.eQSL.cc/qslcard/importADIF.cfm?ADIFData=" +
      eReport +
      "&EQSL_USER=" +
      encodeURIComponent(eQSLUser.value) +
      "&EQSL_PSWD=" +
      encodeURIComponent(eQSLPassword.value);
    getABuffer(fUrl, eqslCallback, false, "https", 443);
  }
}

function testTrustedQSL(test) {
  if (g_mapSettings.offlineMode == true) {
    lotwTestResult.innerHTML = "Currently<br/>offline";
    return;
  }

  if (
    logLOTWqsoCheckBox.checked == true &&
    g_trustedQslSettings.binaryFileValid == true &&
    g_trustedQslSettings.stationFileValid == true &&
    lotwStation.value.length > 0
  ) {
    lotwTestResult.innerHTML = "Testing Upload";

    var child_process = require("child_process");
    var options = Array();
    options.push("-q");
    options.push("-v");

    child_process.execFile(
      g_trustedQslSettings.binaryFile,
      options,
      (error, stdout, stderr) => {
        if (error) {
          lotwTestResult.innerHTML = "Error encountered";
        }
        lotwTestResult.innerHTML = stderr;
      }
    );
    return;
  } else {
    var worker = "";
    if (g_trustedQslSettings.binaryFileValid == false)
      worker += "Invalid tqsl executable<br/>";
    if (g_trustedQslSettings.stationFileValid == false)
      worker += "TrustQSL not installed<br/>";
    if (!ValidateText(lotwTrusted)) worker += "TQSL Password missing<br/>";
    if (!ValidateText(lotwStation)) worker += "Select Station<br/>";
    lotwTestResult.innerHTML = worker;
    return;
  }
}
var g_trustTempPath = "";

function sendLotwLogEntry(report) {
  if (g_mapSettings.offlineMode == true) return;

  if (
    logLOTWqsoCheckBox.checked == true &&
    g_trustedQslSettings.binaryFileValid == true &&
    g_trustedQslSettings.stationFileValid == true &&
    lotwStation.value.length > 0
  ) {
    var header =
      "Generated " + userTimeString(null) + " for " + myDEcall + "\r\n\r\n";
    var pid = "GridTracker";
    var pver = String(gtVersion);
    header += "<PROGRAMID:" + pid.length + ">" + pid + "\r\n";
    header += "<PROGRAMVERSION:" + pver.length + ">" + pver + "\r\n";
    header += "<EOH>\r\n";
    var finalLog = header + report + "\r\n";

    g_trustTempPath = os.tmpdir() + g_dirSeperator + unique(report) + ".adif";
    fs.writeFileSync(g_trustTempPath, finalLog);

    var child_process = require("child_process");
    var options = Array();
    options.push("-a");
    options.push("all");
    options.push("-l");
    options.push(lotwStation.value);
    if (lotwTrusted.value.length > 0) {
      options.push("-p");
      options.push(lotwTrusted.value);
    }
    options.push("-q");
    options.push("-x");
    options.push("-d");
    options.push("-u");
    options.push(g_trustTempPath);

    child_process.execFile(
      g_trustedQslSettings.binaryFile,
      options,
      (error, stdout, stderr) => {
        if (stderr.indexOf("Final Status: Success") < 0) {
          alert(stderr);
          addLastTraffic("<font style='color:red'>Fail log to TQSL</font>");
        } else {
          addLastTraffic("<font style='color:white'>Logged to TQSL</font>");
        }
        fs.unlinkSync(g_trustTempPath);
      }
    );
  }
}

function n1mmLoggerChanged() {
  g_N1MMSettings.enable = buttonN1MMCheckBox.checked;
  g_N1MMSettings.ip = N1MMIpInput.value;
  g_N1MMSettings.port = N1MMPortInput.value;

  localStorage.N1MMSettings = JSON.stringify(g_N1MMSettings);
}

function log4OMLoggerChanged() {
  g_log4OMSettings.enable = buttonLog4OMCheckBox.checked;
  g_log4OMSettings.ip = log4OMIpInput.value;
  g_log4OMSettings.port = log4OMPortInput.value;

  localStorage.log4OMSettings = JSON.stringify(g_log4OMSettings);
}

function acLogLoggerChanged() {
  g_acLogSettings.enable = buttonacLogCheckBox.checked;
  g_acLogSettings.ip = acLogIpInput.value;
  g_acLogSettings.port = acLogPortInput.value;

  localStorage.acLogSettings = JSON.stringify(g_acLogSettings);
}

function dxkLogLoggerChanged() {
  g_dxkLogSettings.enable = buttondxkLogCheckBox.checked;
  g_dxkLogSettings.ip = dxkLogIpInput.value;
  g_dxkLogSettings.port = dxkLogPortInput.value;

  localStorage.dxkLogSettings = JSON.stringify(g_dxkLogSettings);
}

function hrdLogbookLoggerChanged() {
  g_HRDLogbookLogSettings.enable = buttonHrdLogbookCheckBox.checked;
  g_HRDLogbookLogSettings.ip = hrdLogbookIpInput.value;
  g_HRDLogbookLogSettings.port = hrdLogbookPortInput.value;

  localStorage.HRDLogbookLogSettings = JSON.stringify(g_HRDLogbookLogSettings);
}

function CloudUrlErrorCallback(
  file_url,
  callback,
  flag,
  mode,
  port,
  theData,
  timeoutMs,
  timeoutCallback,
  message
) {
  CloudlogTestResult.innerHTML = message;
}

function CloudlogSendLogResult(buffer, flag) {
  if (flag && flag == true) {
    if (buffer) {
      if (buffer.indexOf("missing api key") > -1) {
        CloudlogTestResult.innerHTML = "API Key Invalid";
      } else if (buffer.indexOf("created") > -1) {
        CloudlogTestResult.innerHTML = "Passed";
      } else {
        CloudlogTestResult.innerHTML = "Invalid Response";
      }
    } else {
      CloudlogTestResult.innerHTML = "Invalid Response";
    }
  } else {
    if (buffer && buffer.indexOf("created") > -1)
      addLastTraffic("<font style='color:white'>Logged to Cloudlog</font>");
    else addLastTraffic("<font style='color:red'>Fail log to Cloudlog</font>");
  }
}

function qrzSendLogResult(buffer, flag) {
  if (typeof buffer != "undefined" && buffer != null) {
    var data = String(buffer);
    var kv = data.split("&");
    if (kv.length > 0) {
      var arrData = Object();
      for (var x in kv) {
        var split = kv[x].split("=");
        arrData[split[0]] = split[1];
      }
      if (
        typeof arrData["RESULT"] == "undefined" ||
        arrData["RESULT"] != "OK"
      ) {
        alert(
          "Error uploading QSO to QRZ.com (" +
            (arrData["REASON"] || "Unknown error") +
            ")"
        );
        addLastTraffic("<font style='color:red'>Fail log to QRZ.com</font>");
      } else
        addLastTraffic("<font style='color:white'>Logged to QRZ.com</font>");
    }
  } else alert("Error uploading QSO to QRZ.com (No response)");
}

function postDialogRetryCallback(
  file_url,
  callback,
  flag,
  mode,
  port,
  theData,
  timeoutMs,
  timeoutCallback,
  who
) {
  if (window.confirm("Error sending QSO to " + who + ", retry?")) {
    getPostBuffer(
      file_url,
      callback,
      flag,
      mode,
      port,
      theData,
      timeoutMs,
      postDialogRetryCallback,
      who
    );
  }
}

function postRetryErrorCallaback(
  file_url,
  callback,
  flag,
  mode,
  port,
  theData,
  timeoutMs,
  timeoutCallback,
  who
) {
  getPostBuffer(
    file_url,
    callback,
    flag,
    mode,
    port,
    theData,
    timeoutMs,
    postDialogRetryCallback,
    who
  );
}

function sendQrzLogEntry(report) {
  if (g_mapSettings.offlineMode == true) return;

  if (logQRZqsoCheckBox.checked == true && ValidateQrzApi(qrzApiKey)) {
    if (typeof nw != "undefined") {
      var postData = {
        KEY: qrzApiKey.value,
        ACTION: "INSERT",
        ADIF: report,
      };
      getPostBuffer(
        "https://logbook.qrz.com/api",
        qrzSendLogResult,
        null,
        "https",
        443,
        postData,
        30000,
        postRetryErrorCallaback,
        "QRZ.com"
      );
    }
  }
}

function clubLogQsoResult(buffer, flag) {
  addLastTraffic("<font style='color:white'>Logged to ClubLog.org</font>");
}

function sendClubLogEntry(report) {
  if (g_mapSettings.offlineMode == true) return;

  if (logClubqsoCheckBox.checked == true) {
    if (typeof nw != "undefined") {
      var postData = {
        email: clubEmail.value,
        password: clubPassword.value,
        callsign: clubCall.value,
        adif: report,
        api: CLk,
      };

      getPostBuffer(
        "https://clublog.org/realtime.php",
        clubLogQsoResult,
        null,
        "https",
        443,
        postData,
        30000,
        postRetryErrorCallaback,
        "ClubLog.org"
      );
    }
  }
}

function sendCloudlogEntry(report) {
  if (g_mapSettings.offlineMode == true) return;

  if (logCloudlogQSOCheckBox.checked == true) {
    if (typeof nw != "undefined") {
      var postData = { key: CloudlogAPI.value, type: "adif", string: report };
      getPostJSONBuffer(
        CloudlogURL.value,
        CloudlogSendLogResult,
        null,
        "https",
        80,
        postData,
        10000,
        CloudUrlErrorCallback,
        "Failed to Send"
      );
    }
  }
}

function hrdSendLogResult(buffer, flag) {
  if (flag && flag == true) {
    if (buffer.indexOf("Unknown user") > -1) {
      HRDLogTestResult.innerHTML = "Failed";
      logHRDLOGqsoCheckBox.checked = false;
      adifLogQsoCheckBoxChanged(logHRDLOGqsoCheckBox);
    } else HRDLogTestResult.innerHTML = "Passed";
  } else {
    if (buffer.indexOf("Unknown user") == -1)
      addLastTraffic("<font style='color:white'>Logged to HRDLOG.net</font>");
    else
      addLastTraffic("<font style='color:red'>Fail log to HRDLOG.net</font>");
  }
}

function sendHrdLogEntry(report) {
  if (g_mapSettings.offlineMode == true) return;

  if (logHRDLOGqsoCheckBox.checked == true) {
    if (typeof nw != "undefined") {
      var postData = {
        Callsign: HRDLOGCallsign.value,
        Code: HRDLOGUploadCode.value,
        App: "GridTracker " + gtVersion,
        ADIFData: report,
      };
      getPostBuffer(
        "https://www.hrdlog.net/NewEntry.aspx",
        hrdSendLogResult,
        null,
        "https",
        443,
        postData,
        30000,
        postRetryErrorCallaback,
        "HRDLog.net"
      );
    }
  }
}

function hrdCredentialTest(test) {
  if (test && test == true) {
    HRDLogTestResult.innerHTML = "Testing";

    if (typeof nw != "undefined") {
      var postData = {
        Callsign: HRDLOGCallsign.value,
        Code: HRDLOGUploadCode.value,
      };
      getPostBuffer(
        "https://www.hrdlog.net/NewEntry.aspx",
        hrdSendLogResult,
        test,
        "https",
        443,
        postData
      );
    }
  }
}

function ClublogTest(test) {
  if (test && test == true) {
    CloudlogTestResult.innerHTML = "Testing";

    if (typeof nw != "undefined") {
      var postData = { key: CloudlogAPI.value, type: "adif", string: "<eor>" };
      getPostJSONBuffer(
        CloudlogURL.value,
        CloudlogSendLogResult,
        test,
        "https",
        80,
        postData,
        10000,
        CloudUrlErrorCallback,
        "No Response<br/>or</br>Timeout"
      );
    }
  }
}

function getPostJSONBuffer(
  file_url,
  callback,
  flag,
  mode,
  port,
  theData,
  timeoutMs,
  timeoutCallback,
  who
) {
  try {
    var postData = JSON.stringify(theData);
    var url = require("url");
    var protocol = url.parse(file_url).protocol;
    var http = require(protocol.replace(":", ""));
    var fileBuffer = null;
    var options = {
      host: url.parse(file_url).hostname,
      port: url.parse(file_url).port,
      path: url.parse(file_url).path,
      method: "post",
      headers: {
        "Content-Type": "application/json",
        "Content-Length": postData.length,
      },
    };
    var req = http.request(options, function (res) {
      var fsize = res.headers["content-length"];
      var cookies = null;
      if (typeof res.headers["set-cookie"] != "undefined")
        cookies = res.headers["set-cookie"];
      res
        .on("data", function (data) {
          if (fileBuffer == null) fileBuffer = data;
          else fileBuffer += data;
        })
        .on("end", function () {
          if (typeof callback === "function") {
            // Call it, since we have confirmed it is callable
            callback(fileBuffer, flag, cookies);
          }
        })
        .on("error", function () {});
    });
    if (typeof timeoutMs == "number" && timeoutMs > 0) {
      req.on("socket", function (socket) {
        socket.setTimeout(timeoutMs);
        socket.on("timeout", function () {
          req.abort();
        });
      });
      req.on("error", function (err) {
        if (typeof timeoutCallback != "undefined")
          timeoutCallback(
            file_url,
            callback,
            flag,
            mode,
            80,
            theData,
            timeoutMs,
            timeoutCallback,
            who
          );
      });
    }
    req.write(postData);
    req.end();
  } catch (e) {
    if (typeof timeoutCallback != "undefined")
      timeoutCallback(
        file_url,
        callback,
        flag,
        mode,
        80,
        theData,
        timeoutMs,
        timeoutCallback,
        "Invalid Url"
      );
  }
}

function valueToXmlField(field, value) {
  var adi = "<" + field + ">";
  adi += String(value);
  adi += "</" + field + ">";
  return adi;
}

function aclUpdateControlValue(control, value) {
  return (
    valueToXmlField(
      "CMD",
      "<UPDATE>" +
        valueToXmlField("CONTROL", control) +
        valueToXmlField("VALUE", value)
    ) + "\r\n"
  );
}

function aclAction(action) {
  return (
    valueToXmlField("CMD", "<ACTION>" + valueToXmlField("VALUE", action)) +
    "\r\n"
  );
}

function adifField(record, key) {
  if (key in record) return record[key];
  else return "";
}
function sendACLogMessage(record, port, address) {
  var report = "";

  report += aclAction("CLEAR");
  report += aclUpdateControlValue("TXTENTRYBAND", adifField(record, "BAND"));
  report += aclUpdateControlValue("TXTENTRYCALL", adifField(record, "CALL"));
  report += aclAction("CALLTAB");
  report += aclUpdateControlValue(
    "TXTENTRYFREQUENCY",
    adifField(record, "FREQ")
  );
  if (adifField(record, "SUBMODE").length > 0)
    report += aclUpdateControlValue(
      "TXTENTRYMODE",
      adifField(record, "SUBMODE")
    );
  else
    report += aclUpdateControlValue("TXTENTRYMODE", adifField(record, "MODE"));

  var date = adifField(record, "QSO_DATE");
  var dataString =
    date.substr(0, 4) + "/" + date.substr(4, 2) + "/" + date.substr(6);

  report += aclUpdateControlValue("TXTENTRYDATE", dataString);

  var timeVal = adifField(record, "TIME_ON");
  var whenString =
    timeVal.substr(0, 2) +
    ":" +
    timeVal.substr(2, 2) +
    ":" +
    timeVal.substr(4, 2);
  report += aclUpdateControlValue("TXTENTRYTIMEON", whenString);

  timeVal = adifField(record, "TIME_OFF");
  whenString =
    timeVal.substr(0, 2) +
    ":" +
    timeVal.substr(2, 2) +
    ":" +
    timeVal.substr(4, 2);
  report += aclUpdateControlValue("TXTENTRYTIMEOFF", whenString);

  report += aclUpdateControlValue(
    "TXTENTRYRSTR",
    adifField(record, "RST_RCVD")
  );
  report += aclUpdateControlValue(
    "TXTENTRYRSTS",
    adifField(record, "RST_SENT")
  );
  report += aclUpdateControlValue("TXTENTRYPOWER", adifField(record, "TX_PWR"));
  report += aclUpdateControlValue(
    "TXTENTRYGRID",
    adifField(record, "GRIDSQUARE")
  );
  report += aclUpdateControlValue(
    "TXTENTRYCOMMENTS",
    adifField(record, "COMMENT")
  );
  report += aclUpdateControlValue("TXTENTRYNAMER", adifField(record, "NAME"));
  report += aclUpdateControlValue("TXTENTRYIOTA", adifField(record, "IOTA"));
  report += aclUpdateControlValue(
    "TXTENTRYCONTINENT",
    adifField(record, "CONT")
  );
  report += aclUpdateControlValue("TXTENTRYITUZ", adifField(record, "ITUZ"));
  report += aclUpdateControlValue("TXTENTRYCQZONE", adifField(record, "CQZ"));
  report += aclUpdateControlValue("TXTENTRYCOUNTYR", adifField(record, "CNTY"));

  var sentSpcNum = false;
  if (adifField(record, "SRX").length > 0) {
    report += aclUpdateControlValue(
      "TXTENTRYSERIALNOR",
      adifField(record, "SRX")
    );
  } else if (adifField(record, "CONTEST_ID").length > 0) {
    report += aclUpdateControlValue(
      "TXTENTRYSPCNUM",
      adifField(record, "SRX_STRING")
    );
    sentSpcNum = true;
    report += aclUpdateControlValue(
      "TXTENTRYCLASS",
      adifField(record, "CLASS")
    );
    report += aclUpdateControlValue(
      "TXTENTRYSECTION",
      adifField(record, "ARRL_SECT")
    );
  }

  if (adifField(record, "STATE").length > 0) {
    report += aclUpdateControlValue(
      "TXTENTRYSTATE",
      adifField(record, "STATE")
    );
    if (sentSpcNum == false)
      report += aclUpdateControlValue(
        "TXTENTRYSPCNUM",
        adifField(record, "STATE")
      );
  }

  report += aclAction("ENTER");

  sendTcpMessage(report, report.length, port, address);
}

function sendDXKeeperLogMessage(newMessage, port, address) {
  var report = "";

  report += valueToAdiField("command", "log");
  report += valueToAdiField("parameters", newMessage);
  report += "\r\n";

  sendTcpMessage(report, report.length, Number(port) + 1, address);
}

function parseADIFRecord(adif) {
  var regex = new RegExp("<EOR>", "i");
  var newLine = adif.split(regex);
  var line = newLine[0].trim(); // Catch the naughty case of someone sending two records at the same time
  var record = {};

  // because strings are not escaped for adif.. ie:  :'s and <'s .. we have to walk from left to right
  // cheesy, but damn i'm tired of parsing things
  var x = 0;
  while (line.length > 0) {
    while (line.charAt(0) != "<" && line.length > 0) {
      line = line.substr(1);
    }
    if (line.length > 0) {
      line = line.substr(1);
      var where = line.indexOf(":");
      if (where != -1) {
        var fieldName = line.substr(0, where).toUpperCase();
        line = line.substr(fieldName.length + 1);
        var fieldLength = parseInt(line);
        var end = line.indexOf(">");
        if (end > 0) {
          line = line.substr(end + 1);
          var fieldValue = line.substr(0, fieldLength);
          line = line.substr(fieldLength);
          record[fieldName] = fieldValue;
        }
      }
    }
  }

  return record;
}

function sendHRDLogbookEntry(report, port, address) {
  var command = "ver\rdb add {";
  var items = Object.assign({}, report);

  items["FREQ"] = items["FREQ"].split(".").join("");

  for (var item in items) {
    command += item + '="' + items[item] + '" ';
  }

  command += "}\rexit\r";

  sendTcpMessage(command, command.length, Number(port), address);
}
