import torch
import torch.nn as nn

# from maskrcnn_benchmark.modeling.backbone import resnet


class Predictor(nn.Module):

    def __init__(self, cfg=None, select_num=10, class_num=4, side=False, random_select=False):
        super(Predictor, self).__init__()
        self.side = side

        # Layers for global feature
        self.avgpool_glob = nn.AdaptiveAvgPool2d(output_size=7)
        self.conv_glob1 = nn.Conv2d(1024, 512, 3, padding=1)
        self.relu_glob1 = nn.ReLU(inplace=True)
        self.conv_glob2 = nn.Conv2d(512, 256, 3, padding=1)
        self.relu_glob2 = nn.ReLU(inplace=True)

        self.avgpool_glob2 = nn.AdaptiveAvgPool2d(output_size=1)

        # # Layers for object features
        # self.avgpool_obj = nn.AdaptiveAvgPool2d(output_size=1)

        # # Selector
        # self.random_select = random_select
        # self.select_num = select_num
        # if not random_select:
        #     self.selector = Selector()
        #     self.sftmax = nn.Softmax(dim=0)

        # FC
        self.drop = nn.Dropout(p=0.2) 
        self.fc1 = nn.Linear(256, 128)
        self.relu1 = nn.ReLU(inplace=True)
        self.fc2 = nn.Linear(128, 64)
        self.relu2 = nn.ReLU(inplace=True)
        self.fc3 = nn.Linear(64, class_num)
        if self.side:
            self.fc_side = nn.Linear(128, 21)



    def forward(self, x):
        # Processing global feature
        glob = self.relu_glob1(self.conv_glob1(x['glob_feature']))
        glob = self.relu_glob2(self.conv_glob2(glob))
        glob = self.avgpool_glob(glob) # (1, 256, 7, 7)
        # Processing object features
        # obj = x['roi_features'] # (N, 2048, 7, 7)

        # obj = self.avgpool_obj(obj) # (select_num, 2048, 1, 1)
        glob = self.avgpool_glob2(glob) # (1, 256, 1, 1)
        # glob = glob.expand(obj.shape[0], 256, 1, 1)
        
        # x = torch.cat((obj, glob), dim=1) # (N, 2048+256, 1, 1)
        x = torch.squeeze(glob) # (N, 256, 1, 1)
        del glob
        
        x = self.drop(self.relu1(self.fc1(x))) # (1, 128)
        
        
        tmp = torch.flatten(x) # (128)
        # x = self.drop(self.relu2(self.fc2(tmp)))
        x = self.drop(self.fc2(tmp)) # (64)
        x = self.drop(self.fc3(x)) # (class_num)
        # print(tmp.shape)
        # x = self.fc2(tmp)
        # x = self.fc3(x)
        if self.side:
            side = self.fc_side(tmp)

        return (x, side) if self.side else x



class Selector(nn.Module):
    def __init__(self):
        super(Selector, self).__init__()
        self.conv1 = nn.Conv2d(2048+256, 256, 3, padding=1)
        self.relu1 = nn.ReLU(inplace=True)
        self.conv2 = nn.Conv2d(256, 16, 3, padding=1)
        self.relu2 = nn.ReLU(inplace=True)
        self.conv3 = nn.Conv2d(16, 1, 3, padding=1)
        self.relu3 = nn.ReLU(inplace=True)
        self.avgpool = nn.AdaptiveAvgPool2d(output_size=1)

    def forward(self, x):
        # x.shape (N, 2048+256, 7, 7)
        x = self.relu1(self.conv1(x)) 
        x = self.relu2(self.conv2(x))
        x = self.relu3(self.conv3(x))
        x = self.avgpool(x)

        return x

def build_predictor(cfg, side, random_select=False):
    model = Predictor(cfg, side=side, random_select=random_select)
    return model

if __name__ == '__main__':
    device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
    roi = torch.randn((15,2048,7,7))
    glob = torch.randn((1,1024,45,80))
    roi = roi.to(device)
    glob = glob.to(device)
    x = {'roi_features':roi,
         'glob_feature':glob}

    model = Predictor()
    model.to(device)
    output = model(x)
