#!python
"""jss_helper

Command line utility using jss.py

Copyright (C) 2014 Shea G Craig <shea.craig@da.org>

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 3 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, see <http://www.gnu.org/licenses/>.

"""

import argparse
import subprocess
import sys

from jss import *


def print_object(objs):
    if isinstance(objs, list):
        for obj in objs:
            print("ID: %s\t\tNAME: %s" % (obj.id, obj.name))
    else:
        print(objs)


def create_func(obj_method):
    """Generates a function to perform basic list and xml queries."""
    def f(j, args):
        if args.id:
            id_ = int(args.id)
            try:
                results = obj_method(id_)
            except JSSGetError:
                print("Object ID: %s does not exist!" % id_)
                sys.exit(1)
        else:
            results = obj_method()
        print_object(results)

    return f


def get_group_policies(j, args):
    """Find all policies scoped to a group."""
    search = 'scope/computer_groups/computer_group'
    policies = j.Policy()
    group = j.ComputerGroup(args.group)
    results = get_objects_by_scoped_group(policies, search, group)

    output = build_scope_results('Policies', args.group, results)
    print(output)


def get_objects_by_scoped_group(objs, search, group):
    """Get all objects scoped to Group object group.

    objs:   A list of objects to check.
    search: an xpath string to the list container for that scoping type.
    group: A Group object to search by.

    Returns a list of results.

    """
    results = []
    full_objects = objs.retrieve_all()
    for obj in full_objects:
        for scope_group in obj.findall(search):
            if scope_group.findtext('id') == group.id or \
                    scope_group.findtext('name') == group.name:
                results.append((obj.id, obj.name))
    return results


def build_scope_results(type, group, results):
    """Print a list of results for type."""
    s = "%s in scope for %s:\n" % (type, group)
    for i in results:
        s += "ID: %s\t\tNAME: %s\n" % (i[0], i[1])
    return s


def batch_scope(j, args):
    group = j.ComputerGroup(args.group)
    print("Scoping to group: %s" % group.name)
    print(79 * '-')
    for policy_query in args.policies:
        policy = j.Policy(int(policy_query))
        policy.add_object_to_scope(group)
        policy.update()
        print("%s: Success." % policy.name)


def get_md_configp_group(j, args):
    """Find all mobile device configuration profiles scoped to a group."""
    search = 'scope/mobile_device_groups/mobile_device_group'
    configps = j.MobileDeviceConfigurationProfile()
    group = j.MobileDeviceGroup(args.group)
    results = get_objects_by_scoped_group(configps, search, group)

    output = build_scope_results('Profiles', args.group, results)
    print(output)


def get_group_policy_diff(j, args):
    search = 'scope/computer_groups/computer_group'
    policies = j.Policy()
    policies.sort()
    group1 = j.ComputerGroup(args.group1)
    group2 = j.ComputerGroup(args.group2)
    results1 = get_objects_by_scoped_group(policies, search, group1)
    results2 = get_objects_by_scoped_group(policies, search, group2)

    # I tried to do this with the tempfile module, but the files always ended
    # up being size 0 and dissappearing, despite delete=False.
    with open('/tmp/file1', mode='w') as f1:
        output = build_scope_results("Policies", args.group1, results1)
        f1.write(output)
        file1 = f1.name
    with open('/tmp/file2', mode='w') as f2:
        output = build_scope_results("Policies", args.group2, results2)
        f2.write(output)
        file2 = f2.name

    subprocess.call(['diff', '-y', file1, file2])


def get_md_configp_diff(j, args):
    search = 'scope/mobile_device_groups/mobile_device_group'
    profiles = j.MobileDeviceConfigurationProfile()
    group1 = j.MobileDeviceGroup(args.group1)
    group2 = j.MobileDeviceGroup(args.group2)
    results1 = get_objects_by_scoped_group(profiles, search, group1)
    results2 = get_objects_by_scoped_group(profiles, search, group2)

    # I tried to do this with the tempfile module, but the files always ended
    # up being size 0 and dissappearing, despite delete=False.
    with open('/tmp/file1', mode='w') as f1:
        output = build_scope_results("Profiles", args.group1, results1)
        f1.write(output)
        file1 = f1.name
    with open('/tmp/file2', mode='w') as f2:
        output = build_scope_results("Profiles", args.group2, results2)
        f2.write(output)
        file2 = f2.name

    subprocess.call(['diff', '-y', file1, file2])


