__version__ = "v0.9.12"

import sys
import os
import re
import logging
from collections import OrderedDict

import sublime
from sublime_plugin import WindowCommand, EventListener, TextCommand
from Default.paragraph import expand_to_paragraph

from .SQLToolsAPI import Utils
from .SQLToolsAPI.Storage import Storage, Settings
from .SQLToolsAPI.Connection import Connection
from .SQLToolsAPI.History import History
from .SQLToolsAPI.Completion import Completion

MESSAGE_RUNNING_CMD = 'Executing SQL command...'
SYNTAX_PLAIN_TEXT = 'Packages/Text/Plain text.tmLanguage'
SYNTAX_SQL = 'Packages/SQL/SQL.tmLanguage'
SQLTOOLS_SETTINGS_FILE = 'SQLTools.sublime-settings'
SQLTOOLS_CONNECTIONS_FILE = 'SQLToolsConnections.sublime-settings'
SQLTOOLS_QUERIES_FILE = 'SQLToolsSavedQueries.sublime-settings'

USER_FOLDER                  = None
DEFAULT_FOLDER               = None
SETTINGS_FILENAME            = None
SETTINGS_FILENAME_DEFAULT    = None
CONNECTIONS_FILENAME         = None
CONNECTIONS_FILENAME_DEFAULT = None
QUERIES_FILENAME             = None
QUERIES_FILENAME_DEFAULT     = None
settingsStore                = None
queriesStore                 = None
connectionsStore             = None
historyStore                 = None

# create pluggin logger
DEFAULT_LOG_LEVEL = logging.WARNING
plugin_logger = logging.getLogger(__package__)
# some plugins are not playing by the rules and configure the root loger
plugin_logger.propagate = False
if not plugin_logger.handlers:
    plugin_logger_handler = logging.StreamHandler()
    plugin_logger_formatter = logging.Formatter("[{name}] {levelname}: {message}", style='{')
    plugin_logger_handler.setFormatter(plugin_logger_formatter)
    plugin_logger.addHandler(plugin_logger_handler)
plugin_logger.setLevel(DEFAULT_LOG_LEVEL)
logger = logging.getLogger(__name__)


def getSublimeUserFolder():
    return os.path.join(sublime.packages_path(), 'User')


def startPlugin():
    global USER_FOLDER, DEFAULT_FOLDER
    global SETTINGS_FILENAME, SETTINGS_FILENAME_DEFAULT
    global CONNECTIONS_FILENAME, CONNECTIONS_FILENAME_DEFAULT
    global QUERIES_FILENAME, QUERIES_FILENAME_DEFAULT
    global settingsStore, queriesStore, connectionsStore, historyStore

    USER_FOLDER = getSublimeUserFolder()
    DEFAULT_FOLDER = os.path.dirname(__file__)

    SETTINGS_FILENAME            = os.path.join(USER_FOLDER, SQLTOOLS_SETTINGS_FILE)
    SETTINGS_FILENAME_DEFAULT    = os.path.join(DEFAULT_FOLDER, SQLTOOLS_SETTINGS_FILE)
    CONNECTIONS_FILENAME         = os.path.join(USER_FOLDER, SQLTOOLS_CONNECTIONS_FILE)
    CONNECTIONS_FILENAME_DEFAULT = os.path.join(DEFAULT_FOLDER, SQLTOOLS_CONNECTIONS_FILE)
    QUERIES_FILENAME             = os.path.join(USER_FOLDER, SQLTOOLS_QUERIES_FILE)
    QUERIES_FILENAME_DEFAULT     = os.path.join(DEFAULT_FOLDER, SQLTOOLS_QUERIES_FILE)

    try:
        settingsStore = Settings(SETTINGS_FILENAME, default=SETTINGS_FILENAME_DEFAULT)
    except Exception as e:
        msg = '{0}: Failed to parse {1} file'.format(__package__, SQLTOOLS_SETTINGS_FILE)
        logging.exception(msg)
        Window().status_message(msg)

    try:
        connectionsStore = Settings(CONNECTIONS_FILENAME, default=CONNECTIONS_FILENAME_DEFAULT)
    except Exception as e:
        msg = '{0}: Failed to parse {1} file'.format(__package__, SQLTOOLS_CONNECTIONS_FILE)
        logging.exception(msg)
        Window().status_message(msg)

    queriesStore = Storage(QUERIES_FILENAME, default=QUERIES_FILENAME_DEFAULT)
    historyStore = History(settingsStore.get('history_size', 100))

    if settingsStore.get('debug', False):
        plugin_logger.setLevel(logging.DEBUG)
    else:
        plugin_logger.setLevel(DEFAULT_LOG_LEVEL)

    Connection.setTimeout(settingsStore.get('thread_timeout', 15))
    Connection.setHistoryManager(historyStore)

    logger.info('plugin (re)loaded')
    logger.info('version %s', __version__)


