# Top level script for launching Wing -- users should instead invoke
# wing.exe or the wing bash script on Linux and OS X

#
# Copyright (c) 2000-2018, Archaeopteryx Software, Inc.  All rights reserved.
#
# Written by Stephan R.A. Deibel and John P. Ehresman

import sys
import os

import _winghome

# This is the non-patchable IDE startup module

# Set __file__ if not already set
try:
  __file__
except NameError:
  __file__ = sys.argv[0]

#----------------------------------------------------------------------
def NP_LoadModuleHere(modname):
  """Load the given module from same directory as this file
  without adding it to sys.modules"""

  sys.modules.pop(modname, None)

  sys.path.insert(0, os.path.dirname(__file__))
  exec('import %s' % modname)
  sys.modules.pop(modname, None)

  sys.path = sys.path[1:]
  return locals()[modname]
  
bootstrap_utils = NP_LoadModuleHere('bootstrap_utils')

# Flag for showing garbage collection debug output
if '--gc-debug-stats' in sys.argv:
  sys.argv.remove('--gc-debug-stats')
  import gc
  gc.set_debug(gc.DEBUG_STATS)

# Flag for verbose call tracing
if '--verbose-trace' in sys.argv:
  def trace(frame, event, arg):
    try:
      fname = repr(frame.f_code)
    except:
      fname = '<unknown>'
    sys.__stderr__.write(event + ' ' + fname) 
    sys.__stderr__.write('\n')
  sys.setprofile(trace)
  
# Flags to explicitly enable or disable loading updates
if '--enable-updates' in sys.argv:
  _winghome.kLoadUpdates = True
  sys.argv.remove('--enable-updates')
elif '--disable-updates' in sys.argv:
  _winghome.kLoadUpdates = False
  sys.argv.remove('--disable-updates')
  
# Load updates only if not running from sources
else:
  indicator_file = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
                                'src', 'config.py')
  if os.path.exists(indicator_file):
    _winghome.kLoadUpdates = False
  else:
    _winghome.kLoadUpdates = True
  
#-----------------------------------------------------------------------
# Hack to set default encoding to utf-8 because most Wing code was
# developed to run with pygtk, which also changed the default encoding
# Since Wing changes the default encoding, there are potential bugs
# with any externally developed module that assumes a different default
# encoding.  Remember that dealing with encodings is _hard_ even though
# the most developers assume is that it should be easy (rather like
# threads or floating point).  The encoding story changes with Python 3.
def NP_SetDefaultEncodingToUtf8():

  # XXXQT May want to change this to calling PyUnicode_SetDefaultEncoding
  # XXXQT directly and avoid reloading sys, but this works for now.
  
  try:
    sys.setdefaultencoding
  except AttributeError:
    # Note that reloading resets sys.excepthook, which needs to be set for
    # exception reporting to work in the debugger, and stdin / stdout / stderr,
    # which are needed for Wing's test runner, so the values are saved &
    # then reset
    to_save_names = ['excepthook', 'stdin', 'stdout', 'stderr']
    saved = dict((name, getattr(sys, name)) for name in to_save_names)
    reload(sys)
    for name, val in saved.iteritems():
      setattr(sys, name, val)

  sys.setdefaultencoding('utf-8')

NP_SetDefaultEncodingToUtf8()
  

#-----------------------------------------------------------------------
def NP_FindWingHome(argv):
  """ Returns abspath of winghome as an unicode object. """
  
  fs_encoding = sys.getfilesystemencoding()
  if fs_encoding is None:
    fs_encoding = sys.getdefaultencoding()
  if '--utf8-args' in argv:
    encoding = 'utf_8'
  else:
    encoding = fs_encoding
  
  winghome = None
  
  # This lets us debug the IDE with an installed copy of itself on win32
  if '--use-devel-winghome' in argv:
    i = argv.index('--use-devel-winghome')
    winghome = argv[i + 1]
    del argv[i:i + 2]
    
  # This is used when running the binary installation on win32
  elif '--use-winghome' in argv:
    i = argv.index('--use-winghome')
    winghome=argv[i + 1]
    del argv[i:i + 2]
    
  # Look in environment for a given value (used on Posix and on win32
  # when running Wing from sources)
  elif os.environ.has_key('WINGHOME'):
    winghome=os.environ['WINGHOME']
    encoding = fs_encoding
  
  # If none given, compute WINGHOME based on location of this file
  else:
    winghome = os.path.dirname(os.path.dirname(os.path.abspath(argv[0])))
  
  if winghome is None:
    return None
  
  if encoding is None:
    encoding = sys.getdefaultencoding()
  winghome = unicode(winghome, encoding, 'strict')

  if not os.path.isabs(winghome):
    try:
      cwd = os.getcwdu()
    except Exception:
      pass
    else:
      winghome = os.path.join(cwd, winghome)
      
  return os.path.normpath(winghome)


