# Copyright 1999-2015. Parallels IP Holdings GmbH. All Rights Reserved.
import os
import fileinput, re, string
import pmm_config
import logging


class ExInvalidConfigLine(Exception):
    def __init__(self, line):
        Exception.__init__(self)
        self.line = line

    def __str__(self):
        return "Invalid config line: " + self.line


_logger = logging.getLogger("pmmcli.pmmcli_config")


instance = None


def get(key = None):
    global instance
    if not instance:
        instance = PMMCliConfig()
    if not key:
        return instance
    return instance[key]


class PMMCliConfig:
    __config = {}
    __configName = None

    def __log_info(self, keyname, exception):
        _logger.info("Exception '" + repr(exception) + "' during retrieving key " + keyname + " value: " + str(exception))

    def __init__(self, configName = None):
        if configName == None:
            configName = os.path.join(pmm_config.pmmcli_dir(), "pmmcli-rc")
        self.__configName = configName
        self.__parse(configName)

    def __len__(self):
        return len(self.__config)

    def __getitem__(self, key):
        return self.__config[key]

    def __iter__(self):
        return self.iterkeys()

    def iterkeys(self):
        return self.__config.iterkeys()

    def iteritems(self):
        return self.__config.iteritems()

    # free disk space required to backup operation (in bytes)
    def get_free_disk_space(self):
        free_disk_space = 1073741824
        try:
            free_disk_space = get("FREE_DISK_SPACE")
        except KeyError, e:
            self.__log_info("FREE_DISK_SPACE", e)
            pass
        return free_disk_space

    def get_check_backup_disk_space(self):
        check_backup_disk_space = False
        try:
            check_backup_disk_space_str = get("CHECK_BACKUP_DISK_SPACE")
            check_backup_disk_space = not check_backup_disk_space_str.strip().lower() in ['false','f','n','no','off','0','']
        except KeyError, e:
            self.__log_info("CHECK_BACKUP_DISK_SPACE",e)
            pass
        return check_backup_disk_space

    # daemon job timeout (in minutes)
    def get_daemon_timeout(self):
        return self.__get_int_value("DAEMON_TIMEOUT", 30)

    # timeout to keep temp dumps after last access (in seconds, 0...86400). used with "--export-dump-as-file -temp" command
    def get_keep_temp_dump(self):
        return self.__get_int_value("KEEP_TEMP_DUMP", 1800)

    # period to keep sessions not older then (in days)
    def get_days_to_keep_sessions(self):
        return self.__get_int_value("DAYS_TO_KEEP_SESSIONS", 30)

    def get_max_number_of_log_dirs(self):
        return self.__get_int_value("MAX_NUMBER_OF_LOG_DIRS", 30)

    def get_archiver_library(self):
        return self.__get_string_value('ARCHIVER_LIBRARY', '', False)

    def get_smtpserver(self):
        return self.__get_string_value("SMTPSERVER", 'localhost')

    def get_smtpuser(self):
        return self.__get_string_value("SMTPUSER", '')

    def get_smtppass(self):
        return self.__get_string_value("SMTPPASS", '')

    def get_authrequired(self):
        authrequired = 0
        try:
            authrequired_str = get("AUTHREQUIRED")
            if authrequired_str != '0':
                authrequired = 1
        except KeyError, e:
            self.__log_info("AUTHREQUIRED",e)
            pass
        return authrequired

    def get_notification_mail_subject_backup(self):
        return self.__get_string_value("NOTIFICATION_MAIL_SUBJECT_BACKUP", u'Backup task notification')

    def get_notification_mail_subject_restore(self):
        return self.__get_string_value("NOTIFICATION_MAIL_SUBJECT_RESTORE", u'Restore task notification')

    def get_notification_mail_from(self):
        return self.__get_string_value("NOTIFICATION_MAIL_FROM", '')

    def get_notification_mail_replyto(self):
        return self.__get_string_value("NOTIFICATION_MAIL_REPLYTO", '')

    def get_notification_mail_body_backup(self):
        mailbody = u'Backup task finished on host %HOST_NAME%.\n\nCreation date is: %CREATION_DATE%\nCreated by: %OWNER_NAME% (type=%OWNER_TYPE%, guid=%OWNER_GUID%)\nCreated for: %TOPOBJECT_NAME% (type=%TOPOBJECT_TYPE%, id=%TOPOBJECT_ID%)\nTask status is: %TASK_STATUS% (see details in the attached file)\n\nDump full name is: %FULL_NAME%\n\n\n\n'
        return self.__get_string_value("NOTIFICATION_MAIL_BODY_BACKUP", mailbody)

    def get_notification_mail_body_restore(self):
        mailbody = u'Restore task finished on host %HOST_NAME%.\n\nCreation date is: %CREATION_DATE%\nCreated by: %OWNER_NAME% (type=%OWNER_TYPE%, guid=%OWNER_GUID%)\nCreated for: %TOPOBJECT_NAME% (type=%TOPOBJECT_TYPE%, id=%TOPOBJECT_ID%)\nTask status is: %TASK_STATUS% (see details in the attached file)\n\nDump full name is: %FULL_NAME%\n\n\n\n'
        return self.__get_string_value("NOTIFICATION_MAIL_BODY_RESTORE", mailbody)

    def get_notification_mail_panel_name(self):
        return self.__get_string_value("NOTIFICATION_MAIL_PANEL_NAME", u'Server')

    def get_notification_mail_panel_administrator_name(self):
        return self.__get_string_value("NOTIFICATION_MAIL_PANEL_ADMINISTRATOR_NAME", u'Plesk administrator')

    def get_max_conflict_resolves(self):
        return self.__get_int_value("MAX_CONFLICT_RESOLVES", 3)

    # maximum number of log files stored
    def get_max_log_files(self):
        return self.__get_int_value("MAX_LOG_FILES", 10)

    # maximum size of log file before rotation
    def get_max_log_size(self):
        return self.__get_int_value("MAX_LOG_SIZE", 1048576)

    def force_debug_log(self):
        return self.__get_int_value("FORCE_DEBUG_LOG", 0)

    def get_keep_local_backup_if_export_to_ftp_failed(self):
        return self.__get_int_value("KEEP_LOCAL_BACKUP_IF_EXPORT_TO_FTP_FAILED", 1)

    def get_used_space_coefficient(self):
        return self.__get_float_value("USED_SPACE_COEFFICIENT", 0.7)

    def get_nice_always(self):
        try:
            nice_always_str = get("NICE_ALWAYS")
            nice_always = not nice_always_str.strip().lower() in ['false','f','n','no','off','0','']
        except KeyError, e:
            nice_always = False
            self.__log_info("NICE_ALWAYS",e)
            pass
        return nice_always

    def get_nice_adjustment(self):
        return self.__get_int_value("NICE_ADJUSTMENT", 10)

    def get_content_transport(self):
        return  self.__get_string_value('MIGRATION_TRANSPORT', 'rsync')

    def get_mssql_native_backup_enabled(self):
        return self.__get_int_value('MSSQL_NATIVE_BACKUP_ENABLED', 1)

    def get_max_transfer_download_time(self):
        return self.__get_int_value('MAX_TRANSFER_DOWNLOAD_TIME', 86400)

    def get_min_transfer_download_speed(self):
        return self.__get_float_value("MIN_TRANSFER_DOWNLOAD_SPEED", 1.25)

    def __get_string_value(self, keyName, defaultValue, logFailure = True):
        value = defaultValue
        try:
            value = get(keyName)
        except (KeyError, ValueError), e:
            if logFailure:
                self.__log_info(keyName, e)
            pass
        return value

    def __get_int_value(self, keyName, defaultValue):
        value = defaultValue
        try:
            valueStr = get(keyName)
            value = int(valueStr);
        except (KeyError, ValueError), e:
            self.__log_info(keyName, e)
            pass
        return value

    def __get_float_value(self, keyName, defaultValue):
        value = defaultValue
        try:
            valueStr = get(keyName)
            value = float(valueStr);
        except (KeyError, ValueError), e:
            self.__log_info(keyName, e)
            pass
        return value

    def __parse(self, configFile):
        value = re.compile('^(\S+)\s+(.*)$')
        for line in fileinput.input(configFile, openhook=fileinput.hook_encoded('utf-8')):
            line = line.rstrip()
            line = line.lstrip(u'\ufeff') # remove Byte Order Mark
            if not line:
                continue
            if line.startswith("#"):
                continue
            data = re.match(value, line)
            if not data:
                raise ExInvalidConfigLine, line
            self.__config[data.group(1)] = string.strip(data.group(2), '\'"')
    
    def writeParameter(self, name, value):
        for line in fileinput.input(self.__configName, inplace=1):
            line = line.rstrip()
            if not line:
                continue
            if line.startswith("#"):
                print line
                continue
            if line.find(name) != -1:
                print name + " " + value
                continue
            print line

# [a sort of] testsuite
if __name__ == '__main__':
    pc = PMMCliConfig()
    for i, v in pc.iteritems():
        print i + "->"+ v
