import tensorflow as tf
from kerastuner import HyperParameters
from tensorflow import keras

from models.layers import recurrent_dense, strided_down, encoder_down_block


def guide_all_cnn_hyp_ninef_classi_model(num_strided_down=4,kernel=5,cnn_units=128, dense_units=128, recurrent_layers=8, noise=True):
    seq = keras.Input(shape=(30, 4))
    other = keras.Input(shape=9)

    x = seq
    for _ in range(num_strided_down):
        x = strided_down(x, cnn_units, 1, kernel)
        if noise:
            x = keras.layers.GaussianNoise(.01)(x)

    x = keras.layers.Flatten()(x)

    x = keras.layers.Concatenate()([x, other])

    x = keras.layers.Dense(dense_units, activation=tf.nn.leaky_relu)(x)
    for _ in range(recurrent_layers):
        x = recurrent_dense(x, dense_units)

    outputs = keras.layers.Dense(1)(x)
    # TODO: make a second output that is confidence, and have some allowance of reduced penalty
    #  for low confidence wrong guesses, but overall penalty for low confidence
    return keras.Model(inputs=[seq,other], outputs=outputs)


def guide_all_cnn_hyp_ninef_classi_model_hp(hp: HyperParameters):
    kernel= hp.Choice('kernel',[3,4,5])
    cnn_units = hp.Choice('cnn_units', [8,16,32,64])
    dense_units = hp.Choice('dense_units', [8,16,32,64])
    num_strided_down = hp.Int('num_strided_down', 3,5)
    recurrent_layers = hp.Choice('num_recurrent_layers', [0,1,2,3])
    noise = True # hp.Boolean('use_noise')

    model = guide_all_cnn_hyp_ninef_classi_model(num_strided_down=num_strided_down, kernel=kernel, cnn_units=cnn_units, dense_units=dense_units,
                             recurrent_layers=recurrent_layers, noise=noise)

    #metrics = [keras.metrics.MeanAbsoluteError(), keras.metrics.MeanSquaredError()]

    metrics = ['accuracy']

    model.compile(keras.optimizers.Adam(), tf.losses.binary_crossentropy, metrics=metrics)
    #model.compile(keras.optimizers.Adam(), tf.keras.losses.MeanSquaredError(), metrics=metrics)

    return model

tensorflow.keras.layers.Concatenate tensorflow.keras.Input tensorflow.keras.layers.Dense tensorflow.keras.Model tensorflow.keras.layers.GaussianNoise tensorflow.keras.optimizers.Adam tensorflow.keras.layers.Flatten
[(9, 'tensorflow.keras.Input', 'keras.Input', ([], {'shape': '(30, 4)'}), False, 'from tensorflow import keras\n'), (10, 'tensorflow.keras.Input', 'keras.Input', ([], {'shape': '(9)'}), False, 'from tensorflow import keras\n'), (29, 'tensorflow.keras.Model', 'keras.Model', ([], {'inputs': '[seq, other]', 'outputs': 'outputs'}), False, 'from tensorflow import keras\n'), (14, 'models.layers.strided_down', 'strided_down', (['x', 'cnn_units', '(1)', 'kernel'], {}), False, 'from models.layers import recurrent_dense, strided_down, encoder_down_block\n'), (18, 'tensorflow.keras.layers.Flatten', 'keras.layers.Flatten', ([], {}), False, 'from tensorflow import keras\n'), (20, 'tensorflow.keras.layers.Concatenate', 'keras.layers.Concatenate', ([], {}), False, 'from tensorflow import keras\n'), (22, 'tensorflow.keras.layers.Dense', 'keras.layers.Dense', (['dense_units'], {'activation': 'tf.nn.leaky_relu'}), False, 'from tensorflow import keras\n'), (24, 'models.layers.recurrent_dense', 'recurrent_dense', (['x', 'dense_units'], {}), False, 'from models.layers import recurrent_dense, strided_down, encoder_down_block\n'), (26, 'tensorflow.keras.layers.Dense', 'keras.layers.Dense', (['(1)'], {}), False, 'from tensorflow import keras\n'), (47, 'tensorflow.keras.optimizers.Adam', 'keras.optimizers.Adam', ([], {}), False, 'from tensorflow import keras\n'), (16, 'tensorflow.keras.layers.GaussianNoise', 'keras.layers.GaussianNoise', (['(0.01)'], {}), False, 'from tensorflow import keras\n')]