from typing import Any, List, Optional, cast
import sys
import random
random.seed(0)
import numpy as np
np.random.seed(0)
import tensorflow as tf
import tf_keras
import onnx2tf.gs as gs
from onnx2tf.utils.common_functions import (
    get_constant_or_variable,
    print_node_info,
    inverted_operation_enable_disable,
    make_tf_node_info,
    convert_axis,
    replace_max_values_negative_values,
    get_replacement_parameter,
    replace_parameter,
    pre_process_transpose,
    post_process_transpose,
    stridedslice_with_flexing_deterrence,
    convert_reverse_axis,
)
from onnx2tf.utils.enums import NUMPY_DTYPES_TO_TF_DTYPES
from onnx2tf.utils.logging import *


@print_node_info
@inverted_operation_enable_disable
@get_replacement_parameter
def make_node(
    *,
    graph_node: gs.Node,
    tf_layers_dict: dict,
    **kwargs: Any,
):
    """Slice

    Parameters
    ----------
    graph_node: gs.Node
        graph_surgeon Node

    tf_layers_dict: dict
        optype, shape, dtype, tensorflow graph
    """
    before_op_output_shape_trans_1 = \
        tf_layers_dict.get(graph_node.inputs[0].name, {}).get('before_op_output_shape_trans', True)
    before_op_output_shape_trans_2 = True
    if len(graph_node.inputs) >= 2:
        before_op_output_shape_trans_2 = \
            tf_layers_dict.get(graph_node.inputs[1].name, {}).get('before_op_output_shape_trans', True)
    before_op_output_shape_trans_3 = True
    if len(graph_node.inputs) >= 3:
        before_op_output_shape_trans_3 = \
            tf_layers_dict.get(graph_node.inputs[2].name, {}).get('before_op_output_shape_trans', True)
    before_op_output_shape_trans_4 = True
    if len(graph_node.inputs) >= 4:
        before_op_output_shape_trans_4 = \
            tf_layers_dict.get(graph_node.inputs[3].name, {}).get('before_op_output_shape_trans', True)
    before_op_output_shape_trans_5 = True
    if len(graph_node.inputs) >= 5:
        before_op_output_shape_trans_5 = \
            tf_layers_dict.get(graph_node.inputs[4].name, {}).get('before_op_output_shape_trans', True)
    before_op_output_shape_trans = \
        before_op_output_shape_trans_1 \
        and before_op_output_shape_trans_2 \
        and before_op_output_shape_trans_3 \
        and before_op_output_shape_trans_4 \
        and before_op_output_shape_trans_5

    graph_node_input = get_constant_or_variable(
        graph_node.inputs[0],
        before_op_output_shape_trans,
    )
    input_tensor = tf_layers_dict[graph_node_input.name]['tf_node'] \
        if isinstance(graph_node_input, gs.Variable) else graph_node_input

    # Pre-process transpose
    input_tensor = pre_process_transpose(
        value_before_transpose=input_tensor,
        param_target='inputs',
        param_name=graph_node.inputs[0].name,
        **kwargs,
    )

    input_tensor_shape = input_tensor.shape
    input_tensor_shape_list = input_tensor_shape.as_list() \
        if input_tensor_shape.rank is not None else None
    input_tensor_rank = int(input_tensor_shape.rank) if input_tensor_shape.rank is not None else 1

    starts = None
    if len(graph_node.inputs) >= 2:
        starts = get_constant_or_variable(
            graph_node.inputs[1],
            before_op_output_shape_trans \
                if graph_node.inputs[1].shape is not None and len(graph_node.inputs[1].shape) != 1 else False,
        )
        starts = tf_layers_dict[starts.name]['tf_node'] \
            if isinstance(starts, gs.Variable) else starts

    ends = None
    if len(graph_node.inputs) >= 3:
        ends = get_constant_or_variable(
            graph_node.inputs[2],
            before_op_output_shape_trans \
                if graph_node.inputs[2].shape is not None and len(graph_node.inputs[2].shape) != 1 else False,
        )
        ends = tf_layers_dict[ends.name]['tf_node'] \
            if isinstance(ends, gs.Variable) else ends

    starts = graph_node.attrs.get('starts', starts)
    if isinstance(starts, list):
        starts = tf.convert_to_tensor(np.asarray(starts))
    ends = graph_node.attrs.get('ends', ends)
    if isinstance(ends, list):
        ends = tf.convert_to_tensor(np.asarray(ends))
    if starts is None or ends is None:
        error(f'Slice requires starts/ends. node.name: {graph_node.name}')
        sys.exit(1)
    ends_dtype = tf.int64
    if isinstance(ends, np.ndarray):
        ends_dtype = NUMPY_DTYPES_TO_TF_DTYPES[ends.dtype] \
            if isinstance(ends.dtype, np.dtype) else ends.dtype
    elif tf.is_tensor(ends):
        ends_dtype = cast(Any, ends).dtype

    axes = None
    if len(graph_node.inputs) >= 4:
        axes = get_constant_or_variable(
            graph_node.inputs[3],
            before_op_output_shape_trans \
                if graph_node.inputs[3].shape is not None and len(graph_node.inputs[3].shape) != 1 else False,
        )
    axes = tf_layers_dict[axes.name]['tf_node'] \
        if isinstance(axes, gs.Variable) else axes

    if len(graph_node.inputs) < 4:
        starts_tensor_for_axes = tf.convert_to_tensor(cast(Any, starts))
        axes = tf.range(
            tf.gather(tf.shape(starts_tensor_for_axes), indices=0),
            dtype=cast(Any, ends_dtype),
        )
    elif isinstance(axes, np.ndarray):
        axes = axes
    elif isinstance(axes, list):
        axes = np.asarray(axes, dtype=cast(Any, ends_dtype))

    steps = None
    if len(graph_node.inputs) >= 5:
        steps = get_constant_or_variable(
            graph_node.inputs[4],
            before_op_output_shape_trans \
                if graph_node.inputs[4].shape is not None and len(graph_node.inputs[4].shape) != 1 else False,
        )
    steps = tf_layers_dict[steps.name]['tf_node'] \
        if isinstance(steps, gs.Variable) else steps
    if isinstance(steps, np.ndarray):
        steps_dtype = NUMPY_DTYPES_TO_TF_DTYPES[steps.dtype] \
            if isinstance(steps.dtype, np.dtype) else steps.dtype
        steps = tf.constant(steps, dtype=steps_dtype)

    axes = graph_node.attrs.get('axes', axes)

    if isinstance(axes, list) or (isinstance(axes, np.ndarray) and len(axes.shape) > 0):
        axes = [
            convert_axis(
                axis=idx,
                tensor_rank=input_tensor_rank,
                before_op_output_shape_trans=before_op_output_shape_trans,
            ) for idx in axes
        ]
    elif axes is not None and isinstance(axes, np.ndarray) and len(axes.shape) == 0:
        axes = convert_axis(
            axis=axes,
            tensor_rank=input_tensor_rank,
            before_op_output_shape_trans=before_op_output_shape_trans,
        )
    if isinstance(axes, list):
        axes = tf.convert_to_tensor(np.asarray(axes))

    graph_node_output: gs.Variable = graph_node.outputs[0]
    shape = graph_node_output.shape
    dtype = graph_node_output.dtype

    # Preserving Graph Structure (Dict)
    tf_layers_dict[graph_node_output.name] = {
        'optype': graph_node.op,
        'shape': shape,
        'dtype': dtype,
        'nhwc': tf_layers_dict[graph_node_input.name]['nhwc'] \
            if isinstance(graph_node_input, gs.Variable) \
                and 'nhwc' in tf_layers_dict[graph_node_input.name].keys() else False
    }

    # Determine if the axes are randomly placed on the axes.
    axes_np_for_check: Optional[np.ndarray] = None
    if isinstance(axes, np.ndarray):
        axes_np_for_check = axes
    elif tf.is_tensor(axes) and hasattr(axes, 'numpy'):
        axes_np_for_check = np.asarray(cast(Any, axes).numpy())
    if axes_np_for_check is not None and axes_np_for_check.ndim > 1:
        base_axes = list(np.asarray(axes_np_for_check).reshape(-1))
        sorted_axes = list(sorted(base_axes))
        if base_axes != sorted_axes:
            if tf_layers_dict[graph_node_output.name]['nhwc'] == True:
                tmp_axes = [
                    convert_reverse_axis(
                        axis=idx,
                        tensor_rank=input_tensor_rank,
                        before_op_output_shape_trans=True,
                    ) for idx in base_axes
                ]
                if isinstance(starts, np.ndarray):
                    starts = starts[tmp_axes]
                elif tf.is_tensor(starts):
                    starts = tf.gather(cast(Any, starts), indices=tmp_axes)
                else:
                    starts = np.asarray(cast(Any, starts))[tmp_axes]
                if isinstance(ends, np.ndarray):
                    ends = ends[tmp_axes]
                elif tf.is_tensor(ends):
                    ends = tf.gather(cast(Any, ends), indices=tmp_axes)
                else:
                    ends = np.asarray(cast(Any, ends))[tmp_axes]
                if steps is not None:
                    if isinstance(steps, np.ndarray):
                        steps = steps[tmp_axes]
                    elif tf.is_tensor(steps):
                        steps = tf.gather(cast(Any, steps), indices=tmp_axes)
                    else:
                        steps = np.asarray(cast(Any, steps))[tmp_axes]
                axes = tf.convert_to_tensor(sorted_axes)

    # Param replacement - OP replacement
    """
    Slice implements special replacements separately at this time
    Ignore all automatic conversions and generate tf.strided_slice directly
    by specifying all parameters of tf.strided_slice directly
    https://www.tensorflow.org/api_docs/python/tf/strided_slice

    import numpy as np
    n = np.asarray(
        [
            [
                [1, 1, 1],
                [2, 2, 2],
            ],
            [
                [3, 3, 3],
                [4, 4, 4],
            ],
            [
                [5, 5, 5],
                [6, 6, 6],
            ],
        ]
    )
    n.shape: (3, 2, 3)

    import tensorflow as tf
    t = tf.constant(
        [
            [
                [1, 1, 1],
                [2, 2, 2],
            ],
            [
                [3, 3, 3],
                [4, 4, 4],
            ],
            [
                [5, 5, 5],
                [6, 6, 6],
            ],
        ]
    )
    t.shape: TensorShape([3, 2, 3])

    # Numpy [begin0:end0:step0, begin1:end1:step1, begin2:end2:step2, ...]
        n[1:2, 0:1, 0:3] -> [[[3, 3, 3]]]
        n[1:2, 0:2, 0:3] -> [[[3, 3, 3], [4, 4, 4]]]
        n[1:2:1, 0:1:1, 0:3:1] -> [[[3, 3, 3]]]

    # TensorFlow [begin0,begin1,begin2, ...], [end0,end1,end2, ...], [strides0,strides1,strides2, ...]
        tf.strided_slice(t, [1, 0, 0], [2, 1, 3], [1, 1, 1]) -> [[[3, 3, 3]]]
        tf.strided_slice(t, [1, 0, 0], [2, 2, 3], [1, 1, 1]) -> [[[3, 3, 3], [4, 4, 4]]]
    """
    COMPRESSION_DEFAULT_VALUE = 5
    op_rep_params = kwargs.get('op_rep_params', [])
    begin_ = None
    end_ = None
    strides_ = None
    begin_mask_ = 0
    end_mask_ = 0
    ellipsis_mask_ = 0
    new_axis_mask_ = 0
    shrink_axis_mask_ = 0
    for op_rep_param in op_rep_params:
        if op_rep_param['param_target'] == 'op':
            begin_ = op_rep_param.get('begin', None)
            end_ = op_rep_param.get('end', None)
            strides_ = op_rep_param.get('strides', None)
            begin_mask_ = op_rep_param.get('begin_mask', 0)
            end_mask_ = op_rep_param.get('end_mask', 0)
            ellipsis_mask_ = op_rep_param.get('ellipsis_mask', 0)
            new_axis_mask_ = op_rep_param.get('new_axis_mask', 0)
            shrink_axis_mask_ = op_rep_param.get('shrink_axis_mask', 0)

            if begin_ is None or end_ is None:
                error(
                    f'When replacing Slice OP, "begin" and "end" must be specified in replace.json. ' +
                    f'Check the specification of tf.strided_slice in TensorFlow and specify the appropriate parameters. ' +
                    f'https://www.tensorflow.org/api_docs/python/tf/strided_slice'
                )
                sys.exit(1)

    # Param replacement - starts
    if len(graph_node.inputs) >= 2:
        starts = replace_parameter(
            value_before_replacement=starts,
            param_target='inputs',
            param_name=graph_node.inputs[1].name,
            **kwargs,
        )
    starts = tf.convert_to_tensor(cast(Any, starts))
    # Param replacement - ends
    if len(graph_node.inputs) >= 3:
        ends = replace_parameter(
            value_before_replacement=ends,
            param_target='inputs',
            param_name=graph_node.inputs[2].name,
            **kwargs,
        )
        ends = tf.convert_to_tensor(cast(Any, ends))
    # Param replacement - axes
    if len(graph_node.inputs) >= 4:
        axes = replace_parameter(
            value_before_replacement=axes,
            param_target='inputs',
            param_name=graph_node.inputs[3].name,
            **kwargs,
        )
        axes = tf.convert_to_tensor(cast(Any, axes))
    # Param replacement - steps
    if len(graph_node.inputs) >= 5:
        steps = replace_parameter(
            value_before_replacement=steps,
            param_target='inputs',
            param_name=graph_node.inputs[4].name,
            **kwargs,
        )
        steps = tf.convert_to_tensor(cast(Any, steps))

    # Generation of TF OP
    tf_type = None
    if isinstance(graph_node_input, gs.Variable) \
        and 'simple_resize2' in tf_layers_dict[graph_node_input.name] \
        and tf_layers_dict[graph_node_input.name]['simple_resize2'] == True:
        tf_layers_dict[graph_node_output.name]['tf_node'] = \
            tf.identity(input=input_tensor)
        tf_layers_dict[graph_node_output.name]['simple_resize2'] = True
        tf_layers_dict[graph_node_output.name]['simple_resize_shape_op'] = tf_layers_dict[graph_node_input.name]['simple_resize_shape_op']
        tf_type = tf.identity

    else:
        if begin_ is None:
            ##### begin
            if isinstance(starts, tf.Tensor) and hasattr(starts, "numpy"):
                begin_ = [int(dim) for dim in cast(Any, starts).numpy().reshape(-1).tolist()]
            elif not isinstance(starts, np.ndarray) and tf_keras.backend.is_keras_tensor(starts):
                begin_ = starts
            elif isinstance(starts, np.ndarray):
                begin_ = [int(dim) for dim in starts.reshape(-1).tolist()]
            else:
                begin_ = [int(dim) for dim in cast(Any, starts)]
            ##### end
            if isinstance(ends, tf.Tensor) and hasattr(ends, "numpy"):
                end_ = [int(dim) for dim in cast(Any, ends).numpy().reshape(-1).tolist()]
            elif not isinstance(ends, np.ndarray) and tf_keras.backend.is_keras_tensor(ends):
                end_ = ends
            elif isinstance(ends, np.ndarray):
                end_ = [int(dim) for dim in ends.reshape(-1).tolist()]
            else:
                end_ = [int(dim) for dim in cast(Any, ends)]
            ##### strides
            strides_ = None
            if steps is not None:
                if isinstance(steps, tf.Tensor) and hasattr(steps, "numpy"):
                    strides_ = [int(dim) for dim in cast(Any, steps).numpy().reshape(-1).tolist()]
                elif not isinstance(steps, np.ndarray) and tf_keras.backend.is_keras_tensor(steps):
                    strides_ = steps
                elif isinstance(steps, np.ndarray):
                    strides_ = [int(dim) for dim in steps.reshape(-1).tolist()]
                else:
                    strides_ = [int(dim) for dim in cast(Any, steps)]

            axes_list_for_reorder = None
            if axes is not None:
                if isinstance(axes, (list, tuple)):
                    axes_list_for_reorder = [int(axis) for axis in list(axes)]
                elif isinstance(axes, np.ndarray):
                    axes_list_for_reorder = [int(axis) for axis in list(np.asarray(axes).reshape(-1))]
                elif tf.is_tensor(axes):
                    axes_tensor = cast(Any, axes)
                    if hasattr(axes_tensor, "numpy"):
                        axes_list_for_reorder = [int(axis) for axis in list(np.asarray(axes_tensor.numpy()).reshape(-1))]
                    else:
                        axes_tensor_inferred_value = getattr(axes_tensor, "_inferred_value", None)
                        if axes_tensor_inferred_value not in (None, [None]):
                            axes_list_for_reorder = [int(axis) for axis in list(axes_tensor_inferred_value)]
            if axes_list_for_reorder is not None:
                axes_list_for_reorder = [
                    int(axis) if int(axis) >= 0 else int(axis) + int(input_tensor_rank)
                    for axis in axes_list_for_reorder
                ]

            # Adjust the number of dimensions of the input data according to the number of axes [List]
            ##### Replace max values
            if isinstance(begin_, list):
                if axes_list_for_reorder is not None:
                    expanded_begin = [0] * int(input_tensor_rank)
                    for src_idx, axis_idx in enumerate(axes_list_for_reorder):
                        if src_idx >= len(begin_) or axis_idx < 0 or axis_idx >= int(input_tensor_rank):
                            continue
                        expanded_begin[int(axis_idx)] = begin_[int(src_idx)]
                    begin_ = expanded_begin
                begin_ = replace_max_values_negative_values(
                    input_tensor_shape=input_tensor_shape,
                    index_list=begin_,
                    axes=axes,
                )
            ##### Replace negative values
            if isinstance(end_, list):
                if axes_list_for_reorder is not None:
                    expanded_end = [0] * int(input_tensor_rank)
                    for src_idx, axis_idx in enumerate(axes_list_for_reorder):
                        if src_idx >= len(end_) or axis_idx < 0 or axis_idx >= int(input_tensor_rank):
                            continue
                        expanded_end[int(axis_idx)] = end_[int(src_idx)]
                    end_ = expanded_end
                end_ = replace_max_values_negative_values(
                    input_tensor_shape=input_tensor_shape,
                    index_list=end_,
                    axes=axes,
                )
            if strides_ is not None:
                if isinstance(strides_, list):
                    if axes_list_for_reorder is not None:
                        expanded_strides = [1] * int(input_tensor_rank)
                        for src_idx, axis_idx in enumerate(axes_list_for_reorder):
                            if src_idx >= len(strides_) or axis_idx < 0 or axis_idx >= int(input_tensor_rank):
                                continue
                            expanded_strides[int(axis_idx)] = strides_[int(src_idx)]
                        strides_ = expanded_strides

            # Adjust the number of dimensions of the input data according to the number of axes [Tensor]
            begin_dim0 = cast(Any, begin_).shape[0] if (not isinstance(begin_, list) and hasattr(begin_, 'shape') and cast(Any, begin_).shape is not None) else None
            if begin_dim0 is not None and input_tensor_rank > int(begin_dim0):
                begin_zeros = tf.zeros(shape=input_tensor_rank, dtype=tf.int64)
                scatter_axes = axes if axes is not None else tf.range(input_tensor_rank, dtype=tf.int64)
                begin_ = tf.tensor_scatter_nd_update(
                    tensor=begin_zeros,
                    indices=tf.reshape(tf.cast(cast(Any, scatter_axes), tf.int64), [-1, 1]),
                    updates=tf.cast(cast(Any, begin_), tf.int64),
                )
            begin_ = tf.cast(x=cast(Any, begin_), dtype=tf.int64)
            end_dim0 = cast(Any, end_).shape[0] if (not isinstance(end_, list) and hasattr(end_, 'shape') and cast(Any, end_).shape is not None) else None
            if end_dim0 is not None and input_tensor_rank > int(end_dim0):
                end_zeros = tf.zeros(input_tensor_rank, dtype=tf.int64)
                scatter_axes = axes if axes is not None else tf.range(input_tensor_rank, dtype=tf.int64)
                end_ = tf.tensor_scatter_nd_update(
                    tensor=end_zeros,
                    indices=tf.reshape(tf.cast(cast(Any, scatter_axes), tf.int64), [-1, 1]),
                    updates=tf.cast(cast(Any, end_), tf.int64),
                )
            end_ = tf.cast(x=cast(Any, end_), dtype=tf.int64)
            strides_dim0 = cast(Any, strides_).shape[0] if (strides_ is not None and not isinstance(strides_, list) and hasattr(strides_, 'shape') and cast(Any, strides_).shape is not None) else None
            if strides_dim0 is not None and input_tensor_rank > int(strides_dim0):
                strides_ones = tf.ones(input_tensor_rank, dtype=tf.int64)
                scatter_axes = axes if axes is not None else tf.range(input_tensor_rank, dtype=tf.int64)
                strides_ = tf.tensor_scatter_nd_update(
                    tensor=strides_ones,
                    indices=tf.reshape(tf.cast(cast(Any, scatter_axes), tf.int64), [-1, 1]),
                    updates=tf.cast(cast(Any, strides_), tf.int64),
                )
                strides_ = tf.cast(x=cast(Any, strides_), dtype=tf.int64)

            ##### begin_mask
            begin_bit_mask = tf.constant([2**idx for idx in range(input_tensor_rank)], dtype=tf.int32)
            cliped_values = tf.cast(
                tf.subtract(
                    1,
                    tf.clip_by_value(t=begin_, clip_value_min=0, clip_value_max=1),
                ),
                dtype=tf.int32,
            )
            begin_mask_ = tf.cast(
                tf.math.reduce_sum(
                    input_tensor=tf.math.multiply(x=cliped_values, y=begin_bit_mask),
                ),
                dtype=tf.int32,
            )
            begin_mask_inferred_value = getattr(begin_mask_, '_inferred_value', None)
            if begin_mask_inferred_value == [None]:
                axes_list = None
                if axes is not None:
                    if isinstance(axes, (list, tuple)):
                        axes_list = list(axes)
                    elif isinstance(axes, np.ndarray):
                        axes_list = axes.tolist() if axes.ndim > 0 else [int(axes)]
                    elif tf.is_tensor(axes):
                        axes_tensor = cast(Any, axes)
                        if hasattr(axes_tensor, 'numpy'):
                            axes_list = np.asarray(axes_tensor.numpy()).reshape(-1).tolist()
                        else:
                            axes_tensor_inferred_value = getattr(axes_tensor, '_inferred_value', None)
                            if axes_tensor_inferred_value not in (None, [None]):
                                axes_list = list(axes_tensor_inferred_value)
                if axes_list is not None:
                    begin_mask_ = sum(
                        1 << axis for axis in range(input_tensor_rank) if axis not in axes_list
                    )
                else:
                    begin_mask_ = 0

            ##### end_mask
            end_bit_mask = tf.constant([2**idx for idx in range(input_tensor_rank)], dtype=tf.int32)
            cliped_values = tf.cast(
                tf.subtract(
                    1,
                    tf.clip_by_value(t=end_, clip_value_min=0, clip_value_max=1),
                ),
                dtype=tf.int32,
            )
            end_mask_ = tf.cast(
                tf.math.reduce_sum(
                    input_tensor=tf.math.multiply(x=cliped_values, y=end_bit_mask),
                ),
                dtype=tf.int32,
            )
            end_mask_inferred_value = getattr(end_mask_, '_inferred_value', None)
            if end_mask_inferred_value == [None]:
                axes_list = None
                if axes is not None:
                    if isinstance(axes, (list, tuple)):
                        axes_list = list(axes)
                    elif isinstance(axes, np.ndarray):
                        axes_list = axes.tolist() if axes.ndim > 0 else [int(axes)]
                    elif tf.is_tensor(axes):
                        axes_tensor = cast(Any, axes)
                        if hasattr(axes_tensor, 'numpy'):
                            axes_list = np.asarray(axes_tensor.numpy()).reshape(-1).tolist()
                        else:
                            axes_tensor_inferred_value = getattr(axes_tensor, '_inferred_value', None)
                            if axes_tensor_inferred_value not in (None, [None]):
                                axes_list = list(axes_tensor_inferred_value)
                if axes_list is not None:
                    end_mask_ = sum(
                        1 << axis for axis in range(input_tensor_rank) if axis not in axes_list
                    )
                else:
                    end_mask_ = 0

            def _to_int_mask(mask_value: Any) -> int:
                if isinstance(mask_value, (int, np.integer)):
                    return int(mask_value)
                if tf.is_tensor(mask_value):
                    static_value = tf.get_static_value(mask_value)
                    if static_value is not None:
                        return int(np.asarray(static_value).reshape(-1)[0])
                    mask_numpy = getattr(mask_value, 'numpy', None)
                    if callable(mask_numpy):
                        return int(np.asarray(mask_numpy()).reshape(-1)[0])
                    return 0
                try:
                    return int(mask_value)
                except Exception:
                    return 0

            begin_mask_int = _to_int_mask(begin_mask_)
            end_mask_int = _to_int_mask(end_mask_)

            # strided_slice
            tf_layers_dict[graph_node_output.name]['tf_node'] = \
                tf.strided_slice(
                    input_=input_tensor,
                    begin=begin_,
                    end=end_,
                    strides=strides_,
                    begin_mask=begin_mask_int,
                    end_mask=end_mask_int,
                    name=graph_node.name,
                )
            try:
                if input_tensor_shape_list is not None \
                    and None not in input_tensor_shape_list \
                    and len(input_tensor_shape) > kwargs['number_of_dimensions_after_flexstridedslice_compression']:

                    onnx_slice_dims_count = 0

                    if isinstance(starts, np.ndarray):
                        onnx_slice_dims_count = len(starts)
                    elif hasattr(starts, 'numpy'):
                        starts_numpy = getattr(starts, 'numpy', None)
                        if callable(starts_numpy):
                            onnx_slice_dims_count = len(np.asarray(starts_numpy()))
                        else:
                            onnx_slice_dims_count = len(np.asarray(cast(Any, starts)))
                    elif isinstance(starts, int):
                        onnx_slice_dims_count = 1
                    elif tf_keras.backend.is_keras_tensor(starts):
                        onnx_slice_dims_count = len(starts.shape)
                    else:
                        onnx_slice_dims_count = len(starts)

                    ignore_axes = axes
                    if axes is None:
                        ignore_axes = [idx for idx in range(input_tensor_rank)]

                    tf_layers_dict[graph_node_output.name]['tf_node'] = \
                        stridedslice_with_flexing_deterrence(
                            input_tensor=input_tensor,
                            begin=begin_,
                            end=end_,
                            strides=strides_,
                            begin_mask=begin_mask_int,
                            end_mask=end_mask_int,
                            ignore_axes=ignore_axes,
                            compression_defult_value=COMPRESSION_DEFAULT_VALUE,
                            onnx_slice_dims_count=onnx_slice_dims_count,
                            output_shape=tf_layers_dict[graph_node_output.name]['tf_node'].shape,
                            name=graph_node.name,
                            **kwargs,
                        )
                else:
                    pass
            except Exception as ex:
                tf_layers_dict[graph_node_output.name]['tf_node'] = \
                    tf.strided_slice(
                        input_=input_tensor,
                        begin=begin_,
                        end=end_,
                        strides=strides_,
                        begin_mask=begin_mask_,
                        end_mask=end_mask_,
                        name=graph_node.name,
                    )
                print('')
                print(f'{Color.YELLOW("WARNING")} Dimensional compression of `Slice` fails. node.name: {graph_node.name}')
                print('')

            check_input_shape = input_tensor_shape_list
            output_tensor_shape = tf_layers_dict[graph_node_output.name]['tf_node'].shape
            check_output_shape = output_tensor_shape.as_list() \
                if output_tensor_shape.rank is not None else None
            if check_input_shape is not None \
                and check_output_shape is not None \
                and None not in check_input_shape \
                and None not in check_output_shape \
                and check_input_shape == check_output_shape:
                # Disable useless slice
                tf_layers_dict[graph_node_output.name]['tf_node'] = \
                    tf.identity(
                        input=input_tensor,
                        name=graph_node.name,
                    )

            elif input_tensor.shape.rank is not None:
                # FlexStridedSlice generation suppression process
                onnx_slice_dims_count = 0
                if isinstance(starts, np.ndarray):
                    onnx_slice_dims_count = len(starts)
                elif hasattr(starts, 'numpy'):
                    starts_numpy = getattr(starts, 'numpy', None)
                    if callable(starts_numpy):
                        onnx_slice_dims_count = len(np.asarray(starts_numpy()))
                    else:
                        onnx_slice_dims_count = len(np.asarray(cast(Any, starts)))
                elif isinstance(starts, int):
                    onnx_slice_dims_count = 1
                elif tf_keras.backend.is_keras_tensor(starts):
                    onnx_slice_dims_count = len(starts.shape)
                else:
                    onnx_slice_dims_count = len(starts)

                if onnx_slice_dims_count > COMPRESSION_DEFAULT_VALUE:
                    ignore_axes = axes
                    if axes is None:
                        ignore_axes = [idx for idx in range(input_tensor_rank)]
                    tf_layers_dict[graph_node_output.name]['tf_node'] = \
                        stridedslice_with_flexing_deterrence(
                            input_tensor=input_tensor,
                            begin=begin_,
                            end=end_,
                            strides=strides_,
                            begin_mask=begin_mask_,
                            end_mask=end_mask_,
                            ignore_axes=ignore_axes,
                            compression_defult_value=COMPRESSION_DEFAULT_VALUE,
                            onnx_slice_dims_count=onnx_slice_dims_count,
                            output_shape=tf_layers_dict[graph_node_output.name]['tf_node'].shape,
                            name=graph_node.name,
                            **kwargs,
                        )
        else:
            # OP replacement
            tf_layers_dict[graph_node_output.name]['tf_node'] = \
                tf.strided_slice(
                    input_=input_tensor,
                    begin=begin_,
                    end=end_,
                    strides=strides_,
                    begin_mask=begin_mask_,
                    end_mask=end_mask_,
                    ellipsis_mask=ellipsis_mask_,
                    new_axis_mask=new_axis_mask_,
                    shrink_axis_mask=shrink_axis_mask_,
                    name=graph_node.name,
                )
        tf_type = tf.strided_slice

    # Post-process transpose
    tf_layers_dict[graph_node_output.name]['tf_node'] = post_process_transpose(
        value_before_transpose=tf_layers_dict[graph_node_output.name]['tf_node'],
        param_target='outputs',
        param_name=graph_node.outputs[0].name,
        **kwargs,
    )

    # Generation of Debug Info
    tf_layers_dict[graph_node_output.name]['tf_node_info'] = \
        make_tf_node_info(
            node_info={
                'tf_op_type': tf_type,
                'tf_inputs': {
                    'input_': input_tensor,
                    'begin': starts,
                    'end': ends,
                    'strides': steps,
                },
                'tf_outputs': {
                    'output': tf_layers_dict[graph_node_output.name]['tf_node'],
                },
            }
        )
