text: ## Copyright 2015-2019 Ilgar Lunin, Pedro Cabrera
  
  ## Licensed under the Apache License, Version 2.0 (the "License");
  ## you may not use this file except in compliance with the License.
  ## You may obtain a copy of the License at
  
  ##     http://www.apache.org/licenses/LICENSE-2.0
  
  ## Unless required by applicable law or agreed to in writing, software
  ## distributed under the License is distributed on an "AS IS" BASIS,
  ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  ## See the License for the specific language governing permissions and
  ## limitations under the License.
  
  
  import argparse
  import sys
  import os
  import json
  import threading
  import time
  
  from PyFlow.App import PyFlow
  from PyFlow import graphUiParser
  from Qt.QtWidgets import QApplication
  from PyFlow import INITIALIZE
  from PyFlow.Core.Common import *
  from PyFlow.Core.version import currentVersion
  from PyFlow.Core.GraphManager import GraphManagerSingleton
  
  
  def getGraphArguments(data, parser):
      """Adds arguments to parser using graph input nodes
  
      :param data: Serialized graph
      :type data: json
      :param parser: ArgumentParser class instance
      :type parser: ArgumentParser
      """
      typeMapping = {
          "BoolPin": bool,
          "StringPin": str,
          "IntPin": int,
          "FloatPin": float
      }
  
      graphNode = None
      for node in data["nodes"]:
          if node["type"] == "graphInputs":
              graphNode = node
              break
  
      for outPin in graphNode["outputs"]:
          pinType = outPin["dataType"]
          if pinType != "ExecPin":
              parser.add_argument("--{0}".format(outPin["name"]), type=typeMapping[pinType])
  
  
  def main():
      parser = argparse.ArgumentParser(description="PyFlow CLI")
      parser.add_argument("-m", "--mode", type=str, default="edit", choices=["edit", "run", "runui"])
      parser.add_argument("-f", "--filePath", type=str, default="untitled.pygraph")
      parser.add_argument("--version", action="version", version=str(currentVersion()))
      parsedArguments, unknown = parser.parse_known_args(sys.argv[1:])
  
      filePath = parsedArguments.filePath
  
      if not filePath.endswith(".pygraph"):
          filePath += ".pygraph"
  
      if parsedArguments.mode == "edit":
          app = QApplication(sys.argv)
  
          instance = PyFlow.instance(software="standalone")
          if instance is not None:
              app.setActiveWindow(instance)
              instance.show()
              if os.path.exists(filePath):
                  with open(filePath, 'r') as f:
                      data = json.load(f)
                      instance.loadFromData(data)
                      instance.currentFileName = filePath
  
              try:
                  sys.exit(app.exec_())
              except Exception as e:
                  print(e)
  
      if parsedArguments.mode == "run":
          data = None
          if not os.path.exists(filePath):
              print("No such file. {}".format(filePath))
              return
          with open(filePath, 'r') as f:
              data = json.load(f)
          getGraphArguments(data, parser)
          parsedArguments = parser.parse_args()
  
          # load updated data
          INITIALIZE()
          GM = GraphManagerSingleton().get()
          GM.deserialize(data)
  
          # fake main loop
          def programLoop():
              while True:
                  GM.Tick(deltaTime=0.02)
                  time.sleep(0.02)
                  if GM.terminationRequested:
                      break
  
          # call graph inputs nodes
          root = GM.findRootGraph()
          graphInputNodes = root.getNodesList(classNameFilters=["graphInputs"])
          evalFunctions = []
          for graphInput in graphInputNodes:
              # update data
              for outPin in graphInput.outputs.values():
                  if outPin.isExec():
                      evalFunctions.append(outPin.call)
                  if hasattr(parsedArguments, outPin.name):
                      cliValue = getattr(parsedArguments, outPin.name)
                      if cliValue is not None:
                          outPin.setData(cliValue)
  
          for foo in evalFunctions:
              foo()
  
          loopThread = threading.Thread(target=programLoop)
          loopThread.start()
          loopThread.join()
  
      if parsedArguments.mode == "runui":
          graphUiParser.run(filePath)
  