#!/usr/bin/python
# -*- coding: utf-8 -*-

# (c) 2012, Jan-Piet Mens <jpmens () gmail.com>
#
# This file is part of Ansible
#
# Ansible 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.
#
# Ansible 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 Ansible.  If not, see <http://www.gnu.org/licenses/>.
#

DOCUMENTATION = '''
---
module: ini_file
short_description: Tweak settings in INI files
description:
     - Manage (add, remove, change) individual settings in an INI-style file without having
       to manage the file as a whole with, say, M(template) or M(assemble). Adds missing
       sections if they don't exist.
version_added: "0.9"
options:
  dest:
    description:
      - Path to the INI-style file; this file is created if required
    required: true
    default: null
  section:
    description:
      - Section name in INI file. This is added if C(state=present) automatically when
        a single value is being set.
    required: true
    default: null
  option:
    description:
      - if set (required for changing a I(value)), this is the name of the option.
      - May be omitted if adding/removing a whole I(section).
    required: false
    default: null
  value:
    description:
     - the string value to be associated with an I(option). May be omitted when removing an I(option).
    required: false
    default: null
  backup:
    description:
      - Create a backup file including the timestamp information so you can get
        the original file back if you somehow clobbered it incorrectly.
    required: false
    default: no
    choices: [ "yes", "no" ]
  others:
     description:
       - all arguments accepted by the M(file) module also work here
     required: false
examples:
   - code: "ini_file: dest=/etc/conf section=drinks option=fav value=lemonade mode=0600 backup=yes"
     description: Ensure C(fav=lemonade) is in section C([drinks]) in said file
   - code: |
           ini_file: dest=/etc/anotherconf
                     section=drinks
                     option=temperature
                     value=cold
                     backup=yes
notes:
   - While it is possible to add an I(option) without specifying a I(value), this makes
     no sense.
   - A section named C(default) cannot be added by the module, but if it exists, individual
     options within the section can be updated. (This is a limitation of Python's I(ConfigParser).)
     Either use M(template) to create a base INI file with a C([default]) section, or use
     M(lineinfile) to add the missing line.
requirements: [ ConfigParser ]
author: Jan-Piet Mens
'''


import ConfigParser

# ==============================================================
# do_ini

def do_ini(module, filename, section=None, option=None, value=None, state='present', backup=False):

    changed = False
    cp = ConfigParser.ConfigParser()

    try:
        f = open(filename)
        cp.readfp(f)
    except IOError:
        pass


    if state == 'absent':
        if option is None and value is None:
            if cp.has_section(section):
                cp.remove_section(section)
                changed = True
        else:
            if option is not None:
                try:
                    if cp.get(section, option):
                        cp.remove_option(section, option)
                        changed = True
                except:
                    pass

    if state == 'present':
        if cp.has_section(section) == False:
            if section.upper() == 'DEFAULT':
                module.fail_json(msg="[DEFAULT] is an illegal section name")

            cp.add_section(section)
            changed = True

        if option is not None and value is not None:
            try:
                oldvalue = cp.get(section, option)
                if str(value) != str(oldvalue):
                    cp.set(section, option, value)
                    changed = True
            except ConfigParser.NoSectionError:
                cp.set(section, option, value)
                changed = True
            except ConfigParser.NoOptionError:
                cp.set(section, option, value)
                changed = True

    if changed:
        if backup:
            module.backup_local(filename)

        try:
            f = open(filename, 'w')
            cp.write(f)
        except:
            module.fail_json(msg="Can't creat %s" % filename)

    return changed

# ==============================================================
# main

def main():

    module = AnsibleModule(
        # not checking because of daisy chain to file module
        check_invalid_arguments = False,
        argument_spec = dict(
            dest = dict(required=True),
            section = dict(required=True),
            option = dict(required=False),
            value = dict(required=False),
            backup = dict(default='no', choices=BOOLEANS),
            state = dict(default='present', choices=['present', 'absent'])
        )
    )

    info = dict()

    dest = os.path.expanduser(module.params['dest'])
    section = module.params['section']
    option = module.params['option']
    value = module.params['value']
    state = module.params['state']
    backup = module.boolean(module.params['backup'])

    changed = do_ini(module, dest, section, option, value, state, backup)

    info['daisychain_args'] = module.params
    info['daisychain_args']['state'] = 'file'
    info['daisychain_args']['dest'] = dest

    # Mission complete
    module.exit_json(dest=dest, 
        changed=changed, msg="OK",
        daisychain="file", daisychain_args=info.get('daisychain_args',''))

# this is magic, see lib/ansible/module_common.py
#<<INCLUDE_ANSIBLE_MODULE_COMMON>>
main()