# encoding: utf-8
"""
An application for IPython.

All top-level applications should use the classes in this module for
handling configuration and creating configurables.

The job of an :class:`Application` is to create the master configuration
object and then create the configurable objects, passing the config to them.
"""

# Copyright (c) IPython Development Team.
# Distributed under the terms of the Modified BSD License.

import atexit
import glob
import logging
import os
import shutil
import sys

from IPython.config.application import Application, catch_config_error
from IPython.config.loader import ConfigFileNotFound
from IPython.core import release, crashhandler
from IPython.core.profiledir import ProfileDir, ProfileDirError
from IPython.utils.path import get_ipython_dir, get_ipython_package_dir, ensure_dir_exists
from IPython.utils import py3compat
from IPython.utils.traitlets import List, Unicode, Type, Bool, Dict, Set, Instance

if os.name == 'nt':
    programdata = os.environ.get('PROGRAMDATA', None)
    if programdata:
        SYSTEM_CONFIG_DIRS = [os.path.join(programdata, 'ipython')]
    else:  # PROGRAMDATA is not defined by default on XP.
        SYSTEM_CONFIG_DIRS = []
else:
    SYSTEM_CONFIG_DIRS = [
        "/usr/local/etc/ipython",
        "/etc/ipython",
    ]


# aliases and flags

base_aliases = {
    'profile-dir': 'ProfileDir.location',
    'profile': 'BaseIPythonApplication.profile',
    'ipython-dir': 'BaseIPythonApplication.ipython_dir',
    'log-level': 'Application.log_level',
    'config': 'BaseIPythonApplication.extra_config_file',
}

base_flags = dict(
    debug=({'Application': {'log_level': logging.DEBUG}},
           "set log level to logging.DEBUG (maximize logging output)"),
    quiet = ({'Application': {'log_level': logging.CRITICAL}},
             "set log level to logging.CRITICAL (minimize logging output)"),
    init = ({'BaseIPythonApplication': {
        'copy_config_files': True,
        'auto_create': True}
    }, """Initialize profile with default config files.  This is equivalent
            to running `ipython profile create <profile>` prior to startup.
            """)
)


class BaseIPythonApplication(Application):

    name = Unicode(u'ipython')
    description = Unicode(u'IPython: an enhanced interactive Python shell.')
    version = Unicode(release.version)

    aliases = Dict(base_aliases)
    flags = Dict(base_flags)
    classes = List([ProfileDir])

    # Track whether the config_file has changed,
    # because some logic happens only if we aren't using the default.
    config_file_specified = Set()

    config_file_name = Unicode()

    def _config_file_name_default(self):
        return self.name.replace('-', '_') + u'_config.py'

    def _config_file_name_changed(self, name, old, new):
        if new != old:
            self.config_file_specified.add(new)

    # The directory that contains IPython's builtin profiles.
    builtin_profile_dir = Unicode(
        os.path.join(
            get_ipython_package_dir(), u'config', u'profile', u'default')
    )

    config_file_paths = List(Unicode)

    def _config_file_paths_default(self):
        return [py3compat.getcwd()]

    extra_config_file = Unicode(config=True,
                                help="""Path to an extra config file to load.
    
    If specified, load this config file in addition to any other IPython config.
    """)

    def _extra_config_file_changed(self, name, old, new):
        try:
            self.config_files.remove(old)
        except ValueError:
            pass
        self.config_file_specified.add(new)
        self.config_files.append(new)

    profile = Unicode(u'default', config=True,
                      help="""The IPython profile to use."""
                      )

    def _profile_changed(self, name, old, new):
        self.builtin_profile_dir = os.path.join(
            get_ipython_package_dir(), u'config', u'profile', new
        )

    ipython_dir = Unicode(config=True,
                          help="""
        The name of the IPython directory. This directory is used for logging
        configuration (through profiles), history storage, etc. The default
        is usually $HOME/.ipython. This option can also be specified through
        the environment variable IPYTHONDIR.
        """
                          )

    def _ipython_dir_default(self):
        d = get_ipython_dir()
        self._ipython_dir_changed('ipython_dir', d, d)
        return d

    _in_init_profile_dir = False
    profile_dir = Instance(ProfileDir)

    def _profile_dir_default(self):
        # avoid recursion
        if self._in_init_profile_dir:
            return
        # profile_dir requested early, force initialization
        self.init_profile_dir()
        return self.profile_dir

    overwrite = Bool(False, config=True,
                     help="""Whether to overwrite existing config files when copying""")
    auto_create = Bool(False, config=True,
                       help="""Whether to create profile dir if it doesn't exist""")

    config_files = List(Unicode)

    def _config_files_default(self):
        return [self.config_file_name]

    copy_config_files = Bool(False, config=True,
                             help="""Whether to install the default config files into the profile dir.
        If a new profile is being created, and IPython contains config files for that
        profile, then they will be staged into the new directory.  Otherwise,
        default config files will be automatically generated.
        """)

    verbose_crash = Bool(False, config=True,
                         help="""Create a massive crash report when IPython encounters what may be an
        internal error.  The default is to append a short message to the
        usual traceback""")