#!/usr/bin/env python

# THIS FILE IS PART OF THE CYLC SUITE ENGINE.
# Copyright (C) 2008-2017 NIWA
#
# 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/>.

"""cylc [control] insert [OPTIONS] ARGS

Insert task proxies into a running suite. Uses of insertion include:
 1) insert a task that was excluded by the suite definition at start-up.
 2) reinstate a task that was previously removed from a running suite.
 3) re-run an old task that cannot be retriggered because its task proxy
 is no longer live in the a suite.

Be aware that inserted cycling tasks keep on cycling as normal, even if
another instance of the same task exists at a later cycle (instances of
the same task at different cycles can coexist, but a newly spawned task
will not be added to the pool if it catches up to another task with the
same ID).

See also 'cylc submit', for running tasks without the scheduler.
"""

import sys
if '--use-ssh' in sys.argv[1:]:
    sys.argv.remove('--use-ssh')
    from cylc.remote import remrun
    if remrun().execute(force_required=True):
        sys.exit(0)

import cylc.flags
from cylc.prompt import prompt
from cylc.option_parsers import CylcOptionParser as COP
from cylc.network.suite_command_client import SuiteCommandClient
from cylc.task_id import TaskID


def main():
    parser = COP(
        __doc__, comms=True, multitask=True,
        argdoc=[
            ("REG", "Suite name"),
            ('TASKID [...]', 'Task identifier')])

    parser.add_option(
        "--stop-point", "--remove-point",
        help="Optional hold/stop cycle point for inserted task.",
        metavar="CYCLE_POINT", action="store", dest="stop_point_string")
    parser.add_option(
        "--no-check", help="Add task even if the provided cycle point is not "
        "valid for the given task.", action="store_true", default=False)

    options, args = parser.parse_args()
    suite = args.pop(0)

    # See "cop.parse_multitask_compat" for back compat discussion
    # "cop.parse_multitask_compat" cannot be used here because argument 3
    # (after suite argument) used to be "options.stop_point_string".
    if (options.multitask_compat and len(args) in [2, 3] and
            all(["/" not in arg for arg in args]) and
            all(["." not in arg for arg in args[1:]])):
        items = [(args[0] + "." + args[1])]
        if len(args) == 3:
            options.stop_point_string = args[2]
        prompt(
            'Insert %s in %s' % (items, suite), options.force)
    else:
        items = args
        for i, item in enumerate(items):
            if not TaskID.is_valid_id_2(item):
                sys.exit('ERROR: "%s": invalid task ID (argument %d)' % (
                    item, i + 1))
        prompt('Insert %s in %s' % (items, suite), options.force)

    pclient = SuiteCommandClient(
        suite, options.owner, options.host, options.port,
        options.comms_timeout, my_uuid=options.set_uuid,
        print_uuid=options.print_uuid)

    pclient.put_command(
        'insert_tasks', items=items, no_check=options.no_check,
        stop_point_string=options.stop_point_string
    )


if __name__ == "__main__":
    try:
        main()
    except Exception as exc:
        if cylc.flags.debug:
            raise
        sys.exit(str(exc))
