text: # -*- coding: utf-8 -*-
  # Copyright (c) 2020-2022 Salvador E. Tropea
  # Copyright (c) 2020-2022 Instituto Nacional de Tecnologïa Industrial
  # License: Apache 2.0
  # Project: KiAuto (formerly kicad-automation-scripts)
  import os
  import re
  import json
  import configparser
  from contextlib import contextmanager
  from sys import exit, path
  from subprocess import check_output, DEVNULL
  
  # Default W,H for recording
  REC_W = 1366
  REC_H = 960
  
  # Return error codes
  # Positive values are ERC/DRC errors
  NO_SCHEMATIC = 1
  WRONG_ARGUMENTS = 2   # This is what argsparse uses
  EESCHEMA_CFG_PRESENT = 11
  KICAD_CFG_PRESENT = 3
  NO_PCB = 4
  PCBNEW_CFG_PRESENT = 5
  WRONG_LAYER_NAME = 6
  WRONG_PCB_NAME = 7
  WRONG_SCH_NAME = 8
  PCBNEW_ERROR = 9
  EESCHEMA_ERROR = 10
  NO_PCBNEW_MODULE = 11
  USER_HOTKEYS_PRESENT = 12
  CORRUPTED_PCB = 13
  NO_EN_LOCALE = 14
  MISSING_TOOL = 15
  KICAD_DIED = 16
  READ_ONLY_PROBLEM = 17
  # Wait 60 s to pcbnew/eeschema window to be present
  WAIT_START = 60
  # Name for testing versions
  NIGHTLY = 'nightly'
  # Scale factor for the timeouts
  TIME_OUT_MULT = 1.0
  
  KICAD_VERSION_5_99 = 5099000
  KICAD_SHARE = '/usr/share/kicad/'
  KICAD_NIGHTLY_SHARE = '/usr/share/kicad-nightly/'
  
  
  @contextmanager
  def hide_stderr():
      """ Low level stderr supression, used to hide KiCad bugs. """
      newstderr = os.dup(2)
      devnull = os.open('/dev/null', os.O_WRONLY)
      os.dup2(devnull, 2)
      os.close(devnull)
      yield
      os.dup2(newstderr, 2)
  
  
  class Config(object):
      def __init__(self, logger, input_file=None, args=None):
          self.export_format = 'pdf'
          if input_file:
              self.input_file = input_file
              self.input_no_ext = os.path.splitext(input_file)[0]
              #
              # As soon as we init pcbnew the following files are modified:
              #
              if os.path.isfile(self.input_no_ext+'.pro'):
                  self.start_pro_stat = os.stat(self.input_no_ext+'.pro')
              else:
                  self.start_pro_stat = None
              if os.path.isfile(self.input_no_ext+'.kicad_pro'):
                  self.start_kicad_pro_stat = os.stat(self.input_no_ext+'.kicad_pro')
              else:
                  self.start_kicad_pro_stat = None
              if os.path.isfile(self.input_no_ext+'.kicad_prl'):
                  self.start_kicad_prl_stat = os.stat(self.input_no_ext+'.kicad_prl')
              else:
                  self.start_kicad_prl_stat = None
          if args:
              # Session debug
              self.use_wm = args.use_wm  # Use a Window Manager, dialogs behaves in a different way
              self.start_x11vnc = args.start_x11vnc
              self.rec_width = args.rec_width
              self.rec_height = args.rec_height
              self.record = args.record
              self.video_dir = args.output_dir
              self.wait_for_key = args.wait_key
              self.time_out_scale = args.time_out_scale
              # Others
              if hasattr(args, 'file_format'):
                  self.export_format = args.file_format.lower()
          else:
              # Session debug
              self.use_wm = False
              self.start_x11vnc = False
              self.rec_width = REC_W
              self.rec_height = REC_H
              self.record = False
              self.video_dir = None
              self.wait_for_key = False
              self.time_out_scale = 1.0
          self.colordepth = 24
          self.video_name = None
          self.video_dir = self.output_dir = ''
          # Executable and dirs
          self.eeschema = 'eeschema'
          self.pcbnew = 'pcbnew'
          self.kicad2step = 'kicad2step'
          self.kicad_conf_dir = 'kicad'
          ng_ver = os.environ.get('KIAUS_USE_NIGHTLY')
          if ng_ver:
              self.eeschema += '-'+NIGHTLY
              self.pcbnew += '-'+NIGHTLY
              self.kicad2step += '-'+NIGHTLY
              self.kicad_conf_dir += os.path.join(NIGHTLY, ng_ver)
              # Path to the Python module
              path.insert(0, '/usr/lib/kicad-nightly/lib/python3/dist-packages')
          # Detect KiCad version
          try:
              import pcbnew
          except ImportError:
              logger.error("Failed to import pcbnew Python module."
                           " Is KiCad installed?"
                           " Do you need to add it to PYTHONPATH?")
              exit(NO_PCBNEW_MODULE)
          kicad_version = pcbnew.GetBuildVersion()
          try:
              # Debian sid may 2021 mess:
              really_index = kicad_version.index('really')
              kicad_version = kicad_version[really_index+6:]
          except ValueError:
              pass
          m = re.search(r'(\d+)\.(\d+)\.(\d+)', kicad_version)
          if m is None:
              logger.error("Unable to detect KiCad version, got: `{}`".format(kicad_version))
              exit(NO_PCBNEW_MODULE)
          self.kicad_version_major = int(m.group(1))
          self.kicad_version_minor = int(m.group(2))
          self.kicad_version_patch = int(m.group(3))
          self.kicad_version = self.kicad_version_major*1000000+self.kicad_version_minor*1000+self.kicad_version_patch
          logger.debug('Detected KiCad v{}.{}.{} ({} {})'.format(self.kicad_version_major, self.kicad_version_minor,
                       self.kicad_version_patch, kicad_version, self.kicad_version))
          self.ki5 = self.kicad_version < KICAD_VERSION_5_99
          # Config file names
          if not self.ki5:
              self.kicad_conf_path = pcbnew.GetSettingsManager().GetUserSettingsPath()
              # No longer needed for 202112021512+6.0.0+rc1+287+gbb08ef2f41+deb11
              # if ng_ver:
              #    self.kicad_conf_path = self.kicad_conf_path.replace('/kicad/', '/kicadnightly/')
          else:
              # Bug in KiCad (#6989), prints to stderr:
              # `../src/common/stdpbase.cpp(62): assert "traits" failed in Get(test_dir): create wxApp before calling this`
              # Found in KiCad 5.1.8, 5.1.9
              # So we temporarily supress stderr
              with hide_stderr():
                  self.kicad_conf_path = pcbnew.GetKicadConfigPath()
          logger.debug('Config path {}'.format(self.kicad_conf_path))
          # First we solve kicad_common because it can redirect to another config dir
          self.conf_kicad = os.path.join(self.kicad_conf_path, 'kicad_common')
          self.conf_kicad_bkp = None
          if not self.ki5:
              self.conf_kicad += '.json'
              self.conf_kicad_json = True
          else:
              self.conf_kicad_json = False
          # Read the environment redefinitions used by KiCad
          if os.path.isfile(self.conf_kicad):
              self.load_kicad_environment(logger)
              if 'KICAD_CONFIG_HOME' in self.env and self.ki5:
                  # The user is redirecting the configuration
                  # KiCad 5 unintentionally allows it, is a bug, and won't be fixed:
                  # https://forum.kicad.info/t/kicad-config-home-inconsistencies-and-detail/26875
                  self.kicad_conf_path = self.env['KICAD_CONFIG_HOME']
                  logger.debug('Redirecting KiCad config path to: '+self.kicad_conf_path)
          else:
              logger.warning('Missing KiCad main config file '+self.conf_kicad)
          # - eeschema config
          self.conf_eeschema = os.path.join(self.kicad_conf_path, 'eeschema')
          self.conf_eeschema_bkp = None
          # - pcbnew config
          self.conf_pcbnew = os.path.join(self.kicad_conf_path, 'pcbnew')
          self.conf_pcbnew_bkp = None
          # Config files that migrated to JSON
          # Note that they remain in the old format until saved
          if not self.ki5:
              self.conf_eeschema += '.json'
              self.conf_pcbnew += '.json'
              self.conf_eeschema_json = True
              self.conf_pcbnew_json = True
              self.pro_ext = 'kicad_pro'
              self.prl_ext = 'kicad_prl'
              self.conf_colors = os.path.join(self.kicad_conf_path, 'colors', 'user.json')
              self.conf_colors_bkp = None
              self.conf_3dview = os.path.join(self.kicad_conf_path, '3d_viewer.json')
              self.conf_3dview_bkp = None
          else:
              self.conf_eeschema_json = False
              self.conf_pcbnew_json = False
              self.pro_ext = 'pro'
              self.prl_ext = None
              self.conf_colors = self.conf_colors_bkp = None
              self.conf_3dview = self.conf_3dview_bkp = None
          # - hotkeys
          self.conf_hotkeys = os.path.join(self.kicad_conf_path, 'user.hotkeys')
          self.conf_hotkeys_bkp = None
          # - sym-lib-table
          self.user_sym_lib_table = os.path.join(self.kicad_conf_path, 'sym-lib-table')
          self.user_fp_lib_table = os.path.join(self.kicad_conf_path, 'fp-lib-table')
          self.sys_sym_lib_table = [KICAD_SHARE+'template/sym-lib-table']
          self.sys_fp_lib_table = [KICAD_SHARE+'template/fp-lib-table']
          if ng_ver:
              # 20200912: sym-lib-table is missing
              self.sys_sym_lib_table.insert(0, KICAD_NIGHTLY_SHARE+'template/sym-lib-table')
              self.sys_fp_lib_table.insert(0, KICAD_NIGHTLY_SHARE+'template/fp-lib-table')
          # Some details about the UI
          if not self.ki5:
              # KiCad 5.99.0
              # self.ee_window_title = r'\[.*\] — Eeschema$'  # "PROJECT [HIERARCHY_PATH] - Eeschema"
              # KiCad 6.0.0 rc1
              self.ee_window_title = r'\[.*\] — Schematic Editor$'  # "PROJECT [HIERARCHY_PATH] - Schematic Editor"
              self.pn_window_title = r'.* — PCB Editor$'  # "PROJECT - PCB Editor"
          else:
              # KiCad 5.1.6
              self.ee_window_title = r'Eeschema.*\.sch'  # "Eeschema - file.sch"
              self.pn_window_title = r'^Pcbnew'
          # Collected errors and unconnecteds (warnings)
          self.errs = []
          self.wrns = []
          # Error filters
          self.err_filters = []
  
      def load_kicad_environment(self, logger):
          self.env = {}
          if self.conf_kicad_json:
              env = self.get_config_vars_json(self.conf_kicad)
              if env:
                  self.env = env
          else:
              env = self.get_config_vars_ini(self.conf_kicad)
              if env:
                  for k, v in env.items():
                      self.env[k.upper()] = v
          logger.debug('KiCad environment: '+str(self.env))
  
      @staticmethod
      def get_config_vars_json(file):
          with open(file, "rt") as f:
              data = json.load(f)
          if 'environment' in data and 'vars' in data['environment']:
              return data['environment']['vars']
          return None
  
      @staticmethod
      def get_config_vars_ini(file):
          config = configparser.ConfigParser()
          with open(file, "rt") as f:
              data = f.read()
          config.read_string('[Various]\n'+data)
          if 'EnvironmentVariables' in config:
              return config['EnvironmentVariables']
          return None
  
  
  def get_en_locale(logger):
      ''' Looks for a usable locale with english as language '''
      try:
          res = check_output(['locale', '-a'], stderr=DEVNULL).decode('utf8')
      except FileNotFoundError:
          logger.warning("The `locale` command isn't installed. Guessing `C.UTF-8` is supported")
          res = 'C.UTF-8'
      found = re.search(r'en(.*)UTF-?8', res, re.I)
      if found:
          res = found.group(0)
      else:
          found = re.search(r'C\.UTF-?8', res, re.I)
          if found:
              res = found.group(0)
          else:
              logger.error("No suitable english locale found. Please add `en_US.utf8` to your system")
              exit(NO_EN_LOCALE)
      logger.debug('English locale: '+res)
      return res
  
  
  __author__ = 'Salvador E. Tropea'
  __copyright__ = 'Copyright 2018-2022, INTI/Productize SPRL'
  __credits__ = ['Salvador E. Tropea', 'Seppe Stas', 'Jesse Vincent', 'Scott Bezek']
  __license__ = 'Apache 2.0'
  __email__ = 'stropea@inti.gob.ar'
  __status__ = 'stable'
  __url__ = 'https://github.com/INTI-CMNB/KiAuto/'
  __version__ = '1.6.13'
  