'''Defines the DoxygenPropertyList class for operating on Info.plist in docsets
'''

# Copyright (c) 2018 Ved Vyas

# This file is part of doxytag2zealdb.

# doxytag2zealdb 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 3 of the License, or
# (at your option) any later version.

# doxytag2zealdb 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 doxytag2zealdb.  If not, see <http://www.gnu.org/licenses/>.

from bs4 import BeautifulSoup


class DoxygenPropertyList(object):
    '''
    A DoxygenPropertyList object can be used to do basic operations on a
    property list file generated by Doxygen when preparing HTML docsets. Such
    property lists contain a top-level dictionary with various key-value
    pairs. DoxygenPropertyList can then add new key-value pairs, modify
    existing ones, and finally save changes to the original file. This class is
    pretty limited at the moment -- it can't handle general property list
    files and only supports string, integer, and Boolean values. Also, it is
    possible that the formatting and ordering of the original property list
    won't be maintained when changes are saved.

    Typical usage:

    plist = DoxygenPropertyList('/path/to/Info.plist')

    # The following is an example of changing an existing key-value
    plist.set_property('CFBundleName', 'MyCoolProject')
    plist.set_property('SomeIntegerProperty', 2)
    plist.set_property('SomeBooleanProperty', False)

    plist.save()
    '''

    def __init__(self, plist_filename):
        '''Initialize a property list representation from an existing file.

        Args:
            plist_filename: A string containing the full path to a
            Doxygen-generated property list file.

        Raises:
            OSError / FileNotFoundError: Input file cannot be read
            RuntimeError: The property list file is not of the expected format
        '''
        self.filename = plist_filename

        with open(self.filename, 'r') as plist_file:
            self.soup = BeautifulSoup(plist_file, 'lxml-xml')
            self.properties = self.soup.findChild(name='dict')

            if self.properties is None:
                raise RuntimeError('Invalid property list file provided')

    def set_property(self, key, value):
        '''Set a new (or updating existing) key value pair.

        Args:
            key: A string containing the key namespace
            value: A str, int, or bool value

        Raises:
            NotImplementedError: an unsupported value-type was provided
        '''
        value_type = type(value)
        if value_type not in [str, int, bool]:
            raise NotImplementedError(
                'Only string, integer, and boolean properties are implemented')

        key_object = self.properties.findChild(name='key', text=key)

        # Key (and value, if it's a valid property list) don't exist
        if key_object is None:
            key_object = self.soup.new_tag('key')
            key_object.string = key

            self.properties.append(key_object)

            value_object = self.soup.new_tag(
                {str: 'string', int: 'integer', bool: str(value).lower()}[
                    value_type])
            if value_type is not bool:
                value_object.string = str(value)

            self.properties.append(value_object)

            return

        # Key (and value, if it's a valid property list) exist
        # Eh, just remove the key+value tags from the tree and re-add them
        # (with the new value)
        value_object = key_object.find_next_sibling()

        key_object.decompose()
        value_object.decompose()

        self.set_property(key, value)

    def save(self):
        '''Save current property list representation to the original file.'''
        with open(self.filename, 'w') as plist_file:
            plist_file.write(str(self.soup))