def readConnections():
    mergedConnections = {}

    # fixes #39 and #45
    if not connectionsStore:
        startPlugin()

    # global connections
    globalConnectionsDict = connectionsStore.get('connections', {})
    # project-specific connections
    projectConnectionsDict = {}
    projectData = Window().project_data()
    if projectData:
        projectConnectionsDict = projectData.get('connections', {})

    # merge connections
    mergedConnections = globalConnectionsDict.copy()
    mergedConnections.update(projectConnectionsDict)

    ordered = OrderedDict(sorted(mergedConnections.items()))

    return ordered


def getDefaultConnectionName():
    default = connectionsStore.get('default', False)
    if not default:
        return
    return default


def createOutput(panel=None, syntax=None, prependText=None):
    onInitialOutput = None
    if not panel:
        panel, onInitialOutput = getOutputPlace(syntax)
    if prependText:
        panel.run_command('append', {'characters': str(prependText)})

    initial = True

    def append(outputContent):
        nonlocal initial
        if initial:
            initial = False
            if onInitialOutput:
                onInitialOutput()

        # append content
        panel.set_read_only(False)
        panel.run_command('append', {'characters': outputContent})
        panel.set_read_only(True)

    return append


def toNewTab(content, name="", suffix="SQLTools Saved Query"):
    resultContainer = Window().new_file()
    resultContainer.set_name(
        ((name + " - ") if name != "" else "") + suffix)
    resultContainer.set_syntax_file(SYNTAX_SQL)
    resultContainer.run_command('append', {'characters': content})


def insertContent(content):
    view = View()
    # getting the settings local to this view/tab
    viewSettings = view.settings()
    # saving the original settings for "auto_indent", or True if none set
    autoIndent = viewSettings.get('auto_indent', True)
    # turn off automatic indenting otherwise the tabbing of the original
    # string is not respected after a newline is encountered
    viewSettings.set('auto_indent', False)
    view.run_command('insert', {'characters': content})
    # restore "auto_indent" setting
    viewSettings.set('auto_indent', autoIndent)


def getOutputPlace(syntax=None, name="SQLTools Result"):
    showResultOnWindow = settingsStore.get('show_result_on_window', False)
    if not showResultOnWindow:
        resultContainer = Window().find_output_panel(name)
        if resultContainer is None:
            resultContainer = Window().create_output_panel(name)
    else:
        resultContainer = None
        views = Window().views()
        for view in views:
            if view.name() == name:
                resultContainer = view
                break
        if not resultContainer:
            resultContainer = Window().new_file()
            resultContainer.set_name(name)

    resultContainer.set_scratch(True)  # avoids prompting to save
    resultContainer.set_read_only(True)
    resultContainer.settings().set("word_wrap", "false")

    def onInitialOutputCallback():
        if settingsStore.get('clear_output', False):
            resultContainer.set_read_only(False)
            resultContainer.run_command('select_all')
            resultContainer.run_command('left_delete')
            resultContainer.set_read_only(True)

        # set custom syntax highlight, only if one was passed explicitly,
        # otherwise use Plain Text syntax
        if syntax:
            # if custom and SQL related, use that, otherwise defaults to SQL
            if 'sql' in syntax.lower():
                resultContainer.set_syntax_file(syntax)
            else:
                resultContainer.set_syntax_file(SYNTAX_SQL)
        else:
            resultContainer.set_syntax_file(SYNTAX_PLAIN_TEXT)

        # hide previously set command running message (if any)
        Window().status_message('')

        if not showResultOnWindow:
            # if case this is an output pannel, show it
            Window().run_command("show_panel", {"panel": "output." + name})

        if settingsStore.get('focus_on_result', False):
            Window().focus_view(resultContainer)

    return resultContainer, onInitialOutputCallback


def getSelectionText():
    text = []

    selectionRegions = getSelectionRegions()

    if not selectionRegions:
        return text

    for region in selectionRegions:
        text.append(View().substr(region))

    return text


