"""MLP Module."""

from torch import nn
from torch.nn import functional as F

from garage.torch.modules.multi_headed_mlp_module import MultiHeadedMLPModule

class MLPModule(MultiHeadedMLPModule):
    """MLP Model.

    A Pytorch module composed only of a multi-layer perceptron (MLP), which
    maps real-valued inputs to real-valued outputs.

    Args:
        input_dim (int) : Dimension of the network input.
        output_dim (int): Dimension of the network output.
        hidden_sizes (list[int]): Output dimension of dense layer(s).
            For example, (32, 32) means this MLP consists of two
            hidden layers, each with 32 hidden units.
        hidden_nonlinearity (callable or torch.nn.Module): Activation function
            for intermediate dense layer(s). It should return a torch.Tensor.
            Set it to None to maintain a linear activation.
        hidden_w_init (callable): Initializer function for the weight
            of intermediate dense layer(s). The function should return a
            torch.Tensor.
        hidden_b_init (callable): Initializer function for the bias
            of intermediate dense layer(s). The function should return a
            torch.Tensor.
        output_nonlinearity (callable or torch.nn.Module): Activation function
            for output dense layer. It should return a torch.Tensor.
            Set it to None to maintain a linear activation.
        output_w_init (callable): Initializer function for the weight
            of output dense layer(s). The function should return a
            torch.Tensor.
        output_b_init (callable): Initializer function for the bias
            of output dense layer(s). The function should return a
            torch.Tensor.
        layer_normalization (bool): Bool for using layer normalization or not.

    """

    def __init__(self,
                 input_dim,
                 output_dim,
                 hidden_sizes,
                 n_heads=1,
                 n_tasks=1,
                 hidden_nonlinearity=F.relu,
                 hidden_w_init=nn.init.xavier_normal_,
                 hidden_b_init=nn.init.zeros_,
                 output_nonlinearity=None,
                 output_w_init=nn.init.xavier_normal_,
                 output_b_init=nn.init.zeros_,
                 layer_normalization=False, 
                 noisy_net=False, 
                 adaptor=False, 
                 zero_alpha=False,
                 ReDo=False,
                 no_stats=False,
                 forward_manner="Normal"):
        super().__init__(n_heads, n_tasks, input_dim, output_dim, hidden_sizes,
                         hidden_nonlinearity, hidden_w_init, hidden_b_init,
                         output_nonlinearity, output_w_init, output_b_init,
                         layer_normalization, noisy_net, adaptor, zero_alpha, ReDo, no_stats, forward_manner)

        self._output_dim = output_dim

    # pylint: disable=arguments-differ
    def forward(self, input_value, features=None, pandc_cas = False):
        """Forward method.

        Args:
            input_value (torch.Tensor): Input values with (N, *, input_dim)
                shape.

        Returns:
            torch.Tensor: Output value

        """
        ret = super().forward(input_value, features=features,pandc_cas = pandc_cas)
        # if len(ret) == 1:
        #     ret = ret[0]
        return ret

    @property
    def output_dim(self):
        """Return output dimension of network.

        Returns:
            int: Output dimension of network.

        """
        return self._output_dim


# MLP module에서 adaptor True로 두고 forward에서 feature를 받아주자
# MLP module에서 feature 무조건 저장하기.
# KB에서 feature 뽑을 때 무조건 no_grad 켜두고 뽑기