"""
A number of functions that enhance IDLE on Mac OSX.
"""
import sys
import tkinter
from os import path
import warnings

def runningAsOSXApp():
    warnings.warn("runningAsOSXApp() is deprecated, use isAquaTk()",
                        DeprecationWarning, stacklevel=2)
    return isAquaTk()

def isCarbonAquaTk(root):
    warnings.warn("isCarbonAquaTk(root) is deprecated, use isCarbonTk()",
                        DeprecationWarning, stacklevel=2)
    return isCarbonTk()

_tk_type = None

def _initializeTkVariantTests(root):
    """
    Initializes OS X Tk variant values for
    isAquaTk(), isCarbonTk(), isCocoaTk(), and isXQuartz().
    """
    global _tk_type
    if sys.platform == 'darwin':
        ws = root.tk.call('tk', 'windowingsystem')
        if 'x11' in ws:
            _tk_type = "xquartz"
        elif 'aqua' not in ws:
            _tk_type = "other"
        elif 'AppKit' in root.tk.call('winfo', 'server', '.'):
            _tk_type = "cocoa"
        else:
            _tk_type = "carbon"
    else:
        _tk_type = "other"

def isAquaTk():
    """
    Returns True if IDLE is using a native OS X Tk (Cocoa or Carbon).
    """
    assert _tk_type is not None
    return _tk_type == "cocoa" or _tk_type == "carbon"

def isCarbonTk():
    """
    Returns True if IDLE is using a Carbon Aqua Tk (instead of the
    newer Cocoa Aqua Tk).
    """
    assert _tk_type is not None
    return _tk_type == "carbon"

def isCocoaTk():
    """
    Returns True if IDLE is using a Cocoa Aqua Tk.
    """
    assert _tk_type is not None
    return _tk_type == "cocoa"

def isXQuartz():
    """
    Returns True if IDLE is using an OS X X11 Tk.
    """
    assert _tk_type is not None
    return _tk_type == "xquartz"

def tkVersionWarning(root):
    """
    Returns a string warning message if the Tk version in use appears to
    be one known to cause problems with IDLE.
    1. Apple Cocoa-based Tk 8.5.7 shipped with Mac OS X 10.6 is unusable.
    2. Apple Cocoa-based Tk 8.5.9 in OS X 10.7 and 10.8 is better but
        can still crash unexpectedly.
    """

    if isCocoaTk():
        patchlevel = root.tk.call('info', 'patchlevel')
        if patchlevel not in ('8.5.7', '8.5.9'):
            return False
        return (r"WARNING: The version of Tcl/Tk ({0}) in use may"
                r" be unstable.\n"
                r"Visit http://www.python.org/download/mac/tcltk/"
                r" for current information.".format(patchlevel))
    else:
        return False

def addOpenEventSupport(root, flist):
    """
    This ensures that the application will respond to open AppleEvents, which
    makes is feasible to use IDLE as the default application for python files.
    """
    def doOpenFile(*args):
        for fn in args:
            flist.open(fn)

    # The command below is a hook in aquatk that is called whenever the app
    # receives a file open event. The callback can have multiple arguments,
    # one for every file that should be opened.
    root.createcommand("::tk::mac::OpenDocument", doOpenFile)

def hideTkConsole(root):
    try:
        root.tk.call('console', 'hide')
    except tkinter.TclError:
        # Some versions of the Tk framework don't have a console object
        pass