def create_subparser(parser, name, help, func, **kwargs):
    """Create subparsers for our interface."""
    subparser = parser.add_parser(name, help=help)
    if kwargs:
        for arg_name, arg_help in kwargs.items():
            subparser.add_argument(arg_name, help=arg_help)
    subparser.set_defaults(func=func)


def main():
    """Run as a cli command."""
    jss_prefs = JSSPrefs()
    j = JSS(jss_prefs)

    # Create our argument parser
    parser = argparse.ArgumentParser(description="Query the JSS.")
    parser.add_argument('-v', action='store_true', help="Verbose output.")
    subparser = parser.add_subparsers(dest='subparser_name')

    subparsers = {}

    # computer
    subparsers['computer'] = {'help': "Get a list of all computers, or an "
                              "individual computer.",
                              'func': create_func(j.Computer),
                              'kwargs': {'--id': 'ID of computer to retrieve.'}
                             }
    subparsers['group'] = {'help': "Get a list of all computer groups, or an "
                              "individual group.",
                              'func': create_func(j.ComputerGroup),
                              'kwargs': {'--id': 'ID of computer group to '
                                         'retrieve.'}}
    subparsers['policy'] = {'help': "Get a list of all policies' names and "
                            "IDs, or the policy XML.",
                              'func': create_func(j.Policy),
                              'kwargs': {'--id': 'ID of policy to retrieve.',}}
    subparsers['policy_by_group'] = {'help': "Lists all policies scoped to "
                            "provided group.",
                              'func': get_group_policies,
                              'kwargs': {'group': 'Group name to query.'}}
    subparsers['group_policy_diff'] = {'help': "Lists all policies scoped to "
                            "two provided groups, highlighting the "
                            "differences.", 'func': get_group_policy_diff,
                              'kwargs': {'group1': 'Group name to query.',
                                         'group2': 'Group name to query.'}}
    subparsers['category'] = {'help': "Get a list of all categories' names and "
                            "IDs.",
                              'func': create_func(j.Category),
                              'kwargs': {'--id': 'ID of category to retrieve.'}
                             }
    subparsers['md'] = {'help': "Get a list of mobile devices, or find one "
                            "by ID.",
                              'func': create_func(j.MobileDevice),
                              'kwargs': {'--id': 'ID of mobile device to '
                                         'retrieve.'}}
    subparsers['md_group'] = {'help': "Get a list of mobile device groups, "
                              "or find one by ID.",
                              'func': create_func(j.MobileDeviceGroup),
                              'kwargs': {'--id': 'ID of mobile device group '
                                         'to retrieve.'}}
    subparsers['md_configp'] = {'help': "Get a list of mobile device "
                                "configuration profiles, or find one by ID, ",
                               'func':
                               create_func(j.MobileDeviceConfigurationProfile),
                               'kwargs': {'--id': 'ID of mobile device '
                                          'configuration profile to retrieve.'}
                               }
    subparsers['md_configp_by_group'] = {'help': "Lists all mobile "
                                         "configuration profiles scoped to "
                                         "provided group.",
                                        'func': get_md_configp_group,
                                        'kwargs': {'group': 'Group name to '
                                                   'query.'}}
    subparsers['md_configp_diff'] = {'help': "Lists the differnces between"
                                         "all mobile configuration profiles"
                                         "scoped to the provided groups.",
                                        'func': get_md_configp_diff,
                                        'kwargs': {'group1': 'Group name to '\
                                                   'query.', 'group2':
                                                    'Group name to query.'}
                                        }

    for name, d in subparsers.items():
        create_subparser(subparser, name, d['help'], d['func'], **d['kwargs'])

    # More complicated parsers.
    help = "Scope a list of policies to a group."
    batch_scope_subparser = subparser.add_parser('batch_scope', help=help)
    batch_scope_subparser.add_argument('group', help="Name of group to scope "
                                       "policies.")
    help = "A space delimited list of policy IDs."
    batch_scope_subparser.add_argument('policies', help=help, nargs='*')
    batch_scope_subparser.set_defaults(func=batch_scope)

    args = parser.parse_args()
    if args.v:
        j.verbose = True

    args.func(j, args)


if __name__ == '__main__':
    main()
