#!/usr/bin/env python
#
# Copyright 2016-2017 VMware, Inc.  All rights reserved. -- VMware Confidential
#
"""
This module contains functions for reading and manipulating properties files
"""

import logging
import shutil
import re

REGEX_WHITESPACE = "[ \\t]*"
REGEX_KEY_FINDER_TEMPLATE = "^%s" + REGEX_WHITESPACE + "="
REGEX_VALUE_FINDER_GROUP_NAME = "value"
REGEX_VALUE_FINDER_TEMPLATE = REGEX_KEY_FINDER_TEMPLATE +\
                              REGEX_WHITESPACE +\
                              "(?P<" + REGEX_VALUE_FINDER_GROUP_NAME + ">.*)"
PROPERTY_TEMPLATE = "%s = %s\n"
BACKUP_HEADER_TEMPLATE = "# Automatic backup of %s\n\n"
logger = logging.getLogger(__name__)

def backup(file):
   """
   Copies the supplied file with a bck extension added.
   Overrides previous backup file.

   :param file: Path to the file
   :type file: String

   :return: The path to the resulting file
   :rtype: String
   """

   backup_file_path = file + ".bck"

   with open(file) as fp_original:
      with open(backup_file_path, "w") as fp_backup:
         fp_backup.write(BACKUP_HEADER_TEMPLATE % file)
         fp_backup.write(fp_original.read())

   logger.info("%s backed up to %s", (file, backup_file_path))
   return backup_file_path

def add(file, key, value):
   """
   Adds a property to the specified file if the property does not exist;
   Otherwise, does nothing.

   :param file: Path to the file
   :type file: String
   :param key: Key of the property to add
   :type key: String
   :param value: Value of the property to add
   :type value: String

   :return: True if the property was added; otherwise, False
   :rtype: Boolean
   """

   with open(file, "r+") as fp:
      content = fp.read()
      if not re.search(_build_regex(REGEX_KEY_FINDER_TEMPLATE, key), content, re.MULTILINE):
         # Go to end of file first; otherwise, write may fail on some systems
         fp.seek(0, 2)

         if not content.endswith("\n"):
            fp.write("\n")
         fp.write(PROPERTY_TEMPLATE % (key, value))

         logger.info("Property %s has been added to %s", (key, file))
         return True

   return False

def delete(file, key):
   """
   Deletes a property from the specified file if the property exists;
   otherwise, does nothing.

   :param file: Path to the file
   :type file: String
   :param key: Key of the property to delete
   :type key: String

   :return: True if the property was deleted; otherwise False
   :rtype: Boolean
   """

   return replace(file, key, None)

def replace(file, key, value):
   """
   Replaces a property in the specified file if the property exists;
   otherwise, does nothing.

   :param file: Path to the file
   :type file: String
   :param key: Key of the property to add
   :type key: String
   :param value: Value of the property to add
   :type key: String

   :return: True if the property was replaced; otherwise False
   :rtype: Boolean
   """

   regex = re.compile(_build_regex(REGEX_KEY_FINDER_TEMPLATE, key))
   result = False

   with open(file, "r+") as fp:
      lines = fp.readlines()

      # Goes to the beginning of the file
      fp.seek(0)

      # Writes all the valid lines
      for line in lines:
         if regex.match(line):
            result = True
            if value is not None:
               fp.write(PROPERTY_TEMPLATE % (key, value))
               logger.info("Property %s has been replaced in %s", (key, file))
            else:
               logger.info("Property %s has been deleted from %s", (key, file))
         else:
            fp.write(line)

      # Scrubs the rest
      fp.truncate()

   return result

def value(file, key):
   """
   Gets the value of a property from the specified file.

   :param file: Path to the file
   :type file: String
   :param key: Key of the property
   :type key: String

   :return: Value of the property if found; otherwise, None
   :rtype: String
   """

   with open(file) as fp:
      content = fp.read()
      match = re.search(_build_regex(REGEX_VALUE_FINDER_TEMPLATE, key),
                        content,
                        re.MULTILINE)
      if match:
         return match.group(REGEX_VALUE_FINDER_GROUP_NAME)

   return None

def _build_regex(template, key):
    return template % re.escape(key)
