#!/usr/bin/env python2

# THIS FILE IS PART OF THE CYLC SUITE ENGINE.
# Copyright (C) 2008-2019 NIWA & British Crown (Met Office) & Contributors.
#
# 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] TASK_GLOB [...]

Insert new task proxies into the task pool of a running suite, e.g. to allow
re-triggering of an earlier task that has already been removed from the pool.

NOTE: inserted cycling tasks cycle on 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 independently of the scheduler.

TASK_GLOB matches task or family names, to insert task instances into the pool
at a specific given cycle point. (NOTE this differs from other commands which
match name and cycle point patterns against instances already in the pool).
* CYCLE-POINT/TASK-NAME-GLOB
* CYCLE-POINT/FAMILY-NAME-GLOB
* TASK-NAME-GLOB.CYCLE-POINT
* FAMILY-NAME-GLOB.CYCLE-POINT

For example, to match, within the given cycle point (e.g. '20200202T0000Z'):
* all tasks: '20200202T0000Z/*' or '*.20200202T0000Z'
* all tasks named model_N for some character N: '20200202T0000Z/model_?' or
  'model_?.20200202T0000Z'
* all tasks in 'BAR' family: '20200202T0000Z/BAR' or 'BAR.20200202T0000Z'
* all tasks in 'BAR' or 'BAZ' families: '20200202T0000Z/BA[RZ]' or
  'BA[RZ].20200202T0000Z'

The old 'MATCH POINT' syntax will be automatically detected and supported. To
avoid this, use the '--no-multitask-compat' option, or use the new syntax
(with a '/' or a '.') when specifying 2 TASK_GLOB arguments."""

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

import cylc.flags
from cylc.prompt import prompt
from cylc.option_parsers import CylcOptionParser as COP
from cylc.network.httpclient import SuiteRuntimeServiceClient
from cylc.task_id import TaskID


def main():
    parser = COP(
        __doc__, comms=True,
        argdoc=[
            ("REG", "Suite name"),
            ('TASK_GLOB [...]', 'Task match pattern(s)')])

    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)

    parser.add_std_option(
        "--no-multitask-compat",
        help="Disallow backward compatible multitask interface.",
        action="store_false", default=True,
        dest="multitask_compat")

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

    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 = SuiteRuntimeServiceClient(
        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))