def getSelectionRegions():
    expandedRegions = []

    if not View().sel():
        return None

    # If we would need to expand the empty selection, then which type:
    #   'file', 'view' = use text of current view
    #   'paragraph' =  paragraph(s) (text between newlines)
    #   'line' = current line(s)
    expandTo = settingsStore.get('expand_to', 'file')
    if not expandTo:
        expandTo = 'file'

    # keep compatibility with previous settings
    expandToParagraph = settingsStore.get('expand_to_paragraph')
    if expandToParagraph is True:
        expandTo = 'paragraph'

    expandTo = str(expandTo).strip()
    if expandTo not in ['file', 'view', 'paragraph', 'line']:
        expandTo = 'file'

    for region in View().sel():
        # if user did not select anything - expand selection,
        # otherwise use the currently selected region
        if region.empty():
            if expandTo in ['file', 'view']:
                region = sublime.Region(0, View().size())
                # no point in further iterating over selections, just use entire file
                return [region]
            elif expandTo == 'paragraph':
                region = expand_to_paragraph(View(), region.b)
            else:
                # expand to line
                region = View().line(region)

        # even if we could not expand, avoid adding empty regions
        if not region.empty():
            expandedRegions.append(region)

    return expandedRegions


def getCurrentSyntax():
    view = View()
    currentSyntax = None
    if view:
        currentSyntax = view.settings().get('syntax')
    return currentSyntax


