#!/usr/bin/env python

# Copyright (C) 2012-2013 Science and Technology Facilities Council.
#
# 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/>.

from codecs import latin_1_decode
import sys
import subprocess
from optparse import OptionParser

from crab import CrabError, CrabStatus
from crab.client import CrabClient
from crab.util.guesstimezone import guess_timezone

def send_crontab(crontab):
    """Sends the given crontab to the server.

    The crontab should be given as a unicode string.
    This function also attempts to determine the system timezone
    to send along with the crontab.  This might not always be the
    timezone cron uses, for example if configured in /etc/default/cron
    so if in doubt, the environment variable TZ should be set, or
    the crontab should include a CRON_TZ variable."""

    tz = guess_timezone()

    client = CrabClient()
    return client.send_crontab(crontab, timezone=tz)

def read_crontab():
    """Fetches the current user's crontab file.

    The output from ``crontab -l`` is decoded to give a unicode
    string.  Currently ISO-8859-1 encoding is assumed."""

    try:
        p = subprocess.Popen('crontab -l',
                             shell=True, stdout=subprocess.PIPE)
        (stdoutdata, stderrdata) = p.communicate()

        if p.returncode == 0:
            return latin_1_decode(stdoutdata, 'replace')[0]

        else:
            print(sys.argv[0] + ': failed to read crontab')

    #except OSError as err:
    except OSError:
        print(sys.argv[0] + ': could not execute crontab command')

def read_file(path):
    """Returns the contents of the given file.

    The file is opened in text-mode read, so it should return
    a unicode string."""

    if path == '-':
        return sys.stdin.read()

    try:
        fh = None

        try:
            fh = open(path, 'r')
            return fh.read()

        except IOError:
            err = sys.exc_info()[1]
            print(sys.argv[0] + ': cannot read file: ' + str(err))

    finally:
        if fh is not None:
            fh.close()

def main():
    parser = OptionParser(usage = """Usage: %prog [options] [command]

Commands:
  start, finish, unknown - report cron job progress
  fail, couldnotstart    - report problems with cron job
  alreadyrunning         - long running job need not start
  import                 - send crontab to server
  export                 - display cron jobs from server
  info                   - print current configuration""")

    parser.add_option('-c',
        type='string', dest='command',
        help='specify the cron COMMAND being reported', metavar='COMMAND')
    parser.add_option('--id',
        type='string', dest='crabid',
        help='specify the Crab job ID to report', metavar='ID')
    parser.add_option('--file',
        type='string', dest='crontabfile',
        help='specify file name containing crontab to import')
    parser.add_option('--raw',
        action="store_true", dest="raw", default=False,
        help="fetch crontab from server exactly as last imported")

    (options, args) = parser.parse_args()

    status_commands = ['finish', 'fail', 'unknown', 'couldnotstart',
                       'alreadyrunning']

    if len(args) == 0:
        parser.error('no command specified')

    elif args[0] == 'start' or args[0] in status_commands:
        if options.command is None:
            parser.error('no command specified')
        else:
            try:
                client = CrabClient(options.command, crabid=options.crabid)

                if args[0] == 'start':
                    client.start()
                else:
                    client.finish(status=status_commands.index(args[0]))

            #except CrabError as err:
            except CrabError:
                err = sys.exc_info()[1]
                print(sys.argv[0] + ': ' + str(err))
                return 1

    elif args[0] == 'import':
        if options.crontabfile is None:
            crontab = read_crontab()
        else:
            crontab = read_file(options.crontabfile)

        if crontab is None:
            return 1

        try:
            warning = send_crontab(crontab)

            if warning:
                print(sys.argv[0] + ': crontab import warnings:')
                for message in warning:
                    print(message)

                return 1

        #except CrabError as err:
        except CrabError:
            err = sys.exc_info()[1]
            print(sys.argv[0] + ': failed to send crontab: ' + str(err))
            return 1

    elif args[0] == 'export':
        try:
            client = CrabClient()
            crontab = client.fetch_crontab(raw=options.raw)
            print(crontab)

        #except CrabError as err:
        except CrabError:
            err = sys.exc_info()[1]
            print(sys.argv[0] + ': failed to fetch crontab: ' + str(err))
            return 1

    elif args[0] == 'help':
        parser.print_help()

    elif args[0] == 'info':
        client = CrabClient()
        print(client.get_info())

    else:
        parser.error('command not recognised')

    return 0

if __name__ == '__main__':
    sys.exit(main())
