# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
from typing import Optional

import torch

import vllm.model_executor.layers.fused_moe.modular_kernel as mk
from vllm.model_executor.layers.fused_moe.config import FusedMoEQuantConfig
from vllm.model_executor.layers.fused_moe.deep_gemm_moe import (
    DeepGemmExperts, _valid_deep_gemm, _valid_deep_gemm_shape)


class TritonOrGroupGemmExperts(mk.CustomizedFusedMoEPermuteExpertsUnpermute):

    def __init__(
        self,
        use_fp8_w8a8: bool = False,
        use_int8_w8a8: bool = False,
        use_int8_w8a16: bool = False,
        use_int4_w4a16: bool = False,
        use_int4_w4a8: bool = False,
        per_act_token_quant: bool = False,
        block_shape: Optional[list[int]] = None,
        fused_experts = None
    ):
        super().__init__(
            FusedMoEQuantConfig.make(
                use_fp8_w8a8=use_fp8_w8a8,
                use_int8_w8a8=use_int8_w8a8,
                use_int4_w4a8=use_int4_w4a8,
                use_int8_w8a16=use_int8_w8a16,
                use_int4_w4a16=use_int4_w4a16,
                per_act_token_quant=per_act_token_quant,
                block_shape=block_shape,
            ))
        self.fused_experts = fused_experts

    @property
    def activation_formats(
        self
    ) -> tuple[mk.FusedMoEActivationFormat, mk.FusedMoEActivationFormat]:
        return (mk.FusedMoEActivationFormat.Standard,
                mk.FusedMoEActivationFormat.Standard)

    def supports_chunking(self) -> bool:
        return True

    def supports_expert_map(self) -> bool:
        return True

    def workspace_shapes(
        self,
        a: torch.Tensor,
        aq: torch.Tensor,
        M: int,
        N: int,
        K: int,
        topk: int,
        global_num_experts: int,
        local_num_experts: int,
    ) -> tuple[tuple[int, ...], tuple[int, ...], tuple[int, ...], torch.dtype]:
        raise NotImplementedError

    def apply(
        self,
        output: torch.Tensor,
        hidden_states: torch.Tensor,
        q_hidden_states: torch.Tensor,
        w1: torch.Tensor,
        w2: torch.Tensor,
        topk_ids: torch.Tensor,
        activation: str,
        global_num_experts: int,
        expert_map: Optional[torch.Tensor],
        w1_scale: Optional[torch.Tensor],
        w2_scale: Optional[torch.Tensor],
        w1_zp: Optional[torch.Tensor],
        w2_zp: Optional[torch.Tensor],
        a1q_scale: Optional[torch.Tensor],
        a2_scale: Optional[torch.Tensor],
        workspace13: torch.Tensor,
        workspace2: torch.Tensor,
        topk_weights: Optional[torch.Tensor] = None,
        expert_num_tokens: Optional[torch.Tensor] = None,
        use_nn_moe: Optional[bool] = False,
        shared_output: Optional[torch.Tensor] = None,
        routed_scaling_factor: Optional[float] = None,
        expert_num_tokens_cpu: torch.Tensor = None,
    ):
        assert self.fused_experts is not None

        return self.fused_experts(
                    x=hidden_states,
                    w1=w1,
                    w2=w2,
                    topk_ids=topk_ids,
                    topk_weights=topk_weights,
                    global_num_experts=global_num_experts,
                    expert_map=expert_map,
                    apply_router_weight_on_input=False,
                    activation=activation,
                    w1_scale=w1_scale,
                    w2_scale=w2_scale,
                    a1_scale=a1q_scale,
                    a2_scale=a2_scale,
                    expert_num_tokens=expert_num_tokens,
                    use_nn_moe=use_nn_moe,
                    shared_output=shared_output,
                    routed_scaling_factor=routed_scaling_factor,
                    q_x=q_hidden_states,
                    expert_num_tokens_cpu=expert_num_tokens_cpu
                )