#-----------------------------------------------------------------------
def NP_Initialize(argv):
  """The non-patchable initialization phase"""

  err_list = []
  class CTempLog:
    def write(self, s):
      assert err_list != None
      err_list.append(s)

  try:
    # Save original language setting before we muck with it internally
    os.environ['WING_ORIG_LANGUAGE'] = os.environ.get('LANGUAGE', '__undefined__')

    # Work around for Python codec bug on Windows (only affects Wing running as console app)
    if sys.platform == 'win32':
      import codecs
      codecs.register(lambda name: codecs.lookup('utf-8') if name == 'cp65001' else None)
      
    # Some initial debug info
    logger = CTempLog()
    logger.write("=" * 80 + '\n')
    logger.write("STARTING WING with args:\n")
    logger.write(str(argv)+'\n')
    logger.write("sys.executable=%s\n" % sys.executable)
    logger.write("sys.maxint=%s\n" % sys.maxint)
    logger.write("LD_LIBRARY_PATH=%s\n" % os.environ.get('LD_LIBRARY_PATH', "''"))
    logger.write("DYLD_LIBRARY_PATH=%s\n" % os.environ.get('DYLD_LIBRARY_PATH', "''"))
    logger.write("DYLD_FALLBACK_LIBRARY_PATH=%s\n" % os.environ.get('DYLD_FALLBACK_LIBRARY_PATH', "''"))
    logger.write("PATH=%s\n" % os.environ.get('PATH', "''"))
    
    # Find winghome and user settings directory and put into the _winghome module
    winghome, user_settings = bootstrap_utils.NP_SetupWingHomeModule(NP_FindWingHome(argv))

    # Set up merging import manager, which supports update; everything after
    # this can be patched
    importer = bootstrap_utils.NP_CreateMetaImporter(winghome, user_settings,
                                                     'ide-2.7', logger)
    return importer, err_list

  except:
    # If strings in err_list, send them to stderr
    if err_list:
      for err in err_list:
        sys.stderr.write(err)
    raise

  
#-----------------------------------------------------------------------
INITIALIZED = False
if __name__ == '__main__':
  try:
    
    # First run the non-patchable initialization
    argv = list(sys.argv)
    importer, err_list = NP_Initialize(argv)
    INITIALIZED = True
    
    # Everything after this point can be updated with the update manager
    
    # Flag to run installation validation check
    if '--check-install' in argv:
      argv.remove('--check-install')
      import vinfo
      import config
      from wingbase import updateutils
      code_dirs = bootstrap_utils.NP_GetCodeDirs(config.kWingHome, config.kUserSettings,
                                                logger=sys.stdout)
      errors = updateutils.ValidateInstall(code_dirs, [])
      if errors:
        sys.__stderr__.write("Some validation errors were found:\n")
        sys.__stderr__.write('   '+ '\n   '.join(errors) + '\n')
        if config.kAvail101Support:
          prod = 'wing-101'
        elif config.kAvailPersonalSupport:
          prod = 'wing-personal'
        else:
          prod = 'wingpro'
        sys.__stderr__.write("You may want to reinstall from https://wingware.com/downloads/%s/%s\n" % (prod, vinfo.kVersion))
      else:
        sys.__stderr__.write("No validation errors found\n")

      # Start Wing
    else:
      import startwing
      startwing.Main(argv, importer, err_list)
    
  finally:
    # Make sure any pending log entries are written to log files
    try:
      from wingutils import reflect
    except:
      sys.stderr.write("Unable to import wingutils.reflect")
    else:
      reflect._DoWrite()

    
    
