""" turtlevcs module

This file is part of Turtle.

Turtle is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Turtle is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Turtle. If not, see <https://www.gnu.org/licenses/>. 
"""
import os
import sys
import subprocess
import argparse
import textwrap
from threading import Condition
from turtlevcs.package_information import VERSION

TURTLE_APP_ID = "de.philippun1.turtle"

MENU_ITEMS = [
    ("about", "About", "About Turtle", "help-about-symbolic"),
    ("add", "Add", "Add files to index", None),
    ("checkout", "Checkout", "Checkout a branch or commit", None),
    ("clean", "Clean", "Clean repository", None),
    ("clone", "Clone", "Clone repository", None),
    ("commit", "Commit", "Commit current changes", None),
    ("create_branch", "Create Branch", "Create new branch at a specific commit", None),
    ("create_tag", "Create Tag", "Create new tag at a specific commit", None),
    ("diff_file", "Diff", "Show diff of a file", None),
    ("diff", "Diff", "Show diffs", None),
    ("init", "Init", "Init repository", None),
    ("log", "Log", "Show commit log", None),
    ("merge", "Merge", "Merge into current branch", None),
    ("pull", "Pull", "Pull commits", None),
    ("push", "Push", "Push commits", None),
    ("references", "References", "Show references", None),
    ("remotes", "Remotes", "Show remotes", None),
    ("reset", "Reset", "Reset to a specific commit", None),
    ("resolve", "Resolve", "Resolve conflicts", None),
    ("revert", "Revert", "Revert changes", None),
    ("settings", "Settings", "Turtle settings", "emblem-system-symbolic"),
    ("stage", "Stage", "Stage parts of files", None),
    ("submodules", "Submodules", "Manage submodules", None),
    ("sync", "Sync", "Sync with remote", None),
    ("unstage", "Unstage", "Unstage files", None),
]

try:
    import pygit2

    status_icon_map = {
        pygit2.enums.FileStatus.CURRENT: "emblem-default",
        pygit2.enums.FileStatus.INDEX_NEW: "emblem-new",
        pygit2.enums.FileStatus.INDEX_MODIFIED: "emblem-important",
        pygit2.enums.FileStatus.INDEX_DELETED: "edit-delete",
        pygit2.enums.FileStatus.INDEX_RENAMED: "emblem-symbolic-link",
        pygit2.enums.FileStatus.INDEX_TYPECHANGE: "dialog-question",
        pygit2.enums.FileStatus.WT_NEW: "dialog-question",
        pygit2.enums.FileStatus.WT_MODIFIED: "emblem-important",
        pygit2.enums.FileStatus.WT_DELETED: "edit-delete",
        pygit2.enums.FileStatus.WT_TYPECHANGE: "dialog-question",
        pygit2.enums.FileStatus.WT_RENAMED: "emblem-symbolic-link",
        pygit2.enums.FileStatus.WT_UNREADABLE: "emblem-readonly",
        pygit2.enums.FileStatus.IGNORED: "emblem-dropbox-selsync",
        pygit2.enums.FileStatus.CONFLICTED: "software-update-urgent",
    }

    status_friendly_name_map = {
        pygit2.enums.FileStatus.CURRENT: "normal",
        pygit2.enums.FileStatus.INDEX_NEW: "new (staged)",
        pygit2.enums.FileStatus.INDEX_MODIFIED: "modified (staged)",
        pygit2.enums.FileStatus.INDEX_DELETED: "deleted (staged)",
        pygit2.enums.FileStatus.INDEX_RENAMED: "renamed (staged)",
        pygit2.enums.FileStatus.INDEX_TYPECHANGE: "type changed (staged)",
        pygit2.enums.FileStatus.WT_NEW: "new",
        pygit2.enums.FileStatus.WT_MODIFIED: "modified",
        pygit2.enums.FileStatus.WT_DELETED: "deleted",
        pygit2.enums.FileStatus.WT_TYPECHANGE: "type changed",
        pygit2.enums.FileStatus.WT_RENAMED: "renamed",
        pygit2.enums.FileStatus.WT_UNREADABLE: "unreadable",
        pygit2.enums.FileStatus.IGNORED: "ignored",
        pygit2.enums.FileStatus.CONFLICTED: "conflicted",
    }

    delta_status_icon_map = {
        pygit2.enums.DeltaStatus.ADDED: "emblem-new",
        pygit2.enums.DeltaStatus.CONFLICTED: "software-update-urgent",
        pygit2.enums.DeltaStatus.COPIED: "emblem-symbolic-link",
        pygit2.enums.DeltaStatus.DELETED: "edit-delete",
        pygit2.enums.DeltaStatus.IGNORED: "emblem-dropbox-selsync",
        pygit2.enums.DeltaStatus.MODIFIED: "emblem-important",
        pygit2.enums.DeltaStatus.RENAMED: "emblem-symbolic-link",
        pygit2.enums.DeltaStatus.TYPECHANGE: "dialog-question",
        pygit2.enums.DeltaStatus.UNMODIFIED: "emblem-default",
        pygit2.enums.DeltaStatus.UNREADABLE: "emblem-readonly",
        pygit2.enums.DeltaStatus.UNTRACKED: "dialog-question",
    }

    STATUS_WT_BITMAP = (
        pygit2.enums.FileStatus.CONFLICTED +
        pygit2.enums.FileStatus.WT_DELETED +
        pygit2.enums.FileStatus.WT_MODIFIED +
        pygit2.enums.FileStatus.WT_NEW +
        pygit2.enums.FileStatus.WT_RENAMED +
        pygit2.enums.FileStatus.WT_TYPECHANGE +
        pygit2.enums.FileStatus.WT_UNREADABLE)

    STATUS_INDEX_BITMAP = (
        pygit2.enums.FileStatus.INDEX_DELETED +
        pygit2.enums.FileStatus.INDEX_MODIFIED +
        pygit2.enums.FileStatus.INDEX_NEW +
        pygit2.enums.FileStatus.INDEX_RENAMED +
        pygit2.enums.FileStatus.INDEX_TYPECHANGE)