class ST(EventListener):
    connectionDict   = None
    conn             = None
    tables           = []
    columns          = []
    functions        = []
    completion       = None

    @staticmethod
    def bootstrap():
        ST.connectionDict = readConnections()
        ST.setDefaultConnection()

    @staticmethod
    def setDefaultConnection():
        default = getDefaultConnectionName()
        if not default:
            return
        if default not in ST.connectionDict:
            logger.error('connection "%s" set as default, but it does not exists', default)
            return
        logger.info('default connection is set to "%s"', default)
        ST.setConnection(default)

    @staticmethod
    def setConnection(connectionName, callback=None):
        if not connectionName:
            return

        if connectionName not in ST.connectionDict:
            return

        settings = settingsStore.all()
        config = ST.connectionDict.get(connectionName)

        promptKeys = [key for key, value in config.items() if value is None]
        promptDict = {}
        logger.info('[setConnection] prompt keys {}'.format(promptKeys))

        def mergeConfig(config, promptedKeys=None):
            merged = config.copy()
            if promptedKeys:
                merged.update(promptedKeys)
            return merged

        def createConnection(connectionName, config, settings, callback=None):
            # if DB cli binary could not be found in path a FileNotFoundError is thrown
            try:
                ST.conn = Connection(connectionName, config, settings=settings)
            except FileNotFoundError as e:
                # use only first line of the Exception in status message
                Window().status_message(__package__ + ": " + str(e).splitlines()[0])
                raise e
            ST.loadConnectionData(callback)

        if not promptKeys:
            createConnection(connectionName, config, settings, callback)
            return


        def setMissingKey(key, value):
            nonlocal promptDict
            if value is None:
                return
            promptDict[key] = value
            if promptKeys:
                promptNext()
            else:
                merged = mergeConfig(config, promptDict)
                createConnection(connectionName, merged, settings, callback)

        def promptNext():
            nonlocal promptKeys
            if not promptKeys:
                merged = mergeConfig(config, promptDict)
                createConnection(connectionName, merged, settings, callback)
            key = promptKeys.pop();
            Window().show_input_panel(
                    'Connection ' + key,
                    '',
                    lambda userInput: setMissingKey(key, userInput),
                    None,
                    None)

        promptNext()

    @staticmethod
    def loadConnectionData(callback=None):
        # clear the list of identifiers (in case connection is changed)
        ST.tables = []
        ST.columns = []
        ST.functions = []
        ST.completion = None
        objectsLoaded = 0

        if not ST.conn:
            return

        def afterAllDataHasLoaded():
            ST.completion = Completion(ST.tables, ST.columns, ST.functions, settings=settingsStore)
            logger.info('completions loaded')
            if (callback):
                callback()

        def tablesCallback(tables):
            ST.tables = tables
            nonlocal objectsLoaded
            objectsLoaded += 1
            logger.info('loaded tables : "{0}"'.format(tables))
            if objectsLoaded == 3:
                afterAllDataHasLoaded()

        def columnsCallback(columns):
            ST.columns = columns
            nonlocal objectsLoaded
            objectsLoaded += 1
            logger.info('loaded columns : "{0}"'.format(columns))
            if objectsLoaded == 3:
                afterAllDataHasLoaded()

        def functionsCallback(functions):
            ST.functions = functions
            nonlocal objectsLoaded
            objectsLoaded += 1
            logger.info('loaded functions: "{0}"'.format(functions))
            if objectsLoaded == 3:
                logger.info('all objects loaded')
                afterAllDataHasLoaded()

        ST.conn.getTables(tablesCallback)
        ST.conn.getColumns(columnsCallback)
        ST.conn.getFunctions(functionsCallback)

    @staticmethod
    def selectConnectionQuickPanel(callback=None):
        ST.connectionDict = readConnections()
        if len(ST.connectionDict) == 0:
            sublime.message_dialog('You need to setup your connections first.')
            return

        def connectionMenuList(connDictionary):
            menuItemsList = []
            template = '{dbtype}://{user}{host}{port}{db}'
            for name, config in ST.connectionDict.items():
                dbtype = config.get('type', '')
                user = '{}@'.format(config.get('username', '')) if 'username' in config else ''
                # user = config.get('username', '')
                host=config.get('host', '')
                port = ':{}'.format(config.get('port', '')) if 'port' in config else ''
                db = '/{}'.format(config.get('database', '')) if 'database' in config else ''
                connectionInfo = template.format(
                    dbtype=dbtype,
                    user=user,
                    host=host,
                    port=port,
                    db=db)
                menuItemsList.append([name, connectionInfo])
                menuItemsList.sort()
            return menuItemsList

        def onConnectionSelected(index, callback):
            menuItemsList = connectionMenuList(ST.connectionDict)
            if index < 0 or index >= len(menuItemsList):
                return
            connectionName = menuItemsList[index][0]
            ST.setConnection(connectionName, callback)
            logger.info('Connection "{0}" selected'.format(connectionName))

        menu = connectionMenuList(ST.connectionDict)
        # show pannel with callback above
        Window().show_quick_panel(menu, lambda index: onConnectionSelected(index, callback))

    @staticmethod
    def showTablesQuickPanel(callback):
        if len(ST.tables) == 0:
            sublime.message_dialog('Your database has no tables.')
            return

        ST.showQuickPanelWithSelection(ST.tables, callback)

    @staticmethod
    def showFunctionsQuickPanel(callback):
        if len(ST.functions) == 0:
            sublime.message_dialog('Your database has no functions.')
            return

        ST.showQuickPanelWithSelection(ST.functions, callback)

    @staticmethod
    def showQuickPanelWithSelection(arrayOfValues, callback):
        w = Window();
        view = w.active_view()
        selection = view.sel()[0]

        initialText = ''
        # ignore obvious non-identifier selections
        if selection.size() <= 128:
            (row_begin,_) = view.rowcol(selection.begin())
            (row_end,_) = view.rowcol(selection.end())
            # only consider selections within same line
            if row_begin == row_end:
                initialText = view.substr(selection)

        w.show_quick_panel(arrayOfValues, callback)
        w.run_command('insert', {'characters': initialText})
        w.run_command("select_all")

    @staticmethod
    def on_query_completions(view, prefix, locations):
        # skip completions, if no connection
        if ST.conn is None:
            return None

        if ST.completion is None:
            return None

        if ST.completion.isDisabled():
            return None

        if not len(locations):
            return None

        ignoreSelectors = ST.completion.getIgnoreSelectors()
        if ignoreSelectors:
            for selector in ignoreSelectors:
                if view.match_selector(locations[0], selector):
                    return None

        activeSelectors = ST.completion.getActiveSelectors()
        if activeSelectors:
            for selector in activeSelectors:
                if view.match_selector(locations[0], selector):
                    break
            else:
                return None

        # sublimePrefix = prefix
        # sublimeCompletions = view.extract_completions(sublimePrefix, locations[0])

        # preferably get prefix ourselves instead of using default sublime "prefix".
        # Sublime will return only last portion of this preceding text. Given:
        # SELECT table.col|
        # sublime will return: "col", and we need: "table.col"
        # to know more precisely which completions are more appropriate

        # get a Region that starts at the beginning of current line
        # and ends at current cursor position
        currentPoint = locations[0]
        lineStartPoint = view.line(currentPoint).begin()
        lineStartToLocation = sublime.Region(lineStartPoint, currentPoint)
        try:
            lineStr = view.substr(lineStartToLocation)
            prefix = re.split('[^`\"\w.\$]+', lineStr).pop()
        except Exception as e:
            logger.debug(e)

        # use current paragraph as sql text to parse
        sqlRegion = expand_to_paragraph(view, currentPoint)
        sql = view.substr(sqlRegion)
        sqlToCursorRegion = sublime.Region(sqlRegion.begin(), currentPoint)
        sqlToCursor = view.substr(sqlToCursorRegion)

        # get completions
        autoCompleteList, inhibit = ST.completion.getAutoCompleteList(prefix, sql, sqlToCursor)

        # safe check here, so even if we return empty completions and inhibit is true
        # we return empty completions to show default sublime completions
        if autoCompleteList is None or len(autoCompleteList) == 0:
            return None

        if inhibit:
            return (autoCompleteList, sublime.INHIBIT_WORD_COMPLETIONS)

        return autoCompleteList


