text: /*
   * Copyright (c) 2019 - present Adobe. All rights reserved.
   *
   * Permission is hereby granted, free of charge, to any person obtaining a
   * copy of this software and associated documentation files (the "Software"),
   * to deal in the Software without restriction, including without limitation
   * the rights to use, copy, modify, merge, publish, distribute, sublicense,
   * and/or sell copies of the Software, and to permit persons to whom the
   * Software is furnished to do so, subject to the following conditions:
   *
   * The above copyright notice and this permission notice shall be included in
   * all copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
   * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
   * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
   * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
   * DEALINGS IN THE SOFTWARE.
   *
   */
  
  /*eslint no-console: 0*/
  /*eslint max-len: ["error", { "code": 200 }]*/
  define(function (require, exports, module) {
      "use strict";
  
      var ToolingInfo = JSON.parse(require("text!languageTools/ToolingInfo.json")),
          NodeDomain = require("utils/NodeDomain"),
          FileUtils = require("file/FileUtils"),
          EventDispatcher = require("utils/EventDispatcher"),
          BracketsToNodeInterface = require("languageTools/BracketsToNodeInterface").BracketsToNodeInterface;
  
      EventDispatcher.makeEventDispatcher(exports);
      //Register paths required for Language Client and also register default brackets capabilities.
      var _bracketsPath = FileUtils.getNativeBracketsDirectoryPath();
      // The native directory path ends with either "test" or "src".
      _bracketsPath = _bracketsPath.replace(/\/test$/, "/src"); // convert from "test" to "src"
  
      var _modulePath = FileUtils.getNativeModuleDirectoryPath(module),
          _nodePath = "node/RegisterLanguageClientInfo",
          _domainPath = [_bracketsPath, _modulePath, _nodePath].join("/"),
          clientInfoDomain = null,
          clientInfoLoadedPromise = null,
          //Clients that have to be loaded once the LanguageClient info is successfully loaded on the
          //node side.
          pendingClientsToBeLoaded = [];
  
      function syncPrefsWithDomain(languageToolsPrefs) {
          if (clientInfoDomain) {
              clientInfoDomain.exec("syncPreferences", languageToolsPrefs);
          }
      }
  
      function _createNodeDomain(domainName, domainPath) {
          return new NodeDomain(domainName, domainPath);
      }
  
      function loadLanguageClientDomain(clientName, domainPath) {
          //generate a random hash name for the domain, this is the client id
          var domainName = clientName,
              result = $.Deferred(),
              languageClientDomain = _createNodeDomain(domainName, domainPath);
  
          if (languageClientDomain) {
              languageClientDomain.promise()
                  .done(function () {
                      console.log(domainPath + " domain successfully created");
                      result.resolve(languageClientDomain);
                  })
                  .fail(function (err) {
                      console.error(domainPath + " domain could not be created.");
                      result.reject();
                  });
          } else {
              console.error(domainPath + " domain could not be created.");
              result.reject();
          }
  
          return result;
      }
  
      function createNodeInterfaceForDomain(languageClientDomain) {
          var nodeInterface = new BracketsToNodeInterface(languageClientDomain);
  
          return nodeInterface;
      }
  
      function _clientLoader(clientName, clientFilePath, clientPromise) {
          loadLanguageClientDomain(clientName, clientFilePath)
              .then(function (languageClientDomain) {
                  var languageClientInterface = createNodeInterfaceForDomain(languageClientDomain);
  
                  clientPromise.resolve({
                      name: clientName,
                      interface: languageClientInterface
                  });
              }, clientPromise.reject);
      }
  
      function initiateLanguageClient(clientName, clientFilePath) {
          var result = $.Deferred();
  
          //Only load clients after the LanguageClient Info has been initialized
          if (!clientInfoLoadedPromise || clientInfoLoadedPromise.state() === "pending") {
              var pendingClient = {
                  load: _clientLoader.bind(null, clientName, clientFilePath, result)
              };
              pendingClientsToBeLoaded.push(pendingClient);
          } else {
              _clientLoader(clientName, clientFilePath, result);
          }
  
          return result;
      }
  
      /**
       * This function passes Brackets's native directory path as well as the tooling commands
       * required by the LanguageClient node module. This information is then maintained in memory
       * in the node process server for succesfully loading and functioning of all language clients
       * since it is a direct dependency.
       */
      function sendLanguageClientInfo() {
          //Init node with Information required by Language Client
          clientInfoLoadedPromise = clientInfoDomain.exec("initialize", _bracketsPath, ToolingInfo);
  
          function logInitializationError() {
              console.error("Failed to Initialize LanguageClient Module Information.");
          }
  
          //Attach success and failure function for the clientInfoLoadedPromise
          clientInfoLoadedPromise.then(function (success) {
              if (!success) {
                  logInitializationError();
                  return;
              }
  
              if (Array.isArray(pendingClientsToBeLoaded)) {
                  pendingClientsToBeLoaded.forEach(function (pendingClient) {
                      pendingClient.load();
                  });
              } else {
                  exports.trigger("languageClientModuleInitialized");
              }
              pendingClientsToBeLoaded = null;
          }, function () {
              logInitializationError();
          });
      }
  
      /**
       * This function starts a domain which initializes the LanguageClient node module
       * required by the Language Server Protocol framework in Brackets. All the LSP clients
       * can only be successfully initiated once this domain has been successfully loaded and
       * the LanguageClient info initialized. Refer to sendLanguageClientInfo for more.
       */
      function initDomainAndHandleNodeCrash() {
          clientInfoDomain = new NodeDomain("LanguageClientInfo", _domainPath);
          //Initialize LanguageClientInfo once the domain has successfully loaded.
          clientInfoDomain.promise().done(function () {
              sendLanguageClientInfo();
              //This is to handle the node failure. If the node process dies, we get an on close
              //event on the websocket connection object. Brackets then spawns another process and
              //restablishes the connection. Once the connection is restablished we send reinitialize
              //the LanguageClient info.
              clientInfoDomain.connection.on("close", function (event, reconnectedPromise) {
                  reconnectedPromise.done(sendLanguageClientInfo);
              });
          }).fail(function (err) {
              console.error("ClientInfo domain could not be loaded: ", err);
          });
      }
      initDomainAndHandleNodeCrash();
  
  
      exports.initiateLanguageClient = initiateLanguageClient;
      exports.syncPrefsWithDomain = syncPrefsWithDomain;
  });
  