except Exception as _ex:
    pass

def has_index_status(status):
    """ check if a status contains an index status """
    index_status = status & STATUS_INDEX_BITMAP
    return index_status > 0, index_status

def has_worktree_status(status):
    """ check if a status contains an index status """
    wt_status = status & STATUS_WT_BITMAP
    return wt_status > 0, wt_status

def get_full_version():
    """ get full version string of turtle """
    try:
        repo = pygit2.Repository(os.path.realpath(__file__))
        return f"{VERSION}-{repo.get(str(repo.head.target)).short_id}"
    except Exception:
        pass
    return VERSION

def launch_dialog(module, args):
    """ helper function to launch a turtle dialog """
    basedir, _ = os.path.split(os.path.dirname(os.path.realpath(__file__)))
    path = os.path.join(basedir, "turtlevcs", "dialogs", module + ".py")

    executable = sys.executable
    if os.path.exists(path):
        if "PYTHON" in list(os.environ):
            executable = os.environ["PYTHON"]
        subprocess.run([executable, path] + args, cwd=os.getcwd(), check=False)

    else:
        path = os.path.join(basedir, "turtlevcs", "dialogs", "__init__.py")
        if os.path.exists(path):
            subprocess.run([executable, path, "turtlevcs module '" +
                           module + "' not available"], cwd=basedir, check=False)


def get_author(repo):
    """ get the author signature """
    if isinstance(repo, pygit2.Repository):
        return repo.default_signature
    return None


def get_keypair():
    """ get the ssh keypair """
    settings = get_settings()
    if settings:
        if settings.get_boolean("specific-ssh-key"):
            key = settings.get_string("ssh-key")
            if len(key) > 0 and os.path.exists(key):
                return pygit2.Keypair(None, f"{key}.pub", key, None)
        else:
            agent = settings.get_string("agent")
            if len(agent) > 0:
                return pygit2.KeypairFromAgent(agent)

    raise UserWarning("no credentials available")

def can_store_password():
    """
    password can only be stored if secretstorage is available
    and the application is not running in a flatpak
    """
    has_storage = False
    try:
        import secretstorage
        has_storage = True
    except ImportError:
        pass

    return has_storage and not is_flatpak()

def get_credentials_from_dialog(url, username_from_url):
    """ open a dialog to get credentials """
    condition = Condition()

    from turtlevcs.dialogs.enter_credentials import EnterCredentials

    credentials = EnterCredentials(
        parent=None,
        url=url,
        username_from_url=username_from_url,
        condition=condition)

    with condition:
        condition.wait()

    return credentials.get_credentials()

def get_settings():
    """ get the gio settings object """
    # pylint: disable-next=import-outside-toplevel
    from gi.repository import Gio
    source = Gio.SettingsSchemaSource.get_default()
    schema = source.lookup(TURTLE_APP_ID, True)
    if schema is not None:
        settings = Gio.Settings(schema_id=TURTLE_APP_ID)
        return settings

    return None

def is_flatpak():
    """ check if the application is running in a flatpak """
    return os.getenv("container") is not None

def create_parser(program, description):
    """ create a parser for the command line """
    modules = {
        "about": "Display information about Turtle",
        "add": "Add file(s) to the index",
        "checkout": "Checkout a branch or commit",
        "clone": "Clone a repository into a new directory",
        "commit": "Commit changes to the repository",
        "clean": "Clean untracked files in a repository",
        "create_branch": "Create a branch",
        "create_tag": "Create a tag",
        "diff_file": "Show current changes of a file",
        "diff": "Show changes between commits",
        "init": "Create an empty repository",
        "log": "Show commit logs",
        "merge": "Merge a branch or commit into current HEAD",
        "plugin_installer": "Install the nautilus plugin from the flatpak",
        "pull": "Pull the current branch",
        "push": "Push a branch",
        "references": "Show references",
        "remotes": "Manage remotes",
        "reset": "Reset current branch to a specific reference",
        "resolve": "Resolve conflicts in the working tree",
        "revert": "Revert changed files in the working tree",
        "settings": "Show and modify settings",
        "settings_menu": "Show and modify menu entries",
        "stage": "Stage a file",
        "submodules": "Initialize, update or inspect submodules",
        "sync": "Pull, push or fetch changes from or to a remote repository",
        "unstage": "Unstage a file",
        "update": "Update a branch",
    }
    parser = argparse.ArgumentParser(
        prog=program,
        description=textwrap.indent(
            textwrap.dedent(
                f"""
                Turtle {description}
                """
            ).strip(),
            "    "
        ),
        formatter_class=argparse.RawTextHelpFormatter
    )
    parser.add_argument(
        "module",
        choices=modules.keys(),
        help="\n".join([f"{k}:\t{v}".expandtabs(16) for k, v in modules.items()]).strip(),
    )

    parser.add_argument(
        "args",
        nargs=argparse.REMAINDER,
        help="arguments for the module (path, etc.)"
    )

    return parser