# #
# # Commands
# #


# Usage for old keybindings defined by users
class StShowConnectionMenu(WindowCommand):
    @staticmethod
    def run():
        Window().run_command('st_select_connection')


class StSelectConnection(WindowCommand):
    @staticmethod
    def run():
        ST.selectConnectionQuickPanel()


class StShowRecords(WindowCommand):
    @staticmethod
    def run():
        if not ST.conn:
            ST.selectConnectionQuickPanel(callback=lambda: Window().run_command('st_show_records'))
            return

        def onTableSelected(index):
            if index < 0:
                return None
            Window().status_message(MESSAGE_RUNNING_CMD)
            tableName = ST.tables[index]
            prependText = 'Table "{tableName}"\n'.format(tableName=tableName)
            return ST.conn.getTableRecords(
                tableName,
                createOutput(prependText=prependText))

        ST.showTablesQuickPanel(callback=onTableSelected)


class StDescTable(WindowCommand):
    @staticmethod
    def run():
        currentSyntax = getCurrentSyntax()

        if not ST.conn:
            ST.selectConnectionQuickPanel(callback=lambda: Window().run_command('st_desc_table'))
            return

        def onTableSelected(index):
            if index < 0:
                return None
            Window().status_message(MESSAGE_RUNNING_CMD)
            return ST.conn.getTableDescription(ST.tables[index], createOutput(syntax=currentSyntax))

        ST.showTablesQuickPanel(callback=onTableSelected)


class StDescFunction(WindowCommand):
    @staticmethod
    def run():
        currentSyntax = getCurrentSyntax()

        if not ST.conn:
            ST.selectConnectionQuickPanel(callback=lambda: Window().run_command('st_desc_function'))
            return

        def onFunctionSelected(index):
            if index < 0:
                return None
            Window().status_message(MESSAGE_RUNNING_CMD)
            functionName = ST.functions[index].split('(', 1)[0]
            return ST.conn.getFunctionDescription(functionName, createOutput(syntax=currentSyntax))

        # get everything until first occurrence of "(", e.g. get "function_name"
        # from "function_name(int)"
        ST.showFunctionsQuickPanel(callback=onFunctionSelected)


class StRefreshConnectionData(WindowCommand):
    @staticmethod
    def run():
        if not ST.conn:
            return
        ST.loadConnectionData()


class StExplainPlan(WindowCommand):
    @staticmethod
    def run():
        if not ST.conn:
            ST.selectConnectionQuickPanel(callback=lambda: Window().run_command('st_explain_plan'))
            return

        Window().status_message(MESSAGE_RUNNING_CMD)
        ST.conn.explainPlan(getSelectionText(), createOutput())


class StExecute(WindowCommand):
    @staticmethod
    def run():
        if not ST.conn:
            ST.selectConnectionQuickPanel(callback=lambda: Window().run_command('st_execute'))
            return

        Window().status_message(MESSAGE_RUNNING_CMD)
        ST.conn.execute(getSelectionText(), createOutput())


class StExecuteAll(WindowCommand):
    @staticmethod
    def run():
        if not ST.conn:
            ST.selectConnectionQuickPanel(callback=lambda: Window().run_command('st_execute_all'))
            return

        Window().status_message(MESSAGE_RUNNING_CMD)
        allText = View().substr(sublime.Region(0, View().size()))
        ST.conn.execute(allText, createOutput())


