#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# (c) Copyright @ 2015 HP Development Company, L.P.
#
# This program 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 2 of the License, or
# (at your option) any later version.
#
# This program 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 this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
#
# Author: Amarnath Chitumalla
#
import os
import getpass
import time
import string

from . import utils, tui
from .g import *
from .sixext import BytesIO, StringIO
from .sixext.moves import input
from . import pexpect

PASSWORD_RETRY_COUNT = 3

AUTH_TYPES = {'mepis': 'su',
              'debian': 'su',
              'suse': 'su',
              'mandriva': 'su',
              'fedora': 'su',
              'fedora28': 'sudo',
              'redhat': 'su',
              'rhel': 'su',
              'slackware': 'su',
              'gentoo': 'su',
              'redflag': 'su',
              'ubuntu': 'sudo',
              'xandros': 'su',
              'freebsd': 'su',
              'linspire': 'su',
              'ark': 'su',
              'pclinuxos': 'su',
              'centos': 'su',
              'igos': 'su',
              'linuxmint': 'sudo',
              'linpus': 'sudo',
              'gos': 'sudo',
              'boss': 'su',
              'lfs': 'su',
              'manjarolinux': 'sudo',
              'uos': 'sudo',
              'zorinos': 'sudo',
              'zorin' : 'sudo',
              'debiangnu/linux' : 'su',
              'mxlinux' : 'su',
              'elementaryos' : 'sudo',
              }


# This function promts for the username and password and returns
# (username,password)
def showPasswordPrompt(prompt):
    import getpass
    print ("")
    print ("")
    print (log.bold(prompt))
    username = input("Username: ")
    password = getpass.getpass("Password: ")

    return (username, password)


# TBD this function shoud be removed once distro class implemented
def get_distro_name():
    os_name = None
    distro_release_name = str()
    try:
        import platform
    except ImportError:
        os_name = None

    try:
        os_name = platform.dist()[0]
    except AttributeError:
        import distro
        os_name = distro.linux_distribution()[0]
        distro_release_name = distro.distro_release_attr('name')

    if not os_name:
        name = os.popen('lsb_release -i | cut -f 2')
        os_name = name.read().strip()
        name.close()

    if not os_name:
        name = os.popen("cat /etc/issue | awk '{print $1}' | head -n 1")
        os_name = name.read().strip()
        name.close()

    os_name = os_name.lower()
    if 'MX' in distro_release_name:
        os_name = "mxlinux"
    if "redhatenterprise" in os_name:
        os_name = 'rhel'    
    elif "suse" in os_name:
        os_name = 'suse'
    elif "arch" in os_name:
        os_name = 'manjarolinux'
    elif "fedora" in os_name:
        os_name = 'fedora'

    return os_name


