#============================================================================
# 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) 2013 Oz Nahum <nahumoz@gmail.com>
#============================================================================
"""
Define the CLI interface for pwman3 and the helper functions
"""
from __future__ import print_function
from pwman.util.callback import Callback
import subprocess as sp
import getpass
import sys
import struct
import os
import colorama
from pwman.data.tags import TagNew as Tag
from pwman.util.config import get_pass_conf
import pwman.util.generator as generator

if sys.version_info.major > 2:
    raw_input = input

if sys.platform != 'win32':
    import termios
    import fcntl
    import tty
    import readline
    _readline_available = True
else:  # pragma: no cover
    try:
        #import pyreadline as readline
        import readline
        _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 not 'win32' in sys.platform:
        text = ANSI.Underscore + text
    return color + text + colorama.Style.RESET_ALL


def select(question, possible):  # pragma: no cover
    """
    select input from user
    """
    for i in range(0, len(possible)):
        print ("%d - %-" + str(_defaultwidth) + "s") % (i + 1, possible[i])
    while 1:
        uinput = getonechar(question)
        if uinput.isdigit() and int(uinput) in range(1, len(possible) + 1):
            return possible[int(uinput) - 1]


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
    """
    uopen = "xdg-open"
    if macosx:
        uopen = "open"
    try:
        sp.Popen([uopen, link], stdin=sp.PIPE)
    except OSError as e:
        print ("Executing open_url failed with:\n", e)


def getpassword(question, argsgiven=None,
                width=_defaultwidth, echo=False,
                reader=getpass.getpass, numerics=False, leetify=False,
                symbols=False, special_signs=False,
                length=None, config=None):  # pragma: no cover
    if argsgiven == 1 or length:
        while not length:
            try:
                default_length = config.get_value(
                    'Generator', 'default_pw_length') or '8'
                length = getinput(
                    "Password length (default %s): " % default_length,
                    default=default_length)
                length = int(length)
            except ValueError:
                print("please enter a proper integer")

        password, dumpme = generator.generate_password(
            length, length, True, symbols=leetify, numerics=numerics,
            special_chars=special_signs)
        print ("New password: %s" % (password))
        return password
    # no args given
    while True:
        a1 = reader(question.ljust(width))
        if not a1:
            return getpassword(
                '', argsgiven=1, width=width, echo=echo, reader=reader,
                numerics=numerics, leetify=leetify, symbols=symbols,
                special_signs=special_signs, length=length, config=config)
        a2 = reader("[Repeat] %s" % (question.ljust(width)))
        if a1 == a2:
            if leetify:
                return generator.leetify(a1)
            else:
                return a1
        else:
            print ("Passwords don't match. Try again.")


def gettermsize():  # pragma: no cover
    s = struct.pack("HHHH", 0, 0, 0, 0)
    f = sys.stdout.fileno()
    x = fcntl.ioctl(f, termios.TIOCGWINSZ, s)
    rows, cols, width, height = struct.unpack("HHHH", x)
    return rows, cols


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

            x = raw_input(question.ljust(width))
            readline.set_completer(completer)
            readline.set_startup_hook()
            if not x:
                return default
            return x
    else:
        return reader()


class CMDLoop(object):  # pragma: no cover

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

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

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

    def run(self, new_node=None, reader=raw_input):
        while True:
            i = 0
            for x in self.items:
                i = i + 1
                try:
                    current = x.getter
                except AttributeError:
                    current = x

                # when printing tags, we have list ...
                currentstr = b''
                if type(current) == list:
                    for c in current:
                        print(c, type(c))
                        try:
                            currentstr += b' ' + c
                        except TypeError:
                            currentstr += b' ' + c.name
                # for the case we are not dealing with
                # a list of tags
                else:
                    currentstr = current

                print ("%s - %s: %s" % (i, x.name, currentstr))
            print("X - Finish editing")
            option = reader("Enter your choice:")[0]
            try:
                print ("Selection, ", option)
                # substract 1 because array subscripts start at 0
                selection = int(option) - 1
                # new value is created by calling the editor with the
                # previous value as a parameter
                # TODO: enable overriding password policy as if new node
                # is created.
                if selection == 0:
                    new_node.username = getinput("Username:")
                    self.items[0].getter = new_node.username
                    self.items[0].setter = new_node.username
                elif selection == 1:  # for password
                    numerics, leet, s_chars = get_pass_conf()
                    new_node.password = getpassword(
                        'New Password:', numerics=numerics, leetify=leet,
                        special_signs=s_chars)
                    self.items[1].getter = new_node.password
                    self.items[1].setter = new_node.password
                elif selection == 2:
                    new_node.url = getinput("Url:")
                    self.items[2].getter = new_node.url
                    self.items[2].setter = new_node.url
                elif selection == 3:  # for notes
                    # new_node.notes = getinput("Notes:")
                    new_node.notes = reader("Notes:")
                    self.items[3].getter = new_node.notes
                    self.items[3].setter = new_node.notes
                elif selection == 4:
                    taglist = getinput("Tags:")
                    tagstrings = taglist.split()
                    tags = [Tag(tn) for tn in tagstrings]
                    new_node.tags = tags
                    self.items[4].setter = new_node.tags
                    self.items[4].getter = new_node.tags
            except (ValueError, IndexError):
                if (option.upper() == 'X'):
                    break
                print("Invalid selection")


def getonechar(question, width=_defaultwidth):  # pragma: no cover
    question = "%s " % (question)
    print (question.ljust(width),)
    try:
        sys.stdout.flush()
        fd = sys.stdin.fileno()
        # tty module exists only if we are on Posix
        try:
            tty_mode = tty.tcgetattr(fd)
            tty.setcbreak(fd)
        except NameError:
            pass
        try:
            ch = os.read(fd, 1)
        finally:
            try:
                tty.tcsetattr(fd, tty.TCSAFLUSH, tty_mode)
            except NameError:
                pass
    except AttributeError:
        ch = sys.stdin.readline()[0]

    print(ch)
    return ch


class CliMenuItem(object):  # pragma: no cover

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


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 + ":")