class StFormat(TextCommand):
    @staticmethod
    def run(edit):
        selectionRegions = getSelectionRegions()

        if not selectionRegions:
            return

        for region in selectionRegions:
            textToFormat = View().substr(region)
            View().replace(edit, region, Utils.formatSql(textToFormat, settingsStore.get('format', {})))


class StFormatAll(TextCommand):
    @staticmethod
    def run(edit):
        region = sublime.Region(0, View().size())
        textToFormat = View().substr(region)
        View().replace(edit, region, Utils.formatSql(textToFormat, settingsStore.get('format', {})))


class StVersion(WindowCommand):
    @staticmethod
    def run():
        sublime.message_dialog('Using {0} {1}'.format(__package__, __version__))


class StHistory(WindowCommand):
    @staticmethod
    def run():
        if not ST.conn:
            ST.selectConnectionQuickPanel(callback=lambda: Window().run_command('st_history'))
            return

        if len(historyStore.all()) == 0:
            sublime.message_dialog('History is empty.')
            return

        def cb(index):
            if index < 0:
                return None
            return ST.conn.execute(historyStore.get(index), createOutput())

        Window().show_quick_panel(historyStore.all(), cb)


class StSaveQuery(WindowCommand):
    @staticmethod
    def run():
        query = getSelectionText()

        def cb(alias):
            queriesStore.add(alias, query)
        Window().show_input_panel('Query alias', '', cb, None, None)


class StListQueries(WindowCommand):
    @staticmethod
    def run(mode="run"):
        if mode == "run" and not ST.conn:
            ST.selectConnectionQuickPanel(callback=lambda: Window().run_command('st_list_queries',
                                                                               {'mode': mode}))
            return

        queriesList = queriesStore.all()
        if len(queriesList) == 0:
            sublime.message_dialog('No saved queries.')
            return

        options = []
        for alias, query in queriesList.items():
            options.append([str(alias), str(query)])
        options.sort()

        def cb(index):
            if index < 0:
                return None

            alias, query = options[index]
            if mode == "run":
                ST.conn.execute(query, createOutput())
            elif mode == "insert":
                insertContent(query)
            else:
                toNewTab(query, alias)

            return

        try:
            Window().show_quick_panel(options, cb)
        except Exception:
            pass


class StRemoveSavedQuery(WindowCommand):
    @staticmethod
    def run():
        if not ST.conn:
            ST.selectConnectionQuickPanel(callback=lambda: Window().run_command('st_remove_saved_query'))
            return

        queriesList = queriesStore.all()
        if len(queriesList) == 0:
            sublime.message_dialog('No saved queries.')
            return

        options = []
        for alias, query in queriesList.items():
            options.append([str(alias), str(query)])
        options.sort()

        def cb(index):
            if index < 0:
                return None

            return queriesStore.delete(options[index][0])
        try:
            Window().show_quick_panel(options, cb)
        except Exception:
            pass


def Window():
    return sublime.active_window()


def View():
    return Window().active_view()


def reload():
    try:
        # python 3.0 to 3.3
        import imp
        imp.reload(sys.modules[__package__ + ".SQLToolsAPI"])
        imp.reload(sys.modules[__package__ + ".SQLToolsAPI.Utils"])
        imp.reload(sys.modules[__package__ + ".SQLToolsAPI.Completion"])
        imp.reload(sys.modules[__package__ + ".SQLToolsAPI.Storage"])
        imp.reload(sys.modules[__package__ + ".SQLToolsAPI.History"])
        imp.reload(sys.modules[__package__ + ".SQLToolsAPI.Command"])
        imp.reload(sys.modules[__package__ + ".SQLToolsAPI.Connection"])
    except Exception as e:
        raise (e)

    try:
        ST.bootstrap()
    except Exception:
        pass


def plugin_loaded():

    try:
        from package_control import events

        if events.install(__name__):
            logger.info('Installed %s!' % events.install(__name__))
        elif events.post_upgrade(__name__):
            logger.info('Upgraded to %s!' % events.post_upgrade(__name__))
            sublime.message_dialog(('{0} was upgraded.' +
                                    'If you have any problem,' +
                                    'just restart your Sublime Text.'
                                    ).format(__name__)
                                   )

    except Exception:
        pass

    startPlugin()
    reload()


def plugin_unloaded():
    if plugin_logger.handlers:
        plugin_logger.handlers.pop()