def overrideRootMenu(root, flist):
    """
    Replace the Tk root menu by something that is more appropriate for
    IDLE with an Aqua Tk.
    """
    # The menu that is attached to the Tk root (".") is also used by AquaTk for
    # all windows that don't specify a menu of their own. The default menubar
    # contains a number of menus, none of which are appropriate for IDLE. The
    # Most annoying of those is an 'About Tck/Tk...' menu in the application
    # menu.
    #
    # This function replaces the default menubar by a mostly empty one, it
    # should only contain the correct application menu and the window menu.
    #
    # Due to a (mis-)feature of TkAqua the user will also see an empty Help
    # menu.
    from tkinter import Menu, Text, Text
    from idlelib.EditorWindow import prepstr, get_accelerator
    from idlelib import Bindings
    from idlelib import WindowList
    from idlelib.MultiCall import MultiCallCreator

    closeItem = Bindings.menudefs[0][1][-2]

    # Remove the last 3 items of the file menu: a separator, close window and
    # quit. Close window will be reinserted just above the save item, where
    # it should be according to the HIG. Quit is in the application menu.
    del Bindings.menudefs[0][1][-3:]
    Bindings.menudefs[0][1].insert(6, closeItem)

    # Remove the 'About' entry from the help menu, it is in the application
    # menu
    del Bindings.menudefs[-1][1][0:2]

    # Remove the 'Configure' entry from the options menu, it is in the
    # application menu as 'Preferences'
    del Bindings.menudefs[-2][1][0:2]

    menubar = Menu(root)
    root.configure(menu=menubar)
    menudict = {}

    menudict['windows'] = menu = Menu(menubar, name='windows')
    menubar.add_cascade(label='Window', menu=menu, underline=0)

    def postwindowsmenu(menu=menu):
        end = menu.index('end')
        if end is None:
            end = -1

        if end > 0:
            menu.delete(0, end)
        WindowList.add_windows_to_menu(menu)
    WindowList.register_callback(postwindowsmenu)

    def about_dialog(event=None):
        from idlelib import aboutDialog
        aboutDialog.AboutDialog(root, 'About IDLE')

    def config_dialog(event=None):
        from idlelib import configDialog

        # Ensure that the root object has an instance_dict attribute,
        # mirrors code in EditorWindow (although that sets the attribute
        # on an EditorWindow instance that is then passed as the first
        # argument to ConfigDialog)
        root.instance_dict = flist.inversedict
        root.instance_dict = flist.inversedict
        configDialog.ConfigDialog(root, 'Settings')

    def help_dialog(event=None):
        from idlelib import textView
        fn = path.join(path.abspath(path.dirname(__file__)), 'help.txt')
        textView.view_file(root, 'Help', fn)

    root.bind('<<about-idle>>', about_dialog)
    root.bind('<<open-config-dialog>>', config_dialog)
    root.createcommand('::tk::mac::ShowPreferences', config_dialog)
    if flist:
        root.bind('<<close-all-windows>>', flist.close_all_callback)

        # The binding above doesn't reliably work on all versions of Tk
        # on MacOSX. Adding command definition below does seem to do the
        # right thing for now.
        root.createcommand('exit', flist.close_all_callback)

    if isCarbonTk():
        # for Carbon AquaTk, replace the default Tk apple menu
        menudict['application'] = menu = Menu(menubar, name='apple')
        menubar.add_cascade(label='IDLE', menu=menu)
        Bindings.menudefs.insert(0,
            ('application', [
                ('About IDLE', '<<about-idle>>'),
                    None,
                ]))
        tkversion = root.tk.eval('info patchlevel')
        if tuple(map(int, tkversion.split('.'))) < (8, 4, 14):
            # for earlier AquaTk versions, supply a Preferences menu item
            Bindings.menudefs[0][1].append(
                    ('_Preferences....', '<<open-config-dialog>>'),
                )
    if isCocoaTk():
        # replace default About dialog with About IDLE one
        root.createcommand('tkAboutDialog', about_dialog)
        # replace default "Help" item in Help menu
        root.createcommand('::tk::mac::ShowHelp', help_dialog)
        # remove redundant "IDLE Help" from menu
        del Bindings.menudefs[-1][1][0]

def setupApp(root, flist):
    """
    Perform initial OS X customizations if needed.
    Called from PyShell.main() after initial calls to Tk()

    There are currently three major versions of Tk in use on OS X:
        1. Aqua Cocoa Tk (native default since OS X 10.6)
        2. Aqua Carbon Tk (original native, 32-bit only, deprecated)
        3. X11 (supported by some third-party distributors, deprecated)
    There are various differences among the three that affect IDLE
    behavior, primarily with menus, mouse key events, and accelerators.
    Some one-time customizations are performed here.
    Others are dynamically tested throughout idlelib by calls to the
    isAquaTk(), isCarbonTk(), isCocoaTk(), isXQuartz() functions which
    are initialized here as well.
    """
    _initializeTkVariantTests(root)
    if isAquaTk():
        hideTkConsole(root)
        overrideRootMenu(root, flist)
        addOpenEventSupport(root, flist)