class Password(object):

    def __init__(self, Mode=INTERACTIVE_MODE):
        self.__password = ""
        self.__password_prompt_str = ""
        self.__passwordValidated = False
        self.__mode = Mode
        self.__readAuthType()  # self.__authType
        self.__expectList = []

        if not utils.to_bool(sys_conf.get('configure', 'qt5', '0')) and not not utils.to_bool(sys_conf.get('configure', 'qt4', '0')) and utils.to_bool(sys_conf.get('configure', 'qt3', '0')):
            self.__ui_toolkit = 'qt3'
        elif not utils.to_bool(sys_conf.get('configure', 'qt5', '0')) and not utils.to_bool(sys_conf.get('configure', 'qt3', '0')) and utils.to_bool(sys_conf.get('configure', 'qt4', '0')):
            self.__ui_toolkit = 'qt4'
        elif not utils.to_bool(sys_conf.get('configure', 'qt3', '0')) and not utils.to_bool(sys_conf.get('configure', 'qt4', '0')) and utils.to_bool(sys_conf.get('configure', 'qt5', '0')):
            self.__ui_toolkit = 'qt5'

        for s in utils.EXPECT_WORD_LIST:
            try:
                p = re.compile(s, re.I)
            except TypeError:
                self.__expectList.append(s)
            else:
                self.__expectList.append(p)

    ##################### Private functions ######################

    def __readAuthType(self):
        # TBD: Getting distro name should get distro class
        # added replace() to remove the spaces in distro_name
        distro_name = get_distro_name().lower().replace(" ","")

        self.__authType = user_conf.get('authentication', 'su_sudo', '')
        if self.__authType != "su" and self.__authType != "sudo":
            try:
                self.__authType = AUTH_TYPES[distro_name]
                if distro_name == 'fedora':
                    import platform
                    try:
                       ver = int(platform.dist()[1])
                    except AttributeError:
                       import distro
                       ver = int(distro.linux_distribution()[1])
                    if ver >= 28:
                       self.__authType = AUTH_TYPES['fedora28']
            except KeyError:
                log.warn("%s distro is not found in AUTH_TYPES" % distro_name)
                self.__authType = 'su'

    def __getPasswordDisplayString(self):
        if self.__authType == "su":
            return "Please enter the root/superuser password: "
        else:
            return "Please enter the sudoer (%s)'s password: " % os.getenv('USER')

    def __changeAuthType(self):
        if self.__authType == "sudo":
            self.__authType = "su"
        else:
            self.__authType = "sudo"
        user_conf.set('authentication', 'su_sudo', self.__authType)

    def __get_password(self, pswd_msg=''):
        if pswd_msg == '':
            if self.__authType == "su":
                pswd_msg = "Please enter the root/superuser password: "
            else:
                pswd_msg = "Please enter the sudoer (%s)'s password: " % os.getenv(
                    'USER')
        return getpass.getpass(log.bold(pswd_msg))

    def __get_password_ui(self, pswd_msg='', user="root"):
        if pswd_msg == '':
            pswd_msg = "Your HP Device requires to install HP proprietary plugin\nPlease enter root/superuser password to continue"

        if self.__ui_toolkit == "qt3":
            from ui.setupform import showPasswordUI
            username, password = showPasswordUI(pswd_msg, user, False)
        elif self.__ui_toolkit == "qt5":
            from ui5.setupdialog import showPasswordUI
            username, password = showPasswordUI(pswd_msg, user, False)
        else:  # self.__ui_toolkit == "qt4" --> default qt4
            from ui4.setupdialog import showPasswordUI
            username, password = showPasswordUI(pswd_msg, user, False)

        if username == "" and password == "":
            raise Exception("User Cancel")

        return password

    def __password_check(self, cmd, timeout=10):
        import io
        output = io.StringIO()
        ok, ret = False, ''

        try:
            child = pexpect.spawnu(cmd, timeout=timeout)
        except pexpect.ExceptionPexpect:
            return 1, ''

        try:
            try:
                start = time.time()

                while True:
                    update_spinner()

                    i = child.expect(self.__expectList)

                    cb = child.before
                    if cb:
                        start = time.time()
                        output.write(cb)

                    if i == 0:  # EOF
                        ok, ret = True, output.getvalue()
                        break

                    elif i == 1:  # TIMEOUT
                        if('true' in cmd and self.__password_prompt_str == ""):  # sudo true or su -c "true"
                            cb = cb.replace("[", "\[")
                            cb = cb.replace("]", "\]")

                            self.__password_prompt_str = cb
                            try:
                                p = re.compile(cb, re.I)
                            except TypeError:
                                self.__expectList.append(cb)
                            else:
                                self.__expectList.append(p)
                            log.debug(
                                "Adding missing password prompt string [%s]" % self.__password_prompt_str)
                        continue

                    else:  # password
                        if(self.__password_prompt_str == ""):
                            self.__password_prompt_str = utils.EXPECT_WORD_LIST[
                                i]
                            log.debug(
                                "Updating password prompt string [%s]" % self.__password_prompt_str)

                        child.sendline(self.__password)

            except (Exception, pexpect.ExceptionPexpect) as e:
                log.exception()

        finally:
            cleanup_spinner()

            try:
                child.close()
            except OSError:
                pass

        if ok:
            return child.exitstatus, ret
        else:

            return 1, ''

    def __validatePassword(self, pswd_msg):
        x = 1
        while True:
            if self.__mode == INTERACTIVE_MODE:
                self.__password = self.__get_password(pswd_msg)
            else:
                try:
                    if self.getAuthType() == 'su':
                        self.__password = self.__get_password_ui(
                            pswd_msg, "root")
                    else:
                        self.__password = self.__get_password_ui(
                            pswd_msg, os.getenv("USER"))
                except Exception as ex:
                    log.debug(ex)
                    break

            cmd = self.getAuthCmd() % "true"
            log.debug(cmd)

            status, output = self.__password_check(cmd)
            log.debug("status = %s  output=%s " % (status, output))

            if self.__mode == GUI_MODE:
                if self.__ui_toolkit == "qt4":
                    from ui4.setupdialog import FailureMessageUI
                elif self.__ui_toolkit == "qt5":
                    from ui5.setupdialog import FailureMessageUI
                elif self.__ui_toolkit == "qt3":
                    from ui.setupform import FailureMessageUI

            if status == 0:
                self.__passwordValidated = True
                break
            elif "not in the sudoers file" in output:
                # TBD.. IF user doesn't have sudo permissions, needs to change
                # to "su" type and query for password
                self.__changeAuthType()
                msg = "User doesn't have sudo permissions.\nChanging Authentication Type. Try again."
                if self.__mode == GUI_MODE:
                    FailureMessageUI(msg)
                else:
                    log.error(msg)
                raise Exception("User is not in the sudoers file.")

            else:
                self.__password = ""
                x += 1
                if self.__mode == GUI_MODE:
                    if x > PASSWORD_RETRY_COUNT:
                        FailureMessageUI("Password incorrect. ")
                        return
                    else:
                        FailureMessageUI("Password incorrect. %d attempt(s) left." % (
                            PASSWORD_RETRY_COUNT + 1 - x))
                else:
                    if x > PASSWORD_RETRY_COUNT:
                        log.error("Password incorrect. ")
                        return
                    else:
                        log.error("Password incorrect. %d attempt(s) left." % (
                            PASSWORD_RETRY_COUNT + 1 - x))

    def __get_password_utils(self):
        if self.__authType == "su":
            AuthType, AuthCmd = 'su', 'su -c "%s"'
        else:
            AuthType, AuthCmd = 'sudo', 'sudo %s'

        return AuthType, AuthCmd

    def __get_password_utils_ui(self):
        distro_name = get_distro_name().lower()
        if self.__authType == "sudo":
            AuthType, AuthCmd = 'sudo', 'sudo %s'
        else:
            AuthType, AuthCmd = 'su', 'su -c "%s"'
        '''
        if utils.which('kdesu'):
            AuthType, AuthCmd = 'kdesu', 'kdesu -- %s'
        elif utils.which('kdesudo'):
            AuthType, AuthCmd = 'kdesudo', 'kdesudo -- %s'
        elif utils.which('gnomesu'):
            AuthType, AuthCmd = 'gnomesu', 'gnomesu -c "%s"'
        elif utils.which('gksu'):
            AuthType, AuthCmd = 'gksu' , 'gksu "%s"'
        '''

        return AuthType, AuthCmd

    ##################### Public functions ######################

    def clearPassword(self):
        log.debug("Clearing password...")
        self.__password = ""
        self.__passwordValidated = False
        if self.__authType == 'sudo':
            utils.run("sudo -K")

    def getAuthType(self):
        if self.__mode == INTERACTIVE_MODE:
            retValue = self.__authType
        else:
            retValue, AuthCmd = self.__get_password_utils_ui()

        return retValue

    def getAuthCmd(self):
        if self.__mode == INTERACTIVE_MODE:
            AuthType, AuthCmd = self.__get_password_utils()
        else:
            AuthType, AuthCmd = self.__get_password_utils_ui()

        return AuthCmd

    def getPassword(self, pswd_msg='', psswd_queried_cnt=0):
        if self.__passwordValidated:
            return self.__password

        if psswd_queried_cnt:
            return self.__password

        self.__validatePassword(pswd_msg)
        return self.__password

    def getPasswordPromptString(self):
        return self.__password_prompt_str
