# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License.  You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied.  See the License for the
# specific language governing permissions and limitations
# under the License.
"""SliceScatter operator"""

from tvm import topi

from . import utils


def slice_scatter(input_tensor, src, start, end, step, axis):
    """
    Scatters a slice of src into input along the given axis (SSA form).

    Args:
        input_tensor (te.Tensor): The input tensor to scatter into.
        src (te.Tensor): The source tensor to scatter from.
        start (int): The starting index of the slice.
        end (int): The ending index of the slice.
        step (int): The step size of the slice.
        axis (int): The axis to scatter along.

    Returns:
        list[te.Tensor]: A list containing the output tensor with the slice scattered.
    """

    dim_size_expr = input_tensor.shape[axis]  # Expression for dimension size
    dim_size = utils.get_const_int(dim_size_expr)  # Dimension size (as constant int)

    if start == 0 and end == dim_size and step == 1:
        return topi.identity(src)

    mask = topi.full((dim_size,), "bool", True)
    idx = topi.arange(start=0, stop=dim_size, step=1, dtype="int64")

    if start != 0:
        mask = topi.logical_and(mask, topi.greater_equal(idx, start))

    if end != dim_size:
        mask = topi.logical_and(mask, topi.less(idx, end))

    if step != 1:
        step_mask = topi.equal(topi.floor_mod(idx - start, step), 0)
        mask = topi.logical_and(mask, step_mask)

    mask_shape_base = [1] * len(input_tensor.shape)
    mask_shape_base[axis] = dim_size
    mask_shape = tuple(mask_shape_base)

    mask_reshaped = topi.reshape(mask, mask_shape)

    idx_new_pre = idx - start + (step - 1)
    idx_new_div = topi.floor_divide(idx_new_pre, step)
    idx_new = topi.clip(idx_new_div, 0, dim_size - 1)

    temp = topi.take(src, idx_new, axis=axis)

    mask_shape_expanded_base = list(input_tensor.shape)
    mask_shape_expanded = tuple(mask_shape_expanded_base)

    mask_expanded = topi.broadcast_to(mask_reshaped, mask_shape_expanded)

    output = topi.where(mask_expanded, temp, input_tensor)

    return [output]
