import numpy as np
import warnings

from tensorflow.keras.layers import Input, Conv2D, BatchNormalization, Activation, ZeroPadding2D, AveragePooling2D, MaxPooling2D, GlobalAveragePooling2D, GlobalMaxPooling2D
from tensorflow.keras.layers import Dense
from tensorflow.keras.layers import add, Flatten
from tensorflow.keras.models import Model
from tensorflow.keras.preprocessing import image
import tensorflow.keras.backend as K
from keras_applications.imagenet_utils import _obtain_input_shape
from keras.engine.topology import get_source_inputs

def identity_block(input_tensor, kernel_size, filters, stage, block):
    
    filters1, filters2, filters3 = filters
    if K.image_data_format() == 'channels_last':
        bn_axis = 3
    else:
        bn_axis = 1
    conv_name_base = 'res' + str(stage) + block + '_branch'
    bn_name_base = 'bn' + str(stage) + block + '_branch'
 
    x = Conv2D(filters1, (1, 1), name=conv_name_base + '2a')(input_tensor)       # kernel_initializer='he_normal' ???
    x = BatchNormalization(axis=bn_axis, name=bn_name_base + '2a')(x)
    x = Activation('relu')(x)
 
    x = Conv2D(filters2, kernel_size,
               padding='same', name=conv_name_base + '2b')(x)
    x = BatchNormalization(axis=bn_axis, name=bn_name_base + '2b')(x)
    x = Activation('relu')(x)
 
    x = Conv2D(filters3, (1, 1), name=conv_name_base + '2c')(x)
    x = BatchNormalization(axis=bn_axis, name=bn_name_base + '2c')(x)
 
    x = add([x, input_tensor])
    x = Activation('relu')(x)
    return x

def conv_block(input_tensor, kernel_size, filters, stage, block, strides=(2, 2)):
    
    filters1, filters2, filters3 = filters
    if K.image_data_format() == 'channels_last':
        bn_axis = 3
    else:
        bn_axis = 1
    conv_name_base = 'res' + str(stage) + block + '_branch'
    bn_name_base = 'bn' + str(stage) + block + '_branch'
 
    x = Conv2D(filters1, (1, 1), strides=strides,
               name=conv_name_base + '2a')(input_tensor)
    x = BatchNormalization(axis=bn_axis, name=bn_name_base + '2a')(x)
    x = Activation('relu')(x)
 
    x = Conv2D(filters2, kernel_size, padding='same',
               name=conv_name_base + '2b')(x)
    x = BatchNormalization(axis=bn_axis, name=bn_name_base + '2b')(x)
    x = Activation('relu')(x)
 
    x = Conv2D(filters3, (1, 1), name=conv_name_base + '2c')(x)
    x = BatchNormalization(axis=bn_axis, name=bn_name_base + '2c')(x)
 
    shortcut = Conv2D(filters3, (1, 1), strides=strides,
                      name=conv_name_base + '1')(input_tensor)
    shortcut = BatchNormalization(axis=bn_axis, name=bn_name_base + '1')(shortcut)
 
    x = add([x, shortcut])
    x = Activation('relu')(x)
    return x

