# -*- coding: utf-8 -*-

# -----------------------------------------------------------------------------
# Copyright (c) Spyder Project Contributors
#
# Licensed under the terms of the MIT License
# (see spyder/__init__.py for details)
# -----------------------------------------------------------------------------

"""Miscellaneous utilities."""

import os
import os.path as osp
import shutil
import uuid
import psutil
from anaconda_navigator.config import LOCKFILE, PIDFILE


def __remove_pyc_pyo(fname):
    """Eventually remove .pyc and .pyo files associated to a Python script"""
    if osp.splitext(fname)[1] == '.py':
        for ending in ('c', 'o'):
            if osp.exists(fname + ending):
                os.remove(fname + ending)


def rename_file(source, dest):
    """
    Rename file from *source* to *dest*
    If file is a Python script, also rename .pyc and .pyo files if any
    """
    os.rename(source, dest)
    __remove_pyc_pyo(source)


def remove_file(fname):
    """
    Remove file *fname*
    If file is a Python script, also rename .pyc and .pyo files if any
    """
    os.remove(fname)
    __remove_pyc_pyo(fname)


def move_file(source, dest):
    """
    Move file from *source* to *dest*
    If file is a Python script, also rename .pyc and .pyo files if any
    """
    shutil.copy(source, dest)
    remove_file(source)


def abspardir(path):
    """Return absolute parent dir"""
    return osp.abspath(osp.join(path, os.pardir))


def get_common_path(pathlist):
    """Return common path for all paths in pathlist"""
    common = osp.normpath(osp.commonprefix(pathlist))
    if len(common) > 1:
        if not osp.isdir(common):
            return abspardir(common)
        for path in pathlist:
            if not osp.isdir(osp.join(common, path[len(common) + 1:])):
                # `common` is not the real common prefix
                return abspardir(common)
        return osp.abspath(common)
    return None


def path_is_writable(path):
    """Check if given path is writable."""
    path = os.path.abspath(os.path.expanduser(os.path.expandvars(path)))

    path_exists = False
    if os.path.isfile(path):
        test_filepath = path
        remove = False
    else:
        path_exists = os.path.isdir(path)
        remove = True
        if not path_exists:
            try:
                os.makedirs(path)
            except Exception:  # pylint: disable=broad-except
                return False

        test_filepath = os.path.join(path, uuid.uuid4().hex[:5])

    try:
        fh = open(test_filepath, 'a+')  # pylint: disable=consider-using-with,invalid-name,unspecified-encoding
    except (IOError, OSError):
        return False
    else:
        fh.close()
        try:
            if remove:
                os.remove(test_filepath)
            if not path_exists:
                os.rmdir(path)
        except OSError:
            pass
        return True


def save_pid():
    """Save navigator process ID."""
    pid = os.getpid()

    try:
        with open(PIDFILE, 'w') as f:  # pylint: disable=invalid-name,unspecified-encoding
            f.write(str(pid))
    except Exception:  # pylint: disable=broad-except
        pid = None

    return pid


def load_pid():
    """Load navigator process ID."""
    try:
        with open(PIDFILE, 'r') as f:  # pylint: disable=invalid-name,unspecified-encoding
            pid = f.read()
        pid = int(pid)
    except Exception:  # pylint: disable=broad-except
        pid = None

    if pid is not None:
        is_running = psutil.pid_exists(pid)
        process = None
        cmds = []
        try:
            process = psutil.Process(pid)
            if process and is_running:
                cmds = process.cmdline()
        except psutil.NoSuchProcess:
            # print('no such process')
            pass
        except psutil.AccessDenied:
            # Try to remove the pid file, if not possible return False
            if not remove_pid():
                return False

        cmds = [cmd.lower() for cmd in cmds]

        # Check bootstrap
        ch1 = [c for c in cmds if 'python' in c or 'bootstrap.py' in c]
        ch2 = [c for c in cmds if 'python' in c or 'anaconda-navigator' in c]
        ch3 = [c for c in cmds if 'navigator.app' in c]

        check = any(ch1) or any(ch2) or any(ch3)

        if not check:
            pid = None

    return pid


def remove_pid():
    """Load navigator process ID."""
    check = True
    try:
        os.remove(PIDFILE)
    except Exception:  # pylint: disable=broad-except
        check = False
    return check


def remove_lock():
    """Load navigator process ID."""
    check = True
    try:
        os.remove(LOCKFILE)
    except Exception:  # pylint: disable=broad-except
        check = False
    return check


def set_windows_appusermodelid():
    """Make sure correct icon is used on Windows 7 taskbar"""
    try:
        from ctypes import windll  # pylint: disable=import-outside-toplevel
        name = 'anaconda.Anaconda-Navigator'
        return windll.shell32.SetCurrentProcessExplicitAppUserModelID(name)
    except AttributeError:
        return 'SetCurrentProcessExplicitAppUserModelID not found'


if __name__ == '__main__':
    if os.name == 'nt':
        assert get_common_path(  # nosec
            [
                'D:\\Python\\spyder-v21\\spyder\\widgets',
                'D:\\Python\\spyder\\spyder\\utils',
                'D:\\Python\\spyder\\spyder\\widgets',
                'D:\\Python\\spyder-v21\\spyder\\utils',
            ]
        ) == 'D:\\Python'
    else:
        assert get_common_path(  # nosec
            [
                '/Python/spyder-v21/spyder.widgets',
                '/Python/spyder/spyder.utils',
                '/Python/spyder/spyder.widgets',
                '/Python/spyder-v21/spyder.utils',
            ]
        ) == '/Python'
    print(save_pid())
    print(load_pid())
    print(remove_pid())
