# Copyright (c) 2018-2019, NVIDIA CORPORATION. All rights reserved.
#
# Licensed 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.

import torch                    # for torch.cat and torch.zeros
import torch.nn as nn
import torch.utils.model_zoo as model_zoo

from nhwc.conv import Conv2d_NHWC
from nhwc.batch_norm import BatchNorm2d_NHWC
from nhwc.max_pool import MaxPool2d_NHWC

# Group batch norm
from apex.parallel import SyncBatchNorm as gbn
# Persistent group BN for NHWC case
from apex.contrib.groupbn.batch_norm import BatchNorm2d_NHWC as gbn_persistent
import apex.parallel

__all__ = ['resnet']

model_urls = {
    'resnet18': 'https://download.pytorch.org/models/resnet18-5c106cde.pth',
    'resnet34': 'https://download.pytorch.org/models/resnet34-333f7ec4.pth',
    'resnet50': 'https://download.pytorch.org/models/resnet50-19c8e357.pth',
    'resnet101': 'https://download.pytorch.org/models/resnet101-5d3b4d8f.pth',
    'resnet152': 'https://download.pytorch.org/models/resnet152-b121ed2d.pth',
}


class Layers_NCHW:
    Conv2d = nn.Conv2d
    MaxPool = nn.MaxPool2d
    BnAddRelu = None # will be assigned at construction

    def __init__(self, bn_group, **kwargs):
        super(Layers_NCHW, self).__init__()
        self.nhwc = False
        self.bn_group = bn_group

        if (bn_group > 1):
            bn_base = gbn
        else:
            bn_base = nn.BatchNorm2d

        class BnAddRelu_(bn_base):
            def __init__(self, planes, fuse_relu=False, bn_group=1):
                if (bn_group > 1):
                    super(BnAddRelu_, self).__init__(