def ResNet50(include_top = False, 
            weights=None,
            input_tensor=None, input_shape=None,
            pooling=None,
            classes=1000):
   
    # if weights not in {'imagenet', None}:
    #     raise ValueError('The `weights` argument should be either '
    #                      '`None` (random initialization) or `imagenet` '
    #                      '(pre-training on ImageNet).')
 
    # if weights == 'imagenet' and include_top and classes != 1000:
    #     raise ValueError('If using `weights` as imagenet with `include_top`'
    #                      ' as true, `classes` should be 1000')
 
 
    input_shape = _obtain_input_shape(input_shape,
                                      default_size=224,
                                      min_size=32,
                                      data_format=K.image_data_format(),
                                      require_flatten = include_top)
 
    if input_tensor is None:
        img_input = Input(shape=input_shape)
    else:
        if not K.is_keras_tensor(input_tensor):
            img_input = Input(tensor=input_tensor, shape=input_shape)
        else:
            img_input = input_tensor
    if K.image_data_format() == 'channels_last':
        bn_axis = 3
    else:
        bn_axis = 1
 
    #x = ZeroPadding2D((3, 3))(img_input)
    x = Conv2D(64, (7, 7), strides=(2, 2), name='conv1')(img_input)
    x = BatchNormalization(axis=bn_axis, name='bn_conv1')(x)
    x = Activation('relu')(x)
    #x = MaxPooling2D((3, 3), strides=(2, 2))(x)
 
    x = conv_block(x, 3, [64, 64, 256], stage=2, block='a', strides=(1, 1))
    x = identity_block(x, 3, [64, 64, 256], stage=2, block='b')
    x = identity_block(x, 3, [64, 64, 256], stage=2, block='c')
 
    x = conv_block(x, 3, [128, 128, 512], stage=3, block='a')
    x = identity_block(x, 3, [128, 128, 512], stage=3, block='b')
    x = identity_block(x, 3, [128, 128, 512], stage=3, block='c')
    x = identity_block(x, 3, [128, 128, 512], stage=3, block='d')
 
    x = conv_block(x, 3, [256, 256, 1024], stage=4, block='a')
    x = identity_block(x, 3, [256, 256, 1024], stage=4, block='b')
    x = identity_block(x, 3, [256, 256, 1024], stage=4, block='c')
    x = identity_block(x, 3, [256, 256, 1024], stage=4, block='d')
    x = identity_block(x, 3, [256, 256, 1024], stage=4, block='e')
    x = identity_block(x, 3, [256, 256, 1024], stage=4, block='f')
 
    x = conv_block(x, 3, [512, 512, 2048], stage=5, block='a')
    x = identity_block(x, 3, [512, 512, 2048], stage=5, block='b')
    x = identity_block(x, 3, [512, 512, 2048], stage=5, block='c')
 
    x = AveragePooling2D(name='avg_pool')(x)
 
    if include_top:
        x = Flatten()(x)
        x = Dense(classes, activation='softmax', name='fc1000')(x)
    else:
        if pooling == 'avg':
            x = GlobalAveragePooling2D()(x)
            x = Dense(classes, activation='softmax', name='resnet50')(x)
        elif pooling == 'max':
            x = GlobalMaxPooling2D()(x)
 
    if input_tensor is not None:
        inputs = get_source_inputs(input_tensor)
    else:
        inputs = img_input
 
    model = Model(inputs, x, name='resnet50')
 
 
    # if weights == 'imagenet':
    #     if include_top:
    #         weights_path = get_file('resnet50_weights_tf_dim_ordering_tf_kernels.h5',
    #                                 WEIGHTS_PATH,
    #                                 cache_subdir='models',
    #                                 md5_hash='a7b3fe01876f51b976af0dea6bc144eb')
    #     else:
    #         weights_path = get_file('resnet50_weights_tf_dim_ordering_tf_kernels_notop.h5',
    #                                 WEIGHTS_PATH_NO_TOP,
    #                                 cache_subdir='models',
    #                                 md5_hash='a268eb855778b3df3c7506639542a6af')
    #     model.load_weights(weights_path)
    #     if K.backend() == 'theano':
    #         layer_utils.convert_all_kernels_in_model(model)
 
    #     if K.image_data_format() == 'channels_first':
    #         if include_top:
    #             maxpool = model.get_layer(name='avg_pool')
    #             shape = maxpool.output_shape[1:]
    #             dense = model.get_layer(name='fc1000')
    #             layer_utils.convert_dense_weights_data_format(dense, shape, 'channels_first')
 
    #         if K.backend() == 'tensorflow':
    #             warnings.warn('You are using the TensorFlow backend, yet you '
    #                           'are using the Theano '
    #                           'image data format convention '
    #                           '(`image_data_format="channels_first"`). '
    #                           'For best performance, set '
    #                           '`image_data_format="channels_last"` in '
    #                           'your Keras config '
    #                           'at ~/.keras/keras.json.')
    return model
tensorflow.keras.layers.AveragePooling2D tensorflow.keras.layers.Activation tensorflow.keras.layers.GlobalAveragePooling2D tensorflow.keras.backend.image_data_format tensorflow.keras.models.Model tensorflow.keras.backend.is_keras_tensor tensorflow.keras.layers.Dense tensorflow.keras.layers.Conv2D tensorflow.keras.layers.BatchNormalization tensorflow.keras.layers.GlobalMaxPooling2D tensorflow.keras.layers.add tensorflow.keras.layers.Flatten tensorflow.keras.layers.Input
[(35, 'tensorflow.keras.layers.add', 'add', (['[x, input_tensor]'], {}), False, 'from tensorflow.keras.layers import add, Flatten\n'), (66, 'tensorflow.keras.layers.add', 'add', (['[x, shortcut]'], {}), False, 'from tensorflow.keras.layers import add, Flatten\n'), (147, 'tensorflow.keras.models.Model', 'Model', (['inputs', 'x'], {'name': '"""resnet50"""'}), False, 'from tensorflow.keras.models import Model\n'), (16, 'tensorflow.keras.backend.image_data_format', 'K.image_data_format', ([], {}), True, 'import tensorflow.keras.backend as K\n'), (23, 'tensorflow.keras.layers.Conv2D', 'Conv2D', (['filters1', '(1, 1)'], {'name': "(conv_name_base + '2a')"}), False, 'from tensorflow.keras.layers import Input, Conv2D, BatchNormalization, Activation, ZeroPadding2D, AveragePooling2D, MaxPooling2D, GlobalAveragePooling2D, GlobalMaxPooling2D\n'), (24, 'tensorflow.keras.layers.BatchNormalization', 'BatchNormalization', ([], {'axis': 'bn_axis', 'name': "(bn_name_base + '2a')"}), False, 'from tensorflow.keras.layers import Input, Conv2D, BatchNormalization, Activation, ZeroPadding2D, AveragePooling2D, MaxPooling2D, GlobalAveragePooling2D, GlobalMaxPooling2D\n'), (25, 'tensorflow.keras.layers.Activation', 'Activation', (['"""relu"""'], {}), False, 'from tensorflow.keras.layers import Input, Conv2D, BatchNormalization, Activation, ZeroPadding2D, AveragePooling2D, MaxPooling2D, GlobalAveragePooling2D, GlobalMaxPooling2D\n'), (27, 'tensorflow.keras.layers.Conv2D', 'Conv2D', (['filters2', 'kernel_size'], {'padding': '"""same"""', 'name': "(conv_name_base + '2b')"}), False, 'from tensorflow.keras.layers import Input, Conv2D, BatchNormalization, Activation, ZeroPadding2D, AveragePooling2D, MaxPooling2D, GlobalAveragePooling2D, GlobalMaxPooling2D\n'), (29, 'tensorflow.keras.layers.BatchNormalization', 'BatchNormalization', ([], {'axis': 'bn_axis', 'name': "(bn_name_base + '2b')"}), False, 'from tensorflow.keras.layers import Input, Conv2D, BatchNormalization, Activation, ZeroPadding2D, AveragePooling2D, MaxPooling2D, GlobalAveragePooling2D, GlobalMaxPooling2D\n'), (30, 'tensorflow.keras.layers.Activation', 'Activation', (['"""relu"""'], {}), False, 'from tensorflow.keras.layers import Input, Conv2D, BatchNormalization, Activation, ZeroPadding2D, AveragePooling2D, MaxPooling2D, GlobalAveragePooling2D, GlobalMaxPooling2D\n'), (32, 'tensorflow.keras.layers.Conv2D', 'Conv2D', (['filters3', '(1, 1)'], {'name': "(conv_name_base + '2c')"}), False, 'from tensorflow.keras.layers import Input, Conv2D, BatchNormalization, Activation, ZeroPadding2D, AveragePooling2D, MaxPooling2D, GlobalAveragePooling2D, GlobalMaxPooling2D\n'), (33, 'tensorflow.keras.layers.BatchNormalization', 'BatchNormalization', ([], {'axis': 'bn_axis', 'name': "(bn_name_base + '2c')"}), False, 'from tensorflow.keras.layers import Input, Conv2D, BatchNormalization, Activation, ZeroPadding2D, AveragePooling2D, MaxPooling2D, GlobalAveragePooling2D, GlobalMaxPooling2D\n'), (36, 'tensorflow.keras.layers.Activation', 'Activation', (['"""relu"""'], {}), False, 'from tensorflow.keras.layers import Input, Conv2D, BatchNormalization, Activation, ZeroPadding2D, AveragePooling2D, MaxPooling2D, GlobalAveragePooling2D, GlobalMaxPooling2D\n'), (42, 'tensorflow.keras.backend.image_data_format', 'K.image_data_format', ([], {}), True, 'import tensorflow.keras.backend as K\n'), (49, 'tensorflow.keras.layers.Conv2D', 'Conv2D', (['filters1', '(1, 1)'], {'strides': 'strides', 'name': "(conv_name_base + '2a')"}), False, 'from tensorflow.keras.layers import Input, Conv2D, BatchNormalization, Activation, ZeroPadding2D, AveragePooling2D, MaxPooling2D, GlobalAveragePooling2D, GlobalMaxPooling2D\n'), (51, 'tensorflow.keras.layers.BatchNormalization', 'BatchNormalization', ([], {'axis': 'bn_axis', 'name': "(bn_name_base + '2a')"}), False, 'from tensorflow.keras.layers import Input, Conv2D, BatchNormalization, Activation, ZeroPadding2D, AveragePooling2D, MaxPooling2D, GlobalAveragePooling2D, GlobalMaxPooling2D\n'), (52, 'tensorflow.keras.layers.Activation', 'Activation', (['"""relu"""'], {}), False, 'from tensorflow.keras.layers import Input, Conv2D, BatchNormalization, Activation, ZeroPadding2D, AveragePooling2D, MaxPooling2D, GlobalAveragePooling2D, GlobalMaxPooling2D\n'), (54, 'tensorflow.keras.layers.Conv2D', 'Conv2D', (['filters2', 'kernel_size'], {'padding': '"""same"""', 'name': "(conv_name_base + '2b')"}), False, 'from tensorflow.keras.layers import Input, Conv2D, BatchNormalization, Activation, ZeroPadding2D, AveragePooling2D, MaxPooling2D, GlobalAveragePooling2D, GlobalMaxPooling2D\n'), (56, 'tensorflow.keras.layers.BatchNormalization', 'BatchNormalization', ([], {'axis': 'bn_axis', 'name': "(bn_name_base + '2b')"}), False, 'from tensorflow.keras.layers import Input, Conv2D, BatchNormalization, Activation, ZeroPadding2D, AveragePooling2D, MaxPooling2D, GlobalAveragePooling2D, GlobalMaxPooling2D\n'), (57, 'tensorflow.keras.layers.Activation', 'Activation', (['"""relu"""'], {}), False, 'from tensorflow.keras.layers import Input, Conv2D, BatchNormalization, Activation, ZeroPadding2D, AveragePooling2D, MaxPooling2D, GlobalAveragePooling2D, GlobalMaxPooling2D\n'), (59, 'tensorflow.keras.layers.Conv2D', 'Conv2D', (['filters3', '(1, 1)'], {'name': "(conv_name_base + '2c')"}), False, 'from tensorflow.keras.layers import Input, Conv2D, BatchNormalization, Activation, ZeroPadding2D, AveragePooling2D, MaxPooling2D, GlobalAveragePooling2D, GlobalMaxPooling2D\n'), (60, 'tensorflow.keras.layers.BatchNormalization', 'BatchNormalization', ([], {'axis': 'bn_axis', 'name': "(bn_name_base + '2c')"}), False, 'from tensorflow.keras.layers import Input, Conv2D, BatchNormalization, Activation, ZeroPadding2D, AveragePooling2D, MaxPooling2D, GlobalAveragePooling2D, GlobalMaxPooling2D\n'), (62, 'tensorflow.keras.layers.Conv2D', 'Conv2D', (['filters3', '(1, 1)'], {'strides': 'strides', 'name': "(conv_name_base + '1')"}), False, 'from tensorflow.keras.layers import Input, Conv2D, BatchNormalization, Activation, ZeroPadding2D, AveragePooling2D, MaxPooling2D, GlobalAveragePooling2D, GlobalMaxPooling2D\n'), (64, 'tensorflow.keras.layers.BatchNormalization', 'BatchNormalization', ([], {'axis': 'bn_axis', 'name': "(bn_name_base + '1')"}), False, 'from tensorflow.keras.layers import Input, Conv2D, BatchNormalization, Activation, ZeroPadding2D, AveragePooling2D, MaxPooling2D, GlobalAveragePooling2D, GlobalMaxPooling2D\n'), (67, 'tensorflow.keras.layers.Activation', 'Activation', (['"""relu"""'], {}), False, 'from tensorflow.keras.layers import Input, Conv2D, BatchNormalization, Activation, ZeroPadding2D, AveragePooling2D, MaxPooling2D, GlobalAveragePooling2D, GlobalMaxPooling2D\n'), (93, 'tensorflow.keras.layers.Input', 'Input', ([], {'shape': 'input_shape'}), False, 'from tensorflow.keras.layers import Input, Conv2D, BatchNormalization, Activation, ZeroPadding2D, AveragePooling2D, MaxPooling2D, GlobalAveragePooling2D, GlobalMaxPooling2D\n'), (99, 'tensorflow.keras.backend.image_data_format', 'K.image_data_format', ([], {}), True, 'import tensorflow.keras.backend as K\n'), (105, 'tensorflow.keras.layers.Conv2D', 'Conv2D', (['(64)', '(7, 7)'], {'strides': '(2, 2)', 'name': '"""conv1"""'}), False, 'from tensorflow.keras.layers import Input, Conv2D, BatchNormalization, Activation, ZeroPadding2D, AveragePooling2D, MaxPooling2D, GlobalAveragePooling2D, GlobalMaxPooling2D\n'), (106, 'tensorflow.keras.layers.BatchNormalization', 'BatchNormalization', ([], {'axis': 'bn_axis', 'name': '"""bn_conv1"""'}), False, 'from tensorflow.keras.layers import Input, Conv2D, BatchNormalization, Activation, ZeroPadding2D, AveragePooling2D, MaxPooling2D, GlobalAveragePooling2D, GlobalMaxPooling2D\n'), (107, 'tensorflow.keras.layers.Activation', 'Activation', (['"""relu"""'], {}), False, 'from tensorflow.keras.layers import Input, Conv2D, BatchNormalization, Activation, ZeroPadding2D, AveragePooling2D, MaxPooling2D, GlobalAveragePooling2D, GlobalMaxPooling2D\n'), (130, 'tensorflow.keras.layers.AveragePooling2D', 'AveragePooling2D', ([], {'name': '"""avg_pool"""'}), False, 'from tensorflow.keras.layers import Input, Conv2D, BatchNormalization, Activation, ZeroPadding2D, AveragePooling2D, MaxPooling2D, GlobalAveragePooling2D, GlobalMaxPooling2D\n'), (143, 'keras.engine.topology.get_source_inputs', 'get_source_inputs', (['input_tensor'], {}), False, 'from keras.engine.topology import get_source_inputs\n'), (89, 'tensorflow.keras.backend.image_data_format', 'K.image_data_format', ([], {}), True, 'import tensorflow.keras.backend as K\n'), (95, 'tensorflow.keras.backend.is_keras_tensor', 'K.is_keras_tensor', (['input_tensor'], {}), True, 'import tensorflow.keras.backend as K\n'), (96, 'tensorflow.keras.layers.Input', 'Input', ([], {'tensor': 'input_tensor', 'shape': 'input_shape'}), False, 'from tensorflow.keras.layers import Input, Conv2D, BatchNormalization, Activation, ZeroPadding2D, AveragePooling2D, MaxPooling2D, GlobalAveragePooling2D, GlobalMaxPooling2D\n'), (133, 'tensorflow.keras.layers.Flatten', 'Flatten', ([], {}), False, 'from tensorflow.keras.layers import add, Flatten\n'), (134, 'tensorflow.keras.layers.Dense', 'Dense', (['classes'], {'activation': '"""softmax"""', 'name': '"""fc1000"""'}), False, 'from tensorflow.keras.layers import Dense\n'), (137, 'tensorflow.keras.layers.GlobalAveragePooling2D', 'GlobalAveragePooling2D', ([], {}), False, 'from tensorflow.keras.layers import Input, Conv2D, BatchNormalization, Activation, ZeroPadding2D, AveragePooling2D, MaxPooling2D, GlobalAveragePooling2D, GlobalMaxPooling2D\n'), (138, 'tensorflow.keras.layers.Dense', 'Dense', (['classes'], {'activation': '"""softmax"""', 'name': '"""resnet50"""'}), False, 'from tensorflow.keras.layers import Dense\n'), (140, 'tensorflow.keras.layers.GlobalMaxPooling2D', 'GlobalMaxPooling2D', ([], {}), False, 'from tensorflow.keras.layers import Input, Conv2D, BatchNormalization, Activation, ZeroPadding2D, AveragePooling2D, MaxPooling2D, GlobalAveragePooling2D, GlobalMaxPooling2D\n')]