# ============================================================================
# This file is part of Pwman3.
#
# Pwman3 is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License, version 2
# as published by the Free Software Foundation;
#
# Pwman3 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 Pwman3; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
# ============================================================================
# Copyright (C) 2017 Oz Nahum Tiram <oz.tiram@gmail.com>
# ============================================================================
"""
Define the CLI interface for pwman3 and the helper functions
"""

import subprocess as sp
import getpass
import sys
import colorama
import ast
from pwman.util.callback import Callback
from pwman.util.crypto_engine import generate_password


if sys.version_info.major > 2:  # pragma: no cover
    raw_input = input

if not sys.platform.startswith('win'):
    import termios  # noqa
    import fcntl    # noqa
    import readline
    _readline_available = True
else:  # pragma: no cover
    try:
        import readline
        _readline_available = True
    except ImportError as e:
        try:
            import pyreadline as readrline  # noqa
            _readline_available = True
        except ImportError as e:
            _readline_available = False

_defaultwidth = 10


class ANSI(object):

    """
    ANSI Colors
    """
    Reset = 0
    Bold = 1
    Underscore = 2

    Black = 30
    Red = 31
    Green = 32
    Yellow = 33
    Blue = 34
    Magenta = 35
    Cyan = 36
    White = 37


def typeset(text, color, bold=False, underline=False,
            has_colorama=True):  # pragma: no cover
    """
    print colored strings using colorama
    """
    if not has_colorama:
        return text
    if bold:
        text = colorama.Style.BRIGHT + text
    if underline and 'win32' not in sys.platform:
        text = ANSI.Underscore + text
    return color + text + colorama.Style.RESET_ALL


def text_to_clipboards(text):  # pragma: no cover
    """
    copy text to clipboard
    credit:
    https://pythonadventures.wordpress.com/tag/xclip/
    """
    # "primary":
    try:
        xsel_proc = sp.Popen(['xsel', '-pi'], stdin=sp.PIPE)
        xsel_proc.communicate(text)
        # "clipboard":
        xsel_proc = sp.Popen(['xsel', '-bi'], stdin=sp.PIPE)
        xsel_proc.communicate(text)
    except OSError as e:
        print(e, "\nExecuting xsel failed, is it installed ?\n \
              please check your configuration file ... ")


def text_to_mcclipboard(text):  # pragma: no cover
    """
    copy text to mac os x clip board
    credit:
    https://pythonadventures.wordpress.com/tag/xclip/
    """
    # "primary":
    try:
        pbcopy_proc = sp.Popen(['pbcopy'], stdin=sp.PIPE)
        pbcopy_proc.communicate(text)
    except OSError as e:
        print(e, "\nExecuting pbcoy failed...")


def open_url(link, macosx=False,):  # pragma: no cover
    """
    launch xdg-open or open in MacOSX with url
    """
    import webbrowser
    try:
        webbrowser.open(link)
    except webbrowser.Error as E:
        print("Executing open_url failed with:\n", E)


def getinput(question, default="", reader=input,
             completer=None, width=_defaultwidth, drop="drop"):  # pragma: no cover
    """
    http://stackoverflow.com/questions/2617057/\
            supply-inputs-to-python-unittests
    """
    if reader == input:
        if not _readline_available:
            val = input(question.ljust(width))
            if val == "drop":
                return ""
            elif val:
                return val
            else:
                return default
        else:
            def defaulter():
                """define default behavior startup"""
                readline.insert_text(default)

            if _readline_available:
                readline.set_startup_hook(defaulter)
                readline.get_completer()
                readline.set_completer(completer)

            x = input(question.ljust(width))

            if _readline_available:
                readline.set_startup_hook()
            if x == "drop":
                return ""
            elif x:
                return x
            else:
                return default

    else:
        return reader()


def get_or_create_pass():  # pragma: no cover

    p = getpass.getpass(prompt='Password (leave empty to create one):')

    if p:
        return p

    while not p:
        print("Password length (default: 8):", end="")
        sys.stdout.flush()
        ans = sys.stdin.readline().strip()
        try:
            ans = ast.literal_eval(ans)
            if isinstance(ans, int):
                kwargs = {'pass_len': ans}
                break
            elif isinstance(ans, dict):
                kwargs = ans
                break
            else:
                print("Did not understand your input...")
                continue
        except ValueError:
            print("Something evil happend.")
            print("Did not understand your input...")
            continue
        except SyntaxError:
            kwargs = {}
            break

    p = generate_password(**kwargs)
    return p


def _get_secret():
    if sys.stdin.isatty():  # pragma: no cover
        p = get_or_create_pass()
    else:
        p = sys.stdin.readline().rstrip()

    return p


def set_selection(new_node, items, selection, reader):  # pragma: no cover
    if selection == 0:
        new_node.username = getinput("Username:", new_node.username)
        items[0].getter = new_node.username
    elif selection == 1:  # for password
        new_node.password = _get_secret()
        items[1].getter = new_node.password
    elif selection == 2:
        new_node.url = getinput("Url:", new_node.url)
        items[2].getter = new_node.url
    elif selection == 3:  # for notes
        new_node.notes = getinput("Notes :", new_node.notes)
        items[3].getter = new_node.notes
    elif selection == 4:
        taglist = getinput(
            "Tags:", " ".join(map(bytes.decode, new_node.tags)))
        tags = taglist.split()
        new_node.tags = tags
        items[4].getter = ','.join(t for t in tags)


class CMDLoop(object):

    """
    The menu that drives editing of a node
    """

    def __init__(self, config):
        self.items = []
        self.config = config

    def add(self, item):
        if isinstance(item, CliMenuItem):
            self.items.append(item)

    def run(self, new_node=None, reader=raw_input):
        while True:
            for i, x in enumerate(self.items):
                print("%s - %s: %s" % (i + 1, x.name, x.getter))

            print("X - Finish editing")
            # read just the first character entered
            option = reader("Enter your choice:")[0]
            try:
                print("Selection, ", option)
                # substract 1 because array subscripts start at 0
                selection = int(option) - 1
                set_selection(new_node, self.items, selection, reader)
            except (ValueError, IndexError):  # pragma: no cover
                if (option.upper() == 'X'):
                    break
                print("Invalid selection")


class CliMenuItem(object):

    def __init__(self, name, getter):
        self.name = name
        self.getter = getter


class CLICallback(Callback):  # pragma: no cover

    def getinput(self, question):
        return raw_input(question)

    def getsecret(self, question):
        return getpass.getpass(question + ":")

    def getnewsecret(self, question):
        return getpass.getpass(question + ":")
