import tensorflow as tf
import data_reader
from math import floor, ceil
from functools import partial
from conv2d_spectral_norm import conv2d_spectral_norm
from coord_conv import AddCoords

edges_discriminator_name = 'edges_discriminator'
images_discriminator_name = 'image_discriminator'

def float2int(float_image):
    return tf.cast(tf.clip_by_value((float_image + 1) * 127.0, 0, 255), dtype=tf.uint8)
    # return tf.cast(tf.clip_by_value(float_image, 0.0, 255.0), dtype=tf.uint8)

def int2float(int_image):
    return tf.cast(int_image, tf.float32) * (2.0 / 255.0) - 1.0
    # return tf.cast(int_image, tf.float32)

def print_tensor(tensor, comment):
    return tensor
    # return tf.Print(tensor, [tensor], comment, summarize=6)

def activation_summary(x):
    """Helper to create summaries for activations.
    Creates a summary that provides a histogram of activations.
    Creates a summary that measures the sparsity of activations.
    Args:
      x: Tensor
    Returns:
      nothing
    """
    # Remove 'tower_[0-9]/' from the name in case this is a multi-GPU training
    # session. This helps the clarity of presentation on tensorboard.
    tf.summary.histogram(x.op.name + '/activations', x)
    tf.summary.scalar(x.op.name + '/sparsity', tf.nn.zero_fraction(x))

def float32_variable_storage_getter(getter, name, shape=None, dtype=None,
                                    initializer=None, regularizer=None,
                                    trainable=True,
                                    *args, **kwargs):
    """Custom variable getter that forces trainable variables to be stored in
    float32 precision and then casts them to the training precision.
    """
    storage_dtype = tf.float32 if trainable else dtype
    variable = getter(name, shape, dtype=storage_dtype,
                      initializer=initializer, regularizer=regularizer,
                      trainable=trainable,
                      *args, **kwargs)
    if trainable and dtype != tf.float32:
        variable = tf.cast(variable, dtype)
    return variable

def gradients_with_loss_scaling(loss, variables, loss_scale):
    """Gradient calculation with loss scaling to improve numerical stability
    when training with float16.
    """
    grad_scaled = []
    for grad in tf.gradients(loss * loss_scale, variables):
        grad = grad / loss_scale
        grad = tf.Print(grad, [tf.reduce_max(grad)], 'grad')
        grad_scaled.append(grad)
    return grad_scaled
    # return [grad / loss_scale for grad in tf.gradients(loss * loss_scale, variables)]

def serving_input_receiver_fn(float_type):
    """Build the serving inputs."""
    # The outer dimension (None) allows us to batch up inputs for
    # efficiency. However, it also means that if we want a prediction
    # for a single instance, we'll need to wrap it in an outer list.
    inputs = {"i_in": tf.placeholder(shape=[1, data_reader.image_size[0],
                                            data_reader.image_size[1],
                                            3],
                                     dtype=float_type),
              "masks": tf.placeholder(shape=[1, data_reader.image_size[0],
                                             data_reader.image_size[1],
                                             1],
                                      dtype=float_type),
              "edges": tf.placeholder(shape=[1, data_reader.image_size[0],
                                             data_reader.image_size[1],
                                             1],
                                      dtype=float_type)
              }
    return tf.estimator.export.ServingInputReceiver(inputs, inputs)

def model_fn(features, labels, mode, params):
    i_in = features['i_in']
    masks = features['masks']
    edges = features['edges']

    edges_label = edges  # * (1 - masks)

    i_out, i_edges = build_generator(params, i_in, masks, edges, mode)

    if mode == tf.estimator.ModeKeys.PREDICT:
        export_outputs = {'predict_output': tf.estimator.export.PredictOutput({"i_out": i_out})}
        predictions = {'i_out': i_out}
        return tf.estimator.EstimatorSpec(mode, predictions=predictions, export_outputs=export_outputs)

    # Compute loss.
    i_gt = features['i_gt']

    # edges_l1_loss = tf.reduce_mean(tf.abs(i_edges - edges_label))
    edges_l1_loss = tf.reduce_mean(
        tf.reduce_mean(tf.abs(i_edges - edges_label), axis=[1, 2, 3]) /
        (tf.reduce_mean(1 - masks, axis=[1, 2, 3]) + 0.00001))
    # images_l1_loss = tf.reduce_mean(
    #     tf.reduce_mean(tf.abs(i_out - i_gt), axis=[1, 2, 3]) /
    #     (tf.reduce_mean(1 - masks, axis=[1, 2, 3]) + 0.00001))
    images_l1_loss = tf.reduce_mean(tf.abs(i_out - i_gt))

    i_gt_gray = tf.image.rgb_to_grayscale(i_gt)

    discriminator_edges_data, discriminator_edges_data_features = \
        build_discriminator(edges_discriminator_name, tf.concat([i_gt_gray, edges, masks], axis=-1))
    discriminator_edges_z, discriminator_edges_z_features = \
        build_discriminator(edges_discriminator_name, tf.concat([i_gt_gray, i_edges, masks], axis=-1))

    loss_discriminator_edges, loss_generator_edges_adversarial = hinge_gan_loss(discriminator_edges_data,
                                                                                discriminator_edges_z)

    edges_comp = edges * masks + i_edges * (1 - masks)

    # Discriminator of real images
    discriminator_images_data, discriminator_images_data_features = \
        build_discriminator(images_discriminator_name, tf.concat([i_gt, edges_comp], axis=3))
    # Discriminator of generated images
    discriminator_images_z, discriminator_images_z_features = \
        build_discriminator(images_discriminator_name, tf.concat([i_out, edges_comp], axis=3))

    loss_discriminator_images, loss_generator_images_adversarial = hinge_gan_loss(discriminator_images_data,
                                                                                discriminator_images_z)
    # loss_discriminator_images, loss_generator_adversarial = ra_hinge_gan_loss(discriminator_images_data, discriminator_images_z)

    edges_feature_matching_loss = feature_matching_loss(discriminator_edges_data_features,
                                                        discriminator_edges_z_features)

    # images_feature_matching_loss = feature_matching_loss(discriminator_images_data_features,
    #                                                     discriminator_images_z_features)

    loss_generator = 0
    # loss_generator += edges_l1_loss + images_l1_loss
    loss_generator += params['gamma_adversarial'] * loss_generator_edges_adversarial + \
                      params['gamma_feature'] * edges_feature_matching_loss
    loss_generator += params['gamma_adversarial'] * loss_generator_images_adversarial + \
                      images_l1_loss

    # loss_generator = params['gamma_adversarial'] * (loss_generator_edges_adversarial) + \
    #                  params['gamma_feature'] * edges_feature_matching_loss

    loss = loss_generator

    tf.summary.scalar('loss_discriminator_edges', loss_discriminator_edges)
    tf.summary.scalar('loss_discriminator_images', loss_discriminator_images)
    tf.summary.scalar('loss_generator_edges_feature_matching', edges_feature_matching_loss)
    # tf.summary.scalar('loss_generator_images_feature_matching', images_feature_matching_loss)
    tf.summary.scalar('loss_generator_edges_l1', edges_l1_loss)
    tf.summary.scalar('loss_generator_images_l1', images_l1_loss)
    tf.summary.scalar('loss_generator_edges_adversarial', loss_generator_edges_adversarial)
    tf.summary.scalar('loss_generator_images_adversarial', loss_generator_images_adversarial)

    summary_image = tf.summary.image('image', tf.concat(
        [tf.concat([float2int(i_gt), float2int(i_out)], axis=2),
         tf.concat([float2int(tf.image.grayscale_to_rgb(edges)), float2int(tf.image.grayscale_to_rgb(i_edges))],
                   axis=2),
         tf.concat([float2int(i_in), float2int(i_out)], axis=2)],
        axis=1))

    if mode == tf.estimator.ModeKeys.EVAL:
        i_out = (i_out + 1) / 2.
        i_gt = (i_gt + 1) / 2.
        # Compute evaluation metrics.
        psnr = tf.image.psnr(i_out, i_gt, 1.0)
        mean_pnsr = tf.metrics.mean(psnr)

        ssim = tf.image.ssim(i_out, i_gt, 1.0)
        mean_ssim = tf.metrics.mean(ssim)

        mean_edges_l1_loss = tf.metrics.mean(edges_l1_loss)
        mean_images_l1_loss = tf.metrics.mean(images_l1_loss)
        mean_generator_edges_adversarial_loss = tf.metrics.mean(loss_generator_edges_adversarial)
        mean_generator_images_adversarial_loss = tf.metrics.mean(loss_generator_images_adversarial)
        mean_disriminator_edges_loss = tf.metrics.mean(loss_discriminator_edges)
        mean_disriminator_images_loss = tf.metrics.mean(loss_discriminator_images)

        metrics = {'psnr': mean_pnsr,
                   'ssim': mean_ssim,
                   'loss_discriminator_edges': mean_disriminator_edges_loss,
                   'loss_discriminator_images': mean_disriminator_images_loss,
                   'loss_generator_edges_l1': mean_edges_l1_loss,
                   'loss_generator_images_l1': mean_images_l1_loss,
                   'loss_generator_edges_adversarial': mean_generator_edges_adversarial_loss,
                   'loss_generator_images_adversarial': mean_generator_images_adversarial_loss}

        tf.summary.scalar('psnr', mean_pnsr[0])
        tf.summary.scalar('ssim', mean_ssim[0])

        if params['quantize']:
            tf.contrib.quantize.create_eval_graph()

        # summary_image_hook = tf.train.SummarySaverHook(save_steps=save_steps,
        #                                          output_dir='summary',
        #                                          summary_op=tf.summary.merge([summary_image]))

        return tf.estimator.EstimatorSpec(
            mode, loss=loss, eval_metric_ops=metrics)

    # Create training op.
    assert mode == tf.estimator.ModeKeys.TRAIN

    if params['quantize']:
        tf.contrib.quantize.create_training_graph(quant_delay=200000)

    global_step = tf.train.get_global_step()

    with tf.name_scope('learning_rate_scheduler'):
        lr_multiplier = tf.maximum(
            1.0 - tf.cast(
                tf.maximum(tf.constant(0, dtype=tf.int64),
                           global_step - params['num_steps_start_lr_decay']),
                dtype=tf.float32) /
            tf.cast(params['max_iter'] - params['num_steps_start_lr_decay'] + 1, dtype=tf.float32),
            0)
        learning_rate = params['learning_rate'] * lr_multiplier
    tf.summary.scalar('learning_rate', learning_rate)

    g_optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate, beta1=0.0, beta2=0.9)
    d_optimizer = g_optimizer

    g_vars = tf.trainable_variables('generator/')
    d_vars = tf.trainable_variables(edges_discriminator_name + '/')
    d_vars += tf.trainable_variables(images_discriminator_name + '/')

    with tf.control_dependencies(tf.get_collection(tf.GraphKeys.UPDATE_OPS)):
        if params['float_type'] == tf.float16:
            variables = tf.get_collection(tf.GraphKeys.TRAINABLE_VARIABLES)
            grads = gradients_with_loss_scaling(loss, variables, params['gradient_scale'])
            train_op = optimizer.apply_gradients(zip(grads, variables), global_step=global_step)
        else:
            train_op_d = d_optimizer.minimize(loss_discriminator_images + loss_discriminator_edges, var_list=d_vars,
                                              global_step=global_step)
            train_op_g = g_optimizer.minimize(loss_generator, var_list=g_vars,
                                              global_step=global_step)

    class TrainOpSelectorHook(tf.train.SessionRunHook):

        def begin(self):
            self.step = 0

        def before_run(self, run_context):
            print(self.step)
            if self.step % 2 == 0:
                # Train D
                print('D')
                return tf.train.SessionRunArgs({'global_step': global_step, 'train_op': train_op_d})
            else:
                # Train G
                print('G')
                return tf.train.SessionRunArgs({'global_step': global_step, 'train_op': train_op_g})

        def after_run(self, run_context, run_values):
            self.step = run_values.results['global_step']

    return tf.estimator.EstimatorSpec(mode, loss=loss, train_op=tf.no_op(),
                                      training_hooks=[TrainOpSelectorHook()])

def get_initializer():
    """He normal initializer.
    It draws samples from a truncated normal distribution centered on 0
    with `stddev = sqrt(2 / fan_in)`
    where `fan_in` is the number of input units in the weight tensor.
    Arguments:
        seed: A Python integer. Used to seed the random generator.
    Returns:
        An initializer.
    References:
        He et al., http://arxiv.org/abs/1502.01852
    """
    return tf.initializers.variance_scaling(scale=2., mode='fan_in', distribution='normal')

def hinge_gan_loss(discriminator_data, discriminator_z):
    loss_discriminator_data = tf.reduce_mean(tf.nn.relu(1 - discriminator_data))
    loss_discriminator_z = tf.reduce_mean(tf.nn.relu(1 + discriminator_z))
    loss_discriminator = (loss_discriminator_data + loss_discriminator_z)

    loss_generator_adversarial = -tf.reduce_mean(discriminator_z)
    return loss_discriminator, loss_generator_adversarial

def ra_hinge_gan_loss(discriminator_data, discriminator_z):
    # This is relativistic average hinge gan loss as in https://arxiv.org/pdf/1807.00734.pdf C.7 eq. 33, 34
    d_r = discriminator_data - tf.reduce_mean(discriminator_z, axis=0)
    d_z = discriminator_z - tf.reduce_mean(discriminator_data, axis=0)

    loss_discriminator_data = tf.reduce_mean(tf.nn.relu(1 - d_r))
    loss_discriminator_z = tf.reduce_mean(tf.nn.relu(1 + d_z))
    loss_discriminator = (loss_discriminator_data + loss_discriminator_z)  # / 2

    loss_generator_adversarial = (tf.reduce_mean(tf.nn.relu(1 - d_z)) + tf.reduce_mean(tf.nn.relu(1 + d_r)))  # / 2

    return loss_discriminator, loss_generator_adversarial

def feature_matching_loss(features_a, features_b):
    assert len(features_a) == len(features_b)

    losses = []

    for tensor_a, tensor_b in zip(features_a, features_b):
        losses.append(tf.reduce_mean(tf.abs(tensor_a - tensor_b)))

    return tf.add_n(losses)

def normalize(input_tensor, training):
    renorm_params = {'rmax': 1.5,
                     'rmin': 0.0,
                     'dmax': 0.5}
    # input_tensor = tf.cast(input_tensor, tf.float32)
    # normalized = tf.layers.batch_normalization(input_tensor, training=training, trainable=training, fused=False)
    # normalized = tf.cast(normalized, tf.float16)
    # return normalized
    # return input_tensor
    # return tf.layers.batch_normalization(input_tensor, training=training, trainable=training, fused=True, renorm=True,
    #                                      renorm_clipping=renorm_params)
    return tf.layers.batch_normalization(input_tensor, training=training, trainable=training, fused=True, renorm=True)
    # return tf.layers.batch_normalization(input_tensor, training=training, trainable=training, fused=True)
    # return group_normalization(input_tensor, 16)

def group_normalization(input_tensor, num_groups, gamma=1.0, beta=0.0, epsilon=1e-5):
    channels_int = input_tensor.get_shape().as_list()[3]
    while channels_int % num_groups != 0 and num_groups != 0:
        num_groups -= 1

    batch, height, width, channels = input_tensor.shape
    input_tensor = tf.reshape(input_tensor, shape=(batch, height, width, channels // num_groups, num_groups))
    mean, var = tf.nn.moments(input_tensor, [1, 2, 3], keep_dims=True)
    input_tensor = (input_tensor - mean) / tf.sqrt(var + epsilon)
    input_tensor = tf.reshape(input_tensor, [batch, height, width, channels])
    return input_tensor * gamma + beta

def gated_convolution(input_tensor, kernel_size, depth, stride, dilation_rate, activation=None):
    # assert padding in ['SYMMETRIC', 'SAME', 'REFELECT']
    # if padding == 'SYMMETRIC' or padding == 'REFELECT':
    #     p = int(rate * (ksize - 1) / 2)
    #     x = tf.pad(x, [[0, 0], [p, p], [p, p], [0, 0]], mode=padding)
    #     padding = 'VALID'

    # TODO: add mirror padding as in https://arxiv.org/pdf/1801.07892.pdf
    # TODO: in https://arxiv.org/pdf/1801.07892.pdf was used ELU instead of ReLU
    padding = 'SAME'
    # net = tf.layers.separable_conv2d(net, depth, kernel_size, strides=stride, padding=padding, use_bias=False)
    # input_tensor_shape = tf.shape(input_tensor)
    # input_tensor = AddCoords(x_dim=input_tensor_shape[2],
    #                          y_dim=input_tensor_shape[1],
    #                          with_r=True)(input_tensor)

    gating = tf.contrib.layers.conv2d(
        input_tensor,
        depth,
        kernel_size,
        stride=stride,
        padding=padding,
        rate=dilation_rate,
        activation_fn=tf.nn.sigmoid
    )
    features = tf.contrib.layers.conv2d(
        input_tensor,
        depth,
        kernel_size,
        stride=stride,
        padding=padding,
        rate=dilation_rate,
        activation_fn=activation,
        weights_initializer=tf.initializers.variance_scaling()
    )

    features = features * gating
    return features

def regular_convolution(input_tensor, kernel_size, depth, stride, dilation_rate, activation=None):
    # assert padding in ['SYMMETRIC', 'SAME', 'REFELECT']
    # if padding == 'SYMMETRIC' or padding == 'REFELECT':
    #     p = int(rate * (ksize - 1) / 2)
    #     x = tf.pad(x, [[0, 0], [p, p], [p, p], [0, 0]], mode=padding)
    #     padding = 'VALID'

    # TODO: add mirror padding as in https://arxiv.org/pdf/1801.07892.pdf
    # TODO: in https://arxiv.org/pdf/1801.07892.pdf was used ELU instead of ReLU
    padding = 'SAME'
    # net = tf.layers.separable_conv2d(net, depth, kernel_size, strides=stride, padding=padding, use_bias=False)

    # input_tensor_shape = tf.shape(input_tensor)
    # input_tensor = AddCoords(x_dim=input_tensor_shape[2],
    #                          y_dim=input_tensor_shape[1],
    #                          with_r=True)(input_tensor)

    features = tf.contrib.layers.conv2d(
        input_tensor,
        depth,
        kernel_size,
        stride=stride,
        padding=padding,
        rate=dilation_rate,
        activation_fn=activation,
        weights_initializer=tf.initializers.variance_scaling()
    )
    return features

# Taken from https://github.com/taki0112/Spectral_Normalization-Tensorflow
def spectral_norm(w, iteration=1):
    # Placing l2_norm here since we will not need it anywhere else
    def l2_norm(v, eps=1e-12):
        return v / (tf.reduce_sum(v ** 2) ** 0.5 + eps)

    w_shape = w.shape.as_list()
    w = tf.reshape(w, [-1, w_shape[-1]])

    u = tf.get_variable("u", [1, w_shape[-1]], initializer=tf.truncated_normal_initializer(), trainable=False)

    u_hat = u
    v_hat = None
    for i in range(iteration):
        v_ = tf.matmul(u_hat, tf.transpose(w))
        v_hat = l2_norm(v_)

        u_ = tf.matmul(v_hat, w)
        u_hat = l2_norm(u_)

    sigma = tf.matmul(tf.matmul(v_hat, w), tf.transpose(u_hat))
    w_norm = w / sigma

    with tf.control_dependencies([u.assign(u_hat)]):
        w_norm = tf.reshape(w_norm, w_shape)

    return w_norm

def sn_conv2d(inputs,
              filters,
              kernel_size,
              strides=1,
              padding='SAME',
              dilation_rate=1,
              use_bias=True,
              activation=None,
              kernel_initializer=None,
              name=None):
    with tf.variable_scope(name):
        w = tf.get_variable("kernel",
                            shape=[kernel_size, kernel_size, inputs.get_shape()[-1], filters],
                            initializer=kernel_initializer)

        conv = tf.nn.conv2d(input=inputs,
                            filter=spectral_norm(w),
                            padding=padding,
                            strides=[1, strides, strides, 1],
                            dilations=[1, dilation_rate, dilation_rate, 1])
        if use_bias:
            b = tf.get_variable("bias",
                                [filters],
                                initializer=tf.zeros_initializer)
            conv += b

        if activation is not None:
            conv = activation(conv)
    return conv

def upsample_x2(input_tensor):
    scale = 2
    shape = tf.shape(input_tensor)
    # return tf.image.resize_nearest_neighbor(input_tensor, [shape[1] * scale, shape[2] * scale])
    return tf.layers.conv2d_transpose(input_tensor,
                                      filters=input_tensor.get_shape().as_list()[-1],
                                      kernel_size=4,
                                      strides=2,
                                      padding='same')

def resize(x, scale=2., to_shape=None, align_corners=True, dynamic=False,
           func=tf.image.resize_bilinear, name='resize'):
    if dynamic:
        xs = tf.cast(tf.shape(x), tf.float32)
        new_xs = [tf.cast(xs[1] * scale, tf.int32),
                  tf.cast(xs[2] * scale, tf.int32)]
    else:
        xs = x.get_shape().as_list()
        new_xs = [int(xs[1] * scale), int(xs[2] * scale)]
    with tf.variable_scope(name):
        if to_shape is None:
            x = func(x, new_xs, align_corners=align_corners)
        else:
            x = func(x, [to_shape[0], to_shape[1]],
                     align_corners=align_corners)
    return x

def flow_to_image(flow):
    """Transfer flow map to image.
    Part of code forked from flownet.
    """
    out = []
    maxu = -999.
    maxv = -999.
    minu = 999.
    minv = 999.
    maxrad = -1
    for i in range(flow.shape[0]):
        u = flow[i, :, :, 0]
        v = flow[i, :, :, 1]
        idxunknow = (abs(u) > 1e7) | (abs(v) > 1e7)
        u[idxunknow] = 0
        v[idxunknow] = 0
        maxu = max(maxu, np.max(u))
        minu = min(minu, np.min(u))
        maxv = max(maxv, np.max(v))
        minv = min(minv, np.min(v))
        rad = np.sqrt(u ** 2 + v ** 2)
        maxrad = max(maxrad, np.max(rad))
        u = u / (maxrad + np.finfo(float).eps)
        v = v / (maxrad + np.finfo(float).eps)
        img = compute_color(u, v)
        out.append(img)
    return np.float32(np.uint8(out))

def flow_to_image_tf(flow, name='flow_to_image'):
    """Tensorflow ops for computing flow to image.
    """
    with tf.variable_scope(name), tf.device('/cpu:0'):
        img = tf.py_func(flow_to_image, [flow], tf.float32, stateful=False)
        img.set_shape(flow.get_shape().as_list()[0:-1] + [3])
        img = img / 127.5 - 1.
        return img

def highlight_flow(flow):
    """Convert flow into middlebury color code image.
    """
    out = []
    s = flow.shape
    for i in range(flow.shape[0]):
        img = np.ones((s[1], s[2], 3)) * 144.
        u = flow[i, :, :, 0]
        v = flow[i, :, :, 1]
        for h in range(s[1]):
            for w in range(s[1]):
                ui = u[h, w]
                vi = v[h, w]
                img[ui, vi, :] = 255.
        out.append(img)
    return np.float32(np.uint8(out))

def highlight_flow_tf(flow, name='flow_to_image'):
    """Tensorflow ops for highlight flow.
    """
    with tf.variable_scope(name), tf.device('/cpu:0'):
        img = tf.py_func(highlight_flow, [flow], tf.float32, stateful=False)
        img.set_shape(flow.get_shape().as_list()[0:-1] + [3])
        img = img / 127.5 - 1.
        return img

def contextual_attention(f, b, mask=None, ksize=3, stride=1, rate=1,
                         fuse_k=3, softmax_scale=10., training=True, fuse=True):
    """ Contextual attention layer implementation.
    Contextual attention is first introduced in publication:
        Generative Image Inpainting with Contextual Attention, Yu et al.
    Args:
        f: Input feature to match (foreground).
        b: Input feature for match (background).
        mask: Input mask for t, indicating patches not available.
        ksize: Kernel size for contextual attention.
        stride: Stride for extracting patches from t.
        rate: Dilation for matching.
        softmax_scale: Scaled softmax for attention.
        training: Indicating if current graph is training or inference.
    Returns:
        tf.Tensor: output
    """
    # get shapes
    raw_fs = tf.shape(f)
    raw_int_fs = f.get_shape().as_list()
    raw_int_bs = b.get_shape().as_list()
    # extract patches from background with stride and rate
    kernel = 2 * rate
    raw_w = tf.extract_image_patches(
        b, [1, kernel, kernel, 1], [1, rate * stride, rate * stride, 1], [1, 1, 1, 1], padding='SAME')
    raw_w = tf.reshape(raw_w, [raw_int_bs[0], -1, kernel, kernel, raw_int_bs[3]])
    raw_w = tf.transpose(raw_w, [0, 2, 3, 4, 1])  # transpose to b*k*k*c*hw
    # downscaling foreground option: downscaling both foreground and
    # background for matching and use original background for reconstruction.
    f = resize(f, scale=1. / rate, func=tf.image.resize_nearest_neighbor)
    b = resize(b, to_shape=[int(raw_int_bs[1] / rate), int(raw_int_bs[2] / rate)],
               func=tf.image.resize_nearest_neighbor)  # https://github.com/tensorflow/tensorflow/issues/11651
    if mask is not None:
        # mask = resize(mask, scale=1. / rate, func=tf.image.resize_nearest_neighbor
        mask = resize(mask, to_shape=[int(raw_int_bs[1] / rate), int(raw_int_bs[2] / rate)],
                      func=tf.image.resize_nearest_neighbor)  # https://github.com/tensor)
    fs = tf.shape(f)
    int_fs = f.get_shape().as_list()
    f_groups = tf.split(f, int_fs[0], axis=0)
    # from t(H*W*C) to w(b*k*k*c*h*w)
    bs = tf.shape(b)
    int_bs = b.get_shape().as_list()
    w = tf.extract_image_patches(
        b, [1, ksize, ksize, 1], [1, stride, stride, 1], [1, 1, 1, 1], padding='SAME')
    w = tf.reshape(w, [int_fs[0], -1, ksize, ksize, int_fs[3]])
    w = tf.transpose(w, [0, 2, 3, 4, 1])  # transpose to b*k*k*c*hw
    # process mask
    if mask is None:
        mask = tf.zeros([1, bs[1], bs[2], 1])
    m = tf.extract_image_patches(
        mask, [1, ksize, ksize, 1], [1, stride, stride, 1], [1, 1, 1, 1], padding='SAME')
    m = tf.reshape(m, [1, -1, ksize, ksize, 1])
    # m = tf.reshape(m, [int_fs, -1, ksize, ksize, 1])
    m = tf.transpose(m, [0, 2, 3, 4, 1])  # transpose to b*k*k*c*hw
    m = m[0]
    mm = tf.cast(tf.equal(tf.reduce_mean(m, axis=[0, 1, 2], keepdims=True), 0.), tf.float32)
    w_groups = tf.split(w, int_bs[0], axis=0)
    raw_w_groups = tf.split(raw_w, int_bs[0], axis=0)
    y = []
    offsets = []
    k = fuse_k
    scale = softmax_scale
    fuse_weight = tf.reshape(tf.eye(k), [k, k, 1, 1])
    for xi, wi, raw_wi in zip(f_groups, w_groups, raw_w_groups):
        # conv for compare
        wi = wi[0]
        wi_normed = wi / tf.maximum(tf.sqrt(tf.reduce_sum(tf.square(wi), axis=[0, 1, 2])), 1e-4)
        yi = tf.nn.conv2d(xi, wi_normed, strides=[1, 1, 1, 1], padding="SAME")

        # conv implementation for fuse scores to encourage large patches
        if fuse:
            yi = tf.reshape(yi, [1, fs[1] * fs[2], bs[1] * bs[2], 1])
            yi = tf.nn.conv2d(yi, fuse_weight, strides=[1, 1, 1, 1], padding='SAME')
            yi = tf.reshape(yi, [1, fs[1], fs[2], bs[1], bs[2]])
            yi = tf.transpose(yi, [0, 2, 1, 4, 3])
            yi = tf.reshape(yi, [1, fs[1] * fs[2], bs[1] * bs[2], 1])
            yi = tf.nn.conv2d(yi, fuse_weight, strides=[1, 1, 1, 1], padding='SAME')
            yi = tf.reshape(yi, [1, fs[2], fs[1], bs[2], bs[1]])
            yi = tf.transpose(yi, [0, 2, 1, 4, 3])
        yi = tf.reshape(yi, [1, fs[1], fs[2], bs[1] * bs[2]])

        # softmax to match
        yi = yi * mm  # mask
        yi = tf.nn.softmax(yi * scale, 3)
        yi *= mm  # mask

        offset = tf.argmax(yi, axis=3, output_type=tf.int32)
        offset = tf.stack([offset // fs[2], offset % fs[2]], axis=-1)
        # deconv for patch pasting
        # 3.1 paste center
        wi_center = raw_wi[0]
        yi = tf.nn.conv2d_transpose(yi, wi_center, tf.concat([[1], raw_fs[1:]], axis=0),
                                    strides=[1, rate, rate, 1]) / 4.
        y.append(yi)
        offsets.append(offset)
    y = tf.concat(y, axis=0)
    y.set_shape(raw_int_fs)
    offsets = tf.concat(offsets, axis=0)
    offsets.set_shape(int_bs[:3] + [2])
    # case1: visualize optical flow: minus current position
    h_add = tf.tile(tf.reshape(tf.range(bs[1]), [1, bs[1], 1, 1]), [bs[0], 1, bs[2], 1])
    w_add = tf.tile(tf.reshape(tf.range(bs[2]), [1, 1, bs[2], 1]), [bs[0], bs[1], 1, 1])
    offsets = offsets - tf.concat([h_add, w_add], axis=3)
    # to flow image
    flow = flow_to_image_tf(offsets)
    # # case2: visualize which pixels are attended
    # flow = highlight_flow_tf(offsets * tf.cast(mask, tf.int32))
    if rate != 1:
        flow = resize(flow, scale=rate, func=tf.image.resize_nearest_neighbor)
    return y, flow

def non_local_block_2d(input_tensor, mask, pairwise_func='gaussian', spatial_subsampling_factor=2,
                       embedding_depth_subsampling_factor=2):
    # pairwise_func: 'gaussian', 'embedded_gaussian', 'dot_product', 'concatenation'
    input_tensor_shape = tf.shape(input_tensor)
    input_tensor_depth = input_tensor.get_shape().as_list()[3]
    batch_size = input_tensor_shape[0]

    g = tf.layers.conv2d(input_tensor,
                         filters=input_tensor_depth // embedding_depth_subsampling_factor,
                         kernel_size=1,
                         use_bias=False)
    g = tf.reshape(g, [batch_size, -1, input_tensor_depth // embedding_depth_subsampling_factor])

    if pairwise_func == 'gaussian':
        x_i = tf.reshape(input_tensor, [batch_size, -1, input_tensor_depth])
        x_j = x_i
        f = tf.exp(tf.matmul(x_i, x_j, transpose_b=True))
        c = tf.reduce_sum(f, axis=[2])

    elif pairwise_func == 'embedded_gaussian':
        theta = tf.layers.conv2d(input_tensor,
                                 filters=input_tensor_depth // embedding_depth_subsampling_factor,
                                 kernel_size=1,
                                 use_bias=False)
        theta = tf.reshape(theta,
                           shape=[batch_size, -1, input_tensor_depth // embedding_depth_subsampling_factor])

        phi = tf.layers.conv2d(input_tensor,
                               filters=input_tensor_depth // embedding_depth_subsampling_factor,
                               kernel_size=1,
                               use_bias=False)
        phi = tf.reshape(phi, [batch_size, -1, input_tensor_depth // embedding_depth_subsampling_factor])
        phi = tf.transpose(phi, perm=[0, 2, 1])
        f = tf.matmul(theta, phi)

        # f = tf.exp(f)
        # c = tf.reduce_sum(f, axis=[2], keepdims=True)
        # f /= c
        f = tf.nn.softmax(f, axis=2)
        # f = tf.Print(f, [tf.reduce_max(f)], 'max f')

    elif pairwise_func == 'dot_product':
        theta = tf.layers.conv2d(input_tensor,
                                 filters=input_tensor_depth // embedding_depth_subsampling_factor,
                                 kernel_size=1,
                                 use_bias=False)
        theta = tf.reshape(theta,
                           shape=[batch_size, -1, input_tensor_depth // embedding_depth_subsampling_factor])

        phi = tf.layers.conv2d(input_tensor,
                               filters=input_tensor_depth // embedding_depth_subsampling_factor,
                               kernel_size=1,
                               use_bias=False)
        phi = tf.reshape(phi, [batch_size, -1, input_tensor_depth // embedding_depth_subsampling_factor])
        phi = tf.transpose(phi, perm=[0, 2, 1])
        f = tf.matmul(theta, phi)
        c = input_tensor_shape[1] * input_tensor_shape[2]
        c = tf.reshape(c, shape=[batch_size])
    elif pairwise_func == 'concatenation':
        # TODO: Finish implementation
        theta = tf.layers.conv2d(input_tensor,
                                 filters=input_tensor_depth // embedding_depth_subsampling_factor,
                                 kernel_size=1,
                                 use_bias=False)
        theta = tf.reshape(theta,
                           shape=[batch_size, -1, input_tensor_depth // embedding_depth_subsampling_factor])

        phi = tf.layers.conv2d(input_tensor,
                               filters=input_tensor_depth // embedding_depth_subsampling_factor,
                               kernel_size=1,
                               use_bias=False)
        phi = tf.reshape(phi, [batch_size, -1, input_tensor_depth // embedding_depth_subsampling_factor])
        concatenated = tf.concat([theta, phi], axis=2)
        # This is probaly wrong move coz we should have (w_f)T - transposed weight vector. Look for details in
        # Relation Networks for visual reasoning paper.
        f = tf.layers.conv1d(concatenated, 1, 1, use_bias=False, activation=tf.nn.relu)
        raise NotImplementedError('Concatenation is not implemented yet. Use other type of `pairwise_func`')
    else:
        raise ValueError('Wrong value of argument `pairwise_func`')

    y = tf.matmul(f, g)
    # y /= c
    # y = tf.Print(y, [tf.reduce_max(g)], 'max g')
    # y = tf.Print(y, [tf.reduce_max(y)], 'max normalized y')
    y = tf.reshape(y, shape=[batch_size,
                             input_tensor_shape[1],
                             input_tensor_shape[2],
                             input_tensor_depth // embedding_depth_subsampling_factor])

    y = tf.layers.conv2d(y,
                         filters=input_tensor_depth,
                         kernel_size=1,
                         use_bias=False,
                         kernel_initializer=tf.initializers.zeros)
    # for i in range(0, 20):
    #     one_channel_attention = f[:, :, i]
    #     one_channel_attention = tf.reshape(one_channel_attention,
    #                                        shape=[batch_size, input_tensor_shape[1], input_tensor_shape[2], 1])
    #     one_channel_attention -= tf.reduce_min(one_channel_attention, axis=[1, 2, 3])
    #     one_channel_attention /= tf.reduce_max(one_channel_attention, axis=[1, 2, 3])
    #     tf.summary.image('attention_channel_{}'.format(i), one_channel_attention)
    #
    # # y = tf.Print(y, [tf.shape(f)], 'shape of f')
    #
    # activation_summary(y)
    # activation_summary(tf.subtract(y, input_tensor, name='y_and_input_tensor_difference'))
    return y + input_tensor

# def custom_context_attenion(input_tensor, mask):
#     mask = tf.image.resize_images(mask, tf.shape(input_tensor)[1:3], method=tf.image.ResizeMethod.NEAREST_NEIGHBOR)
#     input_tensor_shape = tf.shape(input_tensor)
#     input_tensor_depth = input_tensor.get_shape().as_list()[3]
#     batch_size = input_tensor_shape[0]
#
#     input_tensor = tf.reshape(input_tensor, [batch_size, -1, input_tensor_depth])
#     mask = tf.reshape(mask, [batch_size, -1, 1])
#     mask = tf.transpose(mask, perm=[0, 2, 1])
#     f = tf.matmul(input_tensor, input_tensor, transpose_b=True)
#     f *= mask
#
#     # TODO: Необходимо транспонировать маску, т.к. она зауляет строки
#     # TODO: Заменить hard attention на softmax(f*10)
#     # TODO: Add 1-eye mat as mask to prevent self-attention
#     argmax = tf.argmax(f, axis=-1, output_type=tf.int32)
#     one_hot_mask = tf.one_hot(indices=argmax,
#                               depth=input_tensor_shape[1] * input_tensor_shape[2],
#                               axis=-1,
#                               dtype=input_tensor.dtype)
#     y = tf.matmul(one_hot_mask, input_tensor)
#     y = tf.reshape(y, shape=[batch_size,
#                              input_tensor_shape[1],
#                              input_tensor_shape[2],
#                              input_tensor_depth])
#     return y

def custom_context_attenion(input_tensor, mask):
    mask = tf.image.resize_images(mask, tf.shape(input_tensor)[1:3], method=tf.image.ResizeMethod.BILINEAR)
    input_tensor_shape = tf.shape(input_tensor)
    input_tensor_depth = input_tensor.get_shape().as_list()[3]
    batch_size = input_tensor_shape[0]

    inv_identity_mask = 1 - tf.eye(input_tensor.shape[1].value, input_tensor.shape[2].value,
                                   [input_tensor.shape[0].value])
    inv_identity_mask = tf.expand_dims(inv_identity_mask, axis=-1)
    mask *= inv_identity_mask
    input_tensor = tf.reshape(input_tensor, [batch_size, -1, input_tensor_depth])
    mask = tf.reshape(mask, [batch_size, -1, 1])
    f = tf.matmul(input_tensor, input_tensor, transpose_b=True)
    scores = tf.nn.softmax(f * mask * 10) * mask
    tf.summary.image('attention_scores', tf.expand_dims(scores, axis=3))
    tf.summary.image('attention_scores', tf.expand_dims(scores, axis=3))
    y = tf.matmul(scores, input_tensor)
    y = tf.reshape(y, shape=[batch_size,
                             input_tensor_shape[1],
                             input_tensor_shape[2],
                             input_tensor_depth])
    return y

def hw_flatten(x):
    return tf.reshape(x, shape=[x.shape[0], -1, x.shape[-1]])

# def custom_context_attenion2(input_tensor, depth):
#     batch_size, height, width, num_channels = x.get_shape().as_list()
#     f = conv(input_tensor, ch // 8, kernel=1, stride=1, sn=self.sn, scope='f_conv')  # [bs, h, w, c']
#     f = max_pooling(f)
#
#     g = conv(input_tensor, ch // 8, kernel=1, stride=1, sn=self.sn, scope='g_conv')  # [bs, h, w, c']
#
#     h = conv(input_tensor, ch // 2, kernel=1, stride=1, sn=self.sn, scope='h_conv')  # [bs, h, w, c]
#     h = max_pooling(h)
#
#     # N = h * w
#     s = tf.matmul(hw_flatten(g), hw_flatten(f), transpose_b=True)  # # [bs, N, N]
#
#     beta = tf.nn.softmax(s)  # attention map
#
#     o = tf.matmul(beta, hw_flatten(h))  # [bs, N, C]
#     gamma = tf.get_variable("gamma", [1], initializer=tf.constant_initializer(0.0))
#
#     o = tf.reshape(o, shape=[batch_size, height, width, num_channels // 2])  # [bs, h, w, C]
#     o = conv(o, ch, kernel=1, stride=1, sn=self.sn, scope='attn_conv')
#     x = gamma * o + input_tensor
#
#     return x

def custom_context_attenion2(input_tensor, depth):
    with tf.variable_scope(None, default_name='Attention'):
        batch_size, height, width, num_channels = input_tensor.get_shape().as_list()
        f = tf.layers.conv2d(input_tensor, depth, kernel_size=1, strides=1, name='f_conv')  # [bs, h, w, c']
        # f = max_pooling(f)

        g = tf.layers.conv2d(input_tensor, depth, kernel_size=1, strides=1, name='g_conv')  # [bs, h, w, c']

        h = tf.layers.conv2d(input_tensor, depth, kernel_size=1, strides=1, name='h_conv')  # [bs, h, w, c]
        # h = max_pooling(h)

        # N = h * w
        s = tf.matmul(hw_flatten(g), hw_flatten(f), transpose_b=True)  # # [bs, N, N]

        beta = tf.nn.softmax(s)  # attention map

        o = tf.matmul(beta, hw_flatten(h))  # [bs, N, C]
        o = tf.reshape(o, shape=[batch_size, height, width, num_channels])  # [bs, h, w, C]
        o = tf.layers.conv2d(o, depth, kernel_size=1, strides=1, name='attn_conv')
        gamma = tf.layers.conv2d(input_tensor, depth, kernel_size=1, strides=1, name='gamma_conv')  # [bs, h, w, c']
        gamma = tf.nn.sigmoid(gamma)
        tf.summary.scalar('max_gamma', tf.reduce_max(gamma))
        tf.summary.scalar('mean_gamma', tf.reduce_mean(gamma))
        x = o * gamma + input_tensor * (1 - gamma)
    return x

def custom_context_attenion3(input_tensor, depth):
    with tf.variable_scope(None, default_name='Attention'):
        batch_size, height, width, num_channels = input_tensor.get_shape().as_list()
        f = tf.layers.conv2d(input_tensor, depth, kernel_size=1, strides=1, name='f_conv')  # [bs, h, w, c']
        # f = max_pooling(f)

        g = tf.layers.conv2d(input_tensor, depth, kernel_size=1, strides=1, name='g_conv')  # [bs, h, w, c']

        h = tf.layers.conv2d(input_tensor, depth, kernel_size=1, strides=1, name='h_conv')  # [bs, h, w, c]
        # h = max_pooling(h)

        # N = h * w
        s = tf.matmul(hw_flatten(g), hw_flatten(f), transpose_b=True)  # # [bs, N, N]

        beta = tf.nn.softmax(s)  # attention map

        o = tf.matmul(beta, hw_flatten(h))  # [bs, N, C]
        o = tf.reshape(o, shape=[batch_size, height, width, num_channels])  # [bs, h, w, C]
        o = tf.layers.conv2d(o, depth, kernel_size=1, strides=1, name='attn_conv')
        gamma = tf.layers.conv2d(input_tensor, depth, kernel_size=1, strides=1, name='gamma_conv')  # [bs, h, w, c']
        gamma = tf.nn.sigmoid(gamma)
        tf.summary.scalar('max_gamma', tf.reduce_max(gamma))
        tf.summary.scalar('mean_gamma', tf.reduce_mean(gamma))
        x = o * gamma + input_tensor * (1 - gamma)
    return x

def c(inputs, filters, sn=False, inst_norm=True, activation=True):
    with tf.variable_scope(None, default_name='c'):
        ksize = 7
        rate = 1
        p = int(floor(rate * (ksize - 1))) // 2
        p2 = int(ceil(rate * (ksize - 1))) // 2
        x = tf.pad(inputs, [[0, 0], [p, p2], [p, p2], [0, 0]], mode='REFLECT')
        if sn:
            x = conv2d_spectral_norm(inputs=x, filters=filters, kernel_size=ksize, padding='valid')
        else:
            x = tf.layers.conv2d(inputs=x, filters=filters, kernel_size=ksize, padding='valid')

        if inst_norm:
            x = tf.contrib.layers.instance_norm(inputs=x, trainable=False)
        if activation:
            x = tf.nn.relu(x)
        return x

def d(inputs, filters, sn=False):
    with tf.variable_scope(None, default_name='d'):
        ksize = 4
        if sn:
            x = conv2d_spectral_norm(inputs=inputs, filters=filters, kernel_size=ksize, strides=2, padding='same')
        else:
            x = tf.layers.conv2d(inputs=inputs, filters=filters, kernel_size=ksize, strides=2, padding='same')
        x = tf.contrib.layers.instance_norm(inputs=x, trainable=False)
        x = tf.nn.relu(x)
        return x

def u(inputs, filters, sn=False):
    with tf.variable_scope(None, default_name='u'):
        ksize = 4
        x = tf.layers.conv2d_transpose(inputs=inputs, filters=filters, kernel_size=ksize, strides=2, padding='same')
        x = tf.contrib.layers.instance_norm(inputs=x, trainable=False)
        x = tf.nn.relu(x)
        return x

def r(inputs, filters, sn=False):
    with tf.variable_scope(None, default_name='r'):
        ksize = 3
        rate = 2
        p = int(floor(rate * (ksize - 1))) // 2
        p2 = int(ceil(rate * (ksize - 1))) // 2
        x = tf.pad(inputs, [[0, 0], [p, p2], [p, p2], [0, 0]], mode='REFLECT')
        if sn:
            x = conv2d_spectral_norm(inputs=x, filters=filters, kernel_size=ksize, dilation_rate=rate, padding='valid')
        else:
            x = tf.layers.conv2d(inputs=x, filters=filters, kernel_size=ksize, dilation_rate=rate, padding='valid')
        x = tf.contrib.layers.instance_norm(inputs=x, trainable=False)
        x = tf.nn.relu(x)

        ksize = 3
        rate = 1
        p = int(floor(rate * (ksize - 1))) // 2
        p2 = int(ceil(rate * (ksize - 1))) // 2
        x = tf.pad(x, [[0, 0], [p, p2], [p, p2], [0, 0]], mode='REFLECT')
        if sn:
            x = conv2d_spectral_norm(inputs=x, filters=filters, kernel_size=ksize, padding='valid')
        else:
            x = tf.layers.conv2d(inputs=x, filters=filters, kernel_size=ksize, padding='valid')
        x = tf.contrib.layers.instance_norm(inputs=x, trainable=False)
        x += inputs
        return x

def build_coarse_net(input_tensor):
    with tf.variable_scope('edge_net'):
        cnum = int(64 * 1.0)

        # Args are: input_tensor, kernel_size, depth, stride, dilation_rate, activation
        net = c(input_tensor, 64, sn=True)

        net = d(net, 128, sn=True)
        net = d(net, 256, sn=True)

        net = r(net, 256, sn=True)
        net = r(net, 256, sn=True)
        net = r(net, 256, sn=True)
        net = r(net, 256, sn=True)
        net = r(net, 256, sn=True)
        net = r(net, 256, sn=True)
        net = r(net, 256, sn=True)
        net = r(net, 256, sn=True)

        net = u(net, 128, sn=True)
        net = u(net, 64, sn=True)
        net = c(net, 1, sn=True, inst_norm=False, activation=False)
        net = tf.nn.sigmoid(net)

        # net = tf.clip_by_value(net, -1., 1.)
    return net

def build_refinement_net(input_tensor, mask):
    cnum = int(32 * 1.0)

    with tf.variable_scope('inpainting_net'):
        net = c(input_tensor, cnum)

        net = d(net, 128)
        net = d(net, 256)

        net = r(net, 256)
        net = r(net, 256)
        net = r(net, 256)
        net = r(net, 256)
        net = r(net, 256)
        net = r(net, 256)
        net = r(net, 256)
        net = r(net, 256)

        net = u(net, 128)
        net = u(net, 64)
        net = c(net, 3, inst_norm=False, activation=False)
        # net = tf.nn.tanh(net)
        net = tf.clip_by_value(net, -1., 1.)
        return net

def build_generator(params, images, masks, edges, mode):
    # Input size in https://arxiv.org/pdf/1801.07892.pdf is 256x256
    # training = True if mode == tf.estimator.ModeKeys.TRAIN else False

    with tf.variable_scope('generator'):
        images_gray = tf.image.rgb_to_grayscale(images)
        images_masks_edges = tf.concat([images_gray, masks, edges * masks], axis=3)
        edges_result = build_coarse_net(images_masks_edges)

        # edges_input = edges_result * (1 - masks) + edges * masks
        # edges_input = edges_result
        edges_input = edges
        edges_input = tf.stop_gradient(edges_input)

        # refinement_input_images = edges_result * (1 - masks) + images * masks
        refinement_input = tf.concat([images, masks, edges_input], axis=3)
        refined_result = build_refinement_net(refinement_input, masks)

        return refined_result, edges_result

def build_discriminator(name, input_tensor):
    with tf.variable_scope(name, reuse=tf.AUTO_REUSE):
        # Change to leaky relu
        activation = tf.nn.leaky_relu
        cnum = int(64 * 1.0)

        conv1 = conv2d_spectral_norm(input_tensor, cnum, 4, 1, 'SAME', activation=activation, name='conv1')
        conv2 = conv2d_spectral_norm(conv1, cnum * 2, 4, 2, 'SAME', activation=activation, name='conv2')
        conv3 = conv2d_spectral_norm(conv2, cnum * 4, 4, 2, 'SAME', activation=activation, name='conv3')
        conv4 = conv2d_spectral_norm(conv3, cnum * 8, 4, 2, 'SAME', activation=activation, name='conv4')
        conv5 = conv2d_spectral_norm(conv3, cnum, 4, 2, 'SAME', activation=activation, name='conv5')
        features = [conv1, conv2, conv3, conv4, conv5]
        return conv5, features


#!/usr/bin/env python3
"""
Author : schackartk
Date   : 2019-02-22
Purpose: Pull the first line of each poem within a directory
"""

import argparse
import sys
import os

# --------------------------------------------------
def get_args():
    """get command-line arguments"""
    parser = argparse.ArgumentParser(
        description='Get the first lines',
        formatter_class=argparse.ArgumentDefaultsHelpFormatter)

    parser.add_argument(
        'positional', metavar='DIR', nargs='+', help='A positional argument')

    parser.add_argument(
        '-w',
        '--width',
        type=int,
        metavar='int',
        help='Output line width',
        default=50)

    return parser.parse_args()

# --------------------------------------------------
def warn(msg):
    """Print a message to STDERR"""
    print(msg, file=sys.stderr)

# --------------------------------------------------
def die(msg='Something bad happened'):
    """warn() and exit with error"""
    warn(msg)
    sys.exit(1)

# --------------------------------------------------
def main():
    """Make a jazz noise here"""
    args = get_args()
    dirs = args.positional
    width = args.width

    out = {}
    for dir_n in dirs:
        if not os.path.isdir(dir_n):
            print('"{}" is not a directory'.format(dir_n), file=sys.stderr)
        else:
            print(dir_n)
            files = os.listdir(dir_n)
            for file_n in files:
                with open('{}/{}'.format(dir_n,file_n)) as f:
                    out[f.readline().strip()] = file_n
            for items in sorted (out):
                num_dots = width - len(items) - len(out[items])
                dots = '.' * num_dots
                print('{} {} {}'.format(items,dots,out[items]))
            out.clear()

# --------------------------------------------------
if __name__ == '__main__':
    main()


# SECTION
# NAME: PROLOGUE

import qiskit
from qiskit import QuantumCircuit, ClassicalRegister, QuantumRegister
from qiskit.circuit.library.standard_gates import *
from qiskit.circuit import Parameter
# SECTION
# NAME: CIRCUIT

qr = QuantumRegister(6, name='qr')
cr = ClassicalRegister(6, name='cr')
qc = QuantumCircuit(qr, cr, name='qc')
qc.append(ECRGate(), qargs=[qr[0], qr[1]], cargs=[])
qc.append(RXXGate(0.7226506013555898), qargs=[qr[1], qr[4]], cargs=[])

subcircuit = QuantumCircuit(qr, cr, name='subcircuit')
subcircuit.append(CCXGate(), qargs=[qr[0], qr[1], qr[5]], cargs=[])
subcircuit.append(C4XGate(), qargs=[qr[4], qr[0], qr[5], qr[2], qr[1]], cargs=[])
subcircuit.append(CYGate(), qargs=[qr[0], qr[2]], cargs=[])
subcircuit.append(CRZGate(2.7525044919718797), qargs=[qr[0], qr[3]], cargs=[])
subcircuit.append(RZZGate(0.6881227037382152), qargs=[qr[3], qr[4]], cargs=[])
subcircuit.append(RXXGate(2.605316968096909), qargs=[qr[3], qr[4]], cargs=[])
subcircuit.append(RZGate(1.032590781866799), qargs=[qr[0]], cargs=[])
subcircuit.append(CSwapGate(), qargs=[qr[3], qr[5], qr[2]], cargs=[])

qc.append(subcircuit, qargs=qr, cargs=cr)
qc.append(subcircuit.inverse(), qargs=qr, cargs=cr)
qc.append(YGate(), qargs=[qr[4]], cargs=[])
qc.append(RZXGate(2.9059964560129927), qargs=[qr[5], qr[4]], cargs=[])
qc.append(RCCXGate(), qargs=[qr[0], qr[2], qr[3]], cargs=[])
qc.append(RZXGate(1.7133393609362295), qargs=[qr[5], qr[3]], cargs=[])
qc.append(TdgGate(), qargs=[qr[2]], cargs=[])
qc.append(RCCXGate(), qargs=[qr[2], qr[0], qr[1]], cargs=[])
# SECTION
# NAME: MEASUREMENT

qc.measure(qr, cr)
# SECTION
# NAME: OPTIMIZATION_LEVEL

from qiskit import transpile
qc = transpile(qc, basis_gates=None, optimization_level=0, coupling_map=None)
# SECTION
# NAME: QASM_CONVERSION

qc = QuantumCircuit.from_qasm_str(qc.qasm())
# SECTION
# NAME: EXECUTION

from qiskit import Aer, transpile, execute
backend_470cddb0eb3a48219af00ff25a648093 = Aer.get_backend('qasm_simulator')
counts = execute(qc, backend=backend_470cddb0eb3a48219af00ff25a648093, shots=1385).result().get_counts(qc)
RESULT = counts


""" Create and init the GenesisTextDataloader class, which handles dataloading from ipfs
"""

# The MIT License (MIT)
# Copyright © 2021 Yuma Rao

# Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
# documentation files (the “Software”), to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
# and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

# The above copyright notice and this permission notice shall be included in all copies or substantial portions of
# the Software.

# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
# THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.

import argparse
import copy
from munch import Munch

import bittensor
from . import dataloader_impl

class dataloader:
    """ Create and init the GenesisTextDataloader class, which handles dataloading from ipfs
    """
    def __new__(
            cls,
            config: 'bittensor.config' = None,
            block_size: int = None,
            batch_size: int = None,
            max_corpus_size:int = None,
            num_workers: int = None,
            dataset: str=None
        ):
        if config == None:
            config = dataloader.config()
        config = copy.deepcopy( config )
        config.dataloader.block_size = block_size if block_size != None else config.dataloader.block_size
        config.dataloader.batch_size = batch_size if batch_size != None else config.dataloader.batch_size
        config.dataloader.max_corpus_size = max_corpus_size if max_corpus_size != None else config.dataloader.max_corpus_size
        config.dataloader.num_workers = num_workers if num_workers != None else config.dataloader.num_workers
        config.dataloader.dataset = dataset if dataset != None else config.dataloader.dataset
        dataloader.check_config( config )
        return dataloader_impl.GenesisTextDataloader(
            block_size = config.dataloader.block_size,
            batch_size = config.dataloader.batch_size,
            max_corpus_size = config.dataloader.max_corpus_size,
            num_workers = config.dataloader.num_workers,
            dataset = config.dataloader.dataset,
            data_dir = config.dataloader.data_dir
        )

    @classmethod
    def config(cls) -> 'bittensor.Config':
        """ Get config from the argument parser
            Return: bittensor.config object
        """
        parser = argparse.ArgumentParser()
        dataloader.add_args( parser )
        return bittensor.config( parser )

    @classmethod
    def add_args(cls, parser: argparse.ArgumentParser ):
        """ Accept specific arguments from parser
        """
        try:
            parser.add_argument('--dataloader.batch_size', default=10, type=int, help='Batch size.')
            parser.add_argument('--dataloader.block_size', default=20, type=int, help='Number of text items to pull for each example..')
            parser.add_argument('--dataloader.max_corpus_size', default=1e+6, type=int, help='Maximum amount of data to download from IPFS into memory for training.')
            parser.add_argument('--dataloader.num_workers', default=0, type=int, help='Number of workers for data loader.')
            parser.add_argument('--dataloader.dataset', default='train', type=str, help='Which datasets to use (genesis or wikitext)).')
            parser.add_argument('--dataloader.data_dir', default='~/.bittensor/data/', type=str, help='Where to save and load the data.')
        except argparse.ArgumentError:
            # re-parsing arguments.
            pass

    @classmethod
    def check_config( cls, config: 'bittensor.Config' ):
        """ Check config for batch size, block size, corpus size, num_workers and dataset
        """
        assert config.dataloader.batch_size > 0, 'Batch size must be larger than 0'
        assert config.dataloader.block_size > 0, 'Block size must be larger than 0'
        assert config.dataloader.max_corpus_size > 0, 'max_corpus_size must be larger than 0'
        assert config.dataloader.num_workers >= 0, 'num_workers must be equal to or larger than 0'
        assert config.dataloader.dataset in ['train','test','validation'], 'dataset must be one of the following choices: genesis, wikitext, test, or validation'


'''
Un documento HTML può essere rappresentato sotto forma di albero, come visto a lezione, che si chiama DOM (Document Object Model).

Un qualsiasi nodo di questo albero può essere individuato sulla base delle proprie caratteristiche:
    - tag                       un tag del tipo indicato
    - .classe                   una delle parole presenti nel valore dell'attributo "class"
    - #id                       il valore dell'attributo "id"
    - @[attributo="valore"]     valore di un attributo generico
ed in base alla sua relazione con i tag che lo contengono:
    - avo discendente           il tag 'avo' contiene un tag 'discendente' a qualsiasi profondità
    - padre > figlio            il tag 'padre' contiene il tag 'figlio' nel livello immediatamente sotto

Un selettore CSS è una successione di selettori di tag separati da spazio che serve ad individuare uno o più nodi all'interno del DOM.
Esempio:
    div .class1 > #main_window
        seleziona un qualsiasi tag che ha                                       id="main_window"
        è il figlio di un tag che ha                                            class="... class1 ..."
        e si trova all'interno (a qualsiasi livello di distanza) di un tag      div
Esempio2:
    p  table > tr > td > a
	seleziona un link (<a ...> </a>)
	figlio di una casella (<td> ... </td>)
	figlia di una riga (<tr> ... </tr>)
	figlia di una tabella (<table> ... </table>)
	contenuta a qualsiasi livello in un paragrafo (<p> .... </p>)

NOTA: questa definizione del CSS è una versione ridottissima che non segue lo standard completo.
In particolare, non è possibile usare più relazioni '>' consecutive o costruire selettori alternativi (in OR) e selettori in AND.

Le modifiche da attuare su un DOM possono essere di 3 tipi:
    - conteggio dei nodi che soddisfano il selettore CSS
    - eliminazione di tutti i tag individuati da un selettore CSS
    - modifica degli attributi di tutti i tag individuati da un selettore CSS

Realizzate le funzioni che esplorano e modificano l'albero:
    conta_nodi(       fileIn, selettore)
    elimina_nodi(       fileIn, selettore, fileOut)
    cambia_attributo(	fileIn, selettore, chiave, valore, fileOut)

ATTENZIONE: nei test verrà utilizzato un timout globale di 1*N secondi (se il grader fa N test)
'''

from  my_html import HTMLNode, fparse, _MyHTMLParser

def parse(html):
    parser = _MyHTMLParser()
    parser.feed(html)
    return parser.root

def fparse(fhtml):
    with open(fhtml) as f:
        root = parse(f.read())
    return root

def find_by_tag(node, tag):
    ret = []
    if node.tag == tag: ret += [node]
    if not node.istext():
        for child in node.content:
            ret += find_by_tag(child,tag)

    return ret

def riconosci_info(selettore):
    if not selettore.isalnum():
        if selettore[:1]=='#':
            seleinfo=str('id="')+selettore[1:]+str('"')
        elif selettore[:1]=='.':
            seleinfo=str('class="')+selettore[1:]+str('"')
    else:
        seleinfo=str('<')+selettore
    return seleinfo

def conta_nodi(fileIn, selettore):
    cont=0
    doc = fparse(fileIn)
    node=fparse(fileIn)
    if not selettore.isalnum():
        if len(selettore.split(' '))!=1:
            indice=1
            if selettore.split(' ')[indice]=='>':
                indice=2
            seleinfo=riconosci_info(selettore.split(' ')[indice])
            for node in find_by_tag(doc,selettore[0]):
                parole=node.to_string().split(' ')
                for sez in parole:
                    if seleinfo in sez:
                        cont+=1
        else:
            seleinfo=riconosci_info(selettore)

            parole=doc.to_string().split(' ')
            for sez in parole:
                if seleinfo in sez:
                    cont+=1
    else:
        for node in find_by_tag(doc,selettore):
            cont+=1
    #print(cont)
    return cont
#conta_nodi('page1-3.html', 'p a')

def remove_by_tag(node, tag):
    if node.istext(): return
    for child in node.content:
        remove_by_tag(child,tag)
    newcont = []
    for child in node.content:
        if child.tag == tag:
            if not child.istext():
                newcont += child.content

        else:
            newcont += [child]
    node.content = newcont

def elimina_nodi(fileIn, selettore, fileOut):
    doc=fparse(fileIn)
    if ' ' in selettore:
        selettore=selettore.split(' ')[-1:]
    remove_by_tag(doc,selettore)
    f = open(fileOut,'w')
    f.write(doc.to_string())
    f.close()
    return doc.to_string()
#elimina_nodi('page1-3.html','p > em','prova.html')
def cambia_attributo(fileIn, selettore, chiave, valore, fileOut):
    '''Modifica tutti i nodi dell'albero che soddisfano il selettore CSS'''
    #Inserite qui il vostro codice


import pickle

class persistenceutils:
    def __init__(self):
        pass

    def write_pickle(self):
        db = {}
        for data in customer:
            db['customerdb'] = data
            dbfile = open('custdata', 'ab')
            pickle.dump(db, dbfile)
            dbfile.close()

        db = {}
        for data in account:
            db['accountdb'] = data
            dbfile = open('accountdata', 'ab')
            pickle.dump(db, dbfile)
            dbfile.close()

    def load_pickle(self):
        dbfile = open('bankdata', 'rb')
        db = pickle.load(dbfile)
        print('this is load data')
        print(dbfile)
        for keys in db:
            print(keys, '=>', db[keys])
            dbfile.close()

class person:

    def __init__(self, custid, first_name, last_name):
        self.custid = custid
        self.fname = first_name
        self.lname = last_name

class account:

    def __init__(self, number, actype, owner, balance):
        self.number = number
        self.type = actype
        self.owner = owner
        self.balance = balance

class bank:
    def __init__(self):
        pass

    def add_customer(self, newcust):
        #        print(newcust.custid,newcust.fname,newcust.lname)
        customer.update({newcust.custid: (newcust.fname, newcust.lname)})

    def add_account(self, newaccount):
        #        print(newaccount.number,newaccount.type,newaccount.owner.custid,newaccount.owner.fname, newaccount.owner.lname,newaccount.balance)
        accounts.update({newaccount.number: (newaccount.type, newaccount.owner, newaccount.balance)})

    def delete_account(self, delaccount):
        del accounts[delaccount]

    def deposit_money(self, account, amount):
        newbalance = accounts[account][2] + amount
        l1 = list(accounts[account])
        l1[2] = newbalance
        accounts[account] = tuple(l1)
        # print(accounts[account])

    def withdraw_money(self, account, amount):
        newbalance = accounts[account][2] - amount
        if newbalance > 0:
            l1 = list(accounts[account])
            l1[2] = newbalance
            accounts[account] = tuple(l1)
        else:
            print('Insufficient balance\n')
        print('Your new balance = ', accounts[account][2])

    def balance_inquiry(self, account):
        print("Your balance is - ", accounts[account][2])

    def save_data(self):
        pass

    def load_data(self):
        pass

# initial declaration
customer = {}  # Create empty customer dictionary
accounts = {}  # Create empty accounts dictionary
transact_bank = bank()  # Create an object of Class Bank

# add customer
customer1 = person(1, 'preeti', 'sehgal')
transact_bank.add_customer(customer1)

# add account
account1 = account(1000, 'savings', customer1, 5000)
transact_bank.add_account(account1)

account2 = account(1001, 'savings', customer1, 5000)
transact_bank.add_account(account2)

# remove account
transact_bank.delete_account(account2.number)

# deposit money
transact_bank.deposit_money(account1.number, 3000)

# withdraw money
transact_bank.withdraw_money(account1.number, 2000)

# balance inquiry
transact_bank.balance_inquiry(account1.number)

# Instantiate class for poersisting data
p = persistenceutils
# download data
p.write_pickle(self)

# load data
p.load_data()


# -*- coding: utf-8 -*-

import os
import sys
import codecs

data_dir = '/Users/katinska/awd-lstm-lm/data'

if __name__ == '__main__':
    data_file = codecs.open(os.path.join(data_dir, 'tmp_tagged.txt'), 'r', encoding='utf-8')
    for line in data_file:
        print(line.strip())
        new_line = []
        line = line.split('\t')
        token, tag, lemma = line
        if tag == ',':
            tag = 'None'
        elif tag == '-':
            tag = 'None'
        if lemma == '<unknown>':
            lemma = '<unk>'


"""
You are climbing a staircase. It takes n steps to reach the top.

Each time you can either climb 1 or 2 steps. In how many distinct ways can you climb to the top?
"""
from itertools import combinations
from math import factorial

def climbStairs(n):
    count=0
    for i in range(n,1,-2):
        count += factorial(i+(n-i)//2) / factorial(i) / factorial((n-i)//2)
    return int(count)

print(climbStairs(3))


import sys
#
# >>> Escriba el codigo del mapper a partir de este punto <<<
#
lineas = sys.stdin.readlines()

for linea in lineas:

	if linea not in [None, ' ', '\n', '\t']:

		columnas = linea.split("\t")

		claves = tokenize(columnas[1])
		valor = columnas[0]

		sys.stdout.write("{},{}\n".format(claves,valor))


from indicators.IndicatorBase import IndicatorBase
from math import ceil
class MarketDepthIndictor(IndicatorBase):

    default_settings = {
        "update_type": "market_depth", # "prices" or "news". If news, it won't get prices updates
        "k": 3  # value of k in k-EMA
    }

    def __init__(self):
        self.ask_depth = {}
        self.bid_depth = {}
        self.is_first = True
        self.max_buy = 0
        self.min_sell = 0

    def update(self, update):
        if self.is_first:
            self.ask_depth = update.ask_depth
            self.bid_depth = update.bid_depth
        else:
            for price in update.ask_depth_diff:
                if price not in self.ask_depth:
                    self.ask_depth[price] = 0
                self.ask_depth[price] += update.ask_depth_diff[price]
                if self.ask_depth[price] < 0:
                    del self.ask_depth[price]

                if self.min_sell > self.ask_depth[price]:
                    self.min_sell = self.ask_depth[price]

            for price in update.bid_depth_diff:
                if price not in self.bid_depth:
                    self.bid_depth[price] = 0
                self.bid_depth[price] += update.bid_depth_diff[price]
                if self.bid_depth[price] < 0:
                    del self.bid_depth[price]

                if self.max_buy < self.bid_depth[price]:
                    self.max_buy = self.bid_depth[price]

        self.is_first = False


import numpy as np

def match_bboxes(pred_scores, pred_boxes, pred_labels, gt_boxes, gt_labels, matching_threshold):
    rm_threshold = 1e-4
    idxes = np.where(pred_scores >= rm_threshold)[0]
    scores = pred_scores[idxes]
    boxes = pred_boxes[idxes]
    labels = pred_labels[idxes]

    pred_boxes_num = boxes.shape[0]
    real_boxes_num = gt_boxes.shape[0]
    if pred_boxes_num == 0:
        return [0, 0, real_boxes_num]

    xmin = gt_boxes[:,0]
    ymin = gt_boxes[:,1]
    xmax = gt_boxes[:,2]
    ymax = gt_boxes[:,3]
    area = (xmax - xmin) * (ymax - ymin)

    true_positive = 0
    true_boxes = np.zeros((real_boxes_num), dtype=np.bool)
    for i in range(pred_boxes_num):
        score = scores[i]
        box = boxes[i]
        label = labels[i]

        int_xmin = np.maximum(xmin, box[0])
        int_ymin = np.maximum(ymin, box[1])
        int_xmax = np.minimum(xmax, box[2])
        int_ymax = np.minimum(ymax, box[3])

        h = np.maximum(int_ymax - int_ymin, 0.)
        w = np.maximum(int_xmax - int_xmin, 0.)
        # Volumes.
        inter_vol = h * w
        union_vol = area - inter_vol \
            + (box[2] - box[0]) * (box[3] - box[1])
        jaccard = inter_vol / union_vol

        jaccard = np.where(label == gt_labels, jaccard, 0.0)

        idxmax = np.argmax(jaccard, axis=0)
        jcdmax = jaccard[idxmax]

        match = jcdmax > matching_threshold
        existing_match = true_boxes[idxmax]
        # TP: match & no previous match and FP: previous match | no match.
        # If difficult: no record, i.e FP=False and TP=False.
        tp = np.logical_and(match, np.logical_not(existing_match))
        if tp:
            true_positive += 1
            true_boxes[idxmax] = True

    return [true_positive, pred_boxes_num, real_boxes_num]

def match_bboxes_single_label(pred_label, pred_scores, pred_boxes, gt_boxes, gt_labels, gt_difficults, matching_threshold):
    rm_threshold = 1e-5
    idxes = np.where(pred_scores >= rm_threshold)[0]
    scores = pred_scores[idxes]
    boxes = pred_boxes[idxes]

    pred_boxes_num = boxes.shape[0]
    real_boxes_num = gt_boxes.shape[0]

    n_gbboxes = np.count_nonzero(np.logical_and(np.equal(gt_labels, pred_label),
                                                    np.logical_not(gt_difficults)))

    xmin = gt_boxes[:,0]
    ymin = gt_boxes[:,1]
    xmax = gt_boxes[:,2]
    ymax = gt_boxes[:,3]
    area = (xmax - xmin) * (ymax - ymin)

    gmatch = np.zeros((real_boxes_num), dtype=np.bool)
    ta_tp = np.zeros((pred_boxes_num), dtype=np.bool)
    ta_fp = np.zeros((pred_boxes_num), dtype=np.bool)
    for i in range(pred_boxes_num):
        pred_box = boxes[i]

        int_xmin = np.maximum(xmin, pred_box[0])
        int_ymin = np.maximum(ymin, pred_box[1])
        int_xmax = np.minimum(xmax, pred_box[2])
        int_ymax = np.minimum(ymax, pred_box[3])

        h = np.maximum(int_ymax - int_ymin, 0.)
        w = np.maximum(int_xmax - int_xmin, 0.)
        # Volumes.
        inter_vol = h * w
        union_vol = area - inter_vol \
            + (pred_box[2] - pred_box[0]) * (pred_box[3] - pred_box[1])
        jaccard = inter_vol / union_vol

        jaccard = np.where(pred_label == gt_labels, jaccard, 0.0)

        idxmax = np.argmax(jaccard, axis=0)
        jcdmax = jaccard[idxmax]

        match = jcdmax > matching_threshold
        existing_match = gmatch[idxmax]
        not_difficult = np.logical_not(gt_difficults[idxmax])
        # TP: match & no previous match and FP: previous match | no match.
        # If difficult: no record, i.e FP=False and TP=False.
        tp = np.logical_and(not_difficult,
                            np.logical_and(match, np.logical_not(existing_match)))
        ta_tp[i] = tp
        fp = np.logical_and(not_difficult,
                            np.logical_or(existing_match, np.logical_not(match)))

        ta_fp[i] = fp
        # Update grountruth match.
        if not_difficult and match:
            gmatch[idxmax] = True
    return [scores, n_gbboxes, ta_tp, ta_fp]


from django.db import models
from django.core.exceptions import ValidationError
from django.db.models.enums import TextChoices
from django.db.models.signals import pre_save

class Caja(models.Model):

    codigo = models.CharField('Identificador', max_length= 4, unique=True)
    saldo_disponible = models.DecimalField('Saldo disponible en pesos', decimal_places=2, max_digits=9)
    saldo_disponible_dolares = models.DecimalField('Saldo disponible en dolares', decimal_places=2, max_digits=9)
    saldo_disponible_euros = models.DecimalField('Saldo disponible en euros', decimal_places=2, max_digits=9)
    egresos = models.DecimalField('Egresos', decimal_places=2, max_digits=9)
    ingresos_en_pesos = models.DecimalField('Ingresos en pesos', decimal_places=2, max_digits=9)
    ingresos_en_dolares = models.DecimalField('Ingresos en dolares', decimal_places=2, max_digits=9)
    ingresos_en_euros = models.DecimalField('Ingresos en euros', decimal_places=2, max_digits=9)
    saldo_inicial = models.DecimalField('Saldo Inicial', decimal_places=2, max_digits=9)
    saldo_final = models.DecimalField('Saldo Final', decimal_places=2, max_digits=9)
    sucursal_id = models.ForeignKey('Sucursal', on_delete=models.PROTECT, null=True)

    def clean(self):

        cajas = Caja.objects.all()

        for caja in cajas:
            if self.codigo == caja.codigo and self.id != caja.id:
                raise ValidationError('Ya existe una caja con el identificador ingresado.')

        if self.sucursal_id.estado.opciones == 'INACTIVA':
            raise ValidationError('No puedes registrar una caja a una sucursal inactiva.')

        if not self.codigo.isalnum():
            raise ValidationError('El identificador solo puede contener letras y números.')
        if len(self.codigo) < 2 or len(self.codigo) > 4:
            raise ValidationError('El indentificador debe tener entre 2 y 4 caracteres.')
        if self.saldo_disponible == None or self.saldo_disponible < 0 :
            raise ValidationError('El saldo disponible debe ser un número positivo, con un máximo de 7 cifras.')
        if self.saldo_disponible_dolares == None or self.saldo_disponible_dolares < 0:
            raise ValidationError('El saldo disponible en dolares debe ser un número positivo, con un máximo de 7 cifras.')
        if self.saldo_disponible_euros == None or self.saldo_disponible_euros < 0:
            raise ValidationError('El saldo disponible en euros debe ser un número positivo, con un máximo de 7 cifras.')
        if self.egresos == None or self.egresos < 0 :
            raise ValidationError('Los egresos deben ser un número positivo, con un máximo de 7 cifras.')
        if self.ingresos_en_pesos == None or self.ingresos_en_pesos < 0:
            raise ValidationError('Los ingresos en pesos deben ser un número positivo, con un máximo de 7 cifras.')
        if self.ingresos_en_dolares == None or self.ingresos_en_dolares < 0:
            raise ValidationError('Los ingresos en dolares deben ser un número positivo, con un máximo de 7 cifras.')
        if self.ingresos_en_euros == None or self.ingresos_en_euros < 0:
            raise ValidationError('Los ingresos en euros deben ser un número positivo, con un máximo de 7 cifras.')
        if self.saldo_inicial == None or self.saldo_inicial < 0 :
            raise ValidationError('El saldo inicial debe ser un número positivo, con un máximo de 7 cifras.')
        if self.saldo_final == None or self.saldo_final < 0:
            raise ValidationError('El saldo final debe ser un número positivo, con un máximo de 7 cifras.')

    class Meta:

        verbose_name = 'caja'
        verbose_name_plural = 'cajas'

    def __str__(self):
       return self.codigo

class EstadoSucursal(models.Model):

    class opcionesEstado(TextChoices):

        ACTIVA = 'ACTIVA'
        INACTIVA = 'INACTIVA'

    opciones = models.CharField(choices = opcionesEstado.choices, max_length=8, default= 'ACTIVA')

    def __str__(self):
        return self.opciones

class Sucursal (models.Model):

    codigo = models.CharField(max_length = 4, unique=True)
    idCasaCentral = models.IntegerField(default= 1)
    estado = models.ForeignKey(EstadoSucursal, on_delete=models.PROTECT, null=True)
    calle = models.CharField('Calle', max_length=20)
    numero = models.CharField('Numero',  max_length=4)
    localidad = models.CharField('Localidad', max_length=20, null=True)
    provincia = models.CharField('Provincia', max_length= 20, null=True)
    cod_postal = models.CharField('Código postal', max_length=4)

    def clean(self):

        for char in self.localidad:

            if not char.isalpha() and char != " ":
                raise ValidationError('La localidad solo puede tener letras y espacios.')

        for char in self.provincia:

            if not char.isalpha() and char != " ":
                raise ValidationError('La provincia solo puede tener letras y espacios.')

        for char in self.cod_postal:

            if char.isalpha():
                raise ValidationError("El código postal solo puede tener digitos.")

        if not self.codigo.isalnum():
            raise ValidationError('El código de la sucursal solo puede contener letras y números')
        if len(self.codigo) < 2 or len(self.codigo) > 4:
            raise ValidationError('El código de la sucursal debe tener entre 2 y 4 caracteres')

        if len(self.calle) < 4 or len(self.calle) > 20:
            raise ValidationError('La calle debe tener entre 4 y 20 letras')

        if not self.numero.isdigit():
            raise ValidationError('El número solo puede contener digitos.')

        if len(self.numero) < 2 or len(self.numero) > 4:
            raise ValidationError('El número debe tener entre 1 y 4 digitos.')

        if len(self.localidad) < 4 or len(self.localidad)  > 20:
            raise ValidationError('La calle debe tener entre 4 y 20 letras')

        if len(self.provincia) < 4 or len(self.provincia)> 20:
            raise ValidationError('La provincia debe tener entre 4 y 20 letras')

        if not self.cod_postal.isdigit():
            raise ValidationError('El código postal solo puede contener digitos.')

        if len(self.cod_postal) < 1 or len(self.cod_postal) > 4:
            raise ValidationError('El código postal debe tener entre 1 y 4 digitos.')

    class Meta:

        verbose_name = 'sucursal'
        verbose_name_plural = 'sucursales'

    def __str__(self):
        return self.codigo

class Operacion(models.Model):

    fecha = models.DateTimeField('Fecha', auto_now_add=True)
    monto = models.CharField('Monto', max_length=40)
    tipo = models.CharField('Tipo', max_length=10)
    caja_asociada = models.ForeignKey(Caja, on_delete=models.PROTECT)
    identificador = models.CharField('Identificador', max_length= 50)
    responsable = models.IntegerField('ID de responsable')

    class Meta:

        verbose_name = 'operacion'
        verbose_name_plural = 'operaciones'

    def __str__(self):
        return "Identificador: {}, Monto: {}, Tipo: {}".format(self.identificador, self.monto, self.tipo)

def defaultActivoSucursal(sender, instance, **kwargs):

    estados = EstadoSucursal.objects.all()
    if len(estados) > 0:
        if instance.estado_id == None:

            estadosQuery = EstadoSucursal.objects.filter(opciones = 'ACTIVA')
            activo = ""
            for estado in estadosQuery:
                activo = estado.id

            instance.estado_id = activo

pre_save.connect(defaultActivoSucursal, sender = Sucursal)


#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# author: bigfoolliu

"""
基于pygame模块做的贪吃蛇小游戏
"""
import pygame
import numpy as np
import time
from pygame.locals import *

BOARD_WIDTH = 48
BOARD_HEIGHT = 28
score = 0

class Food(object):
    """食物"""

    def __init__(self):
        self.item = (4, 5)  # 初始位置
        self.color = 255, 0, 255  # 颜色
        self.radius = 10  # 圆点直径
        self.width = 10

    def _draw(self, screen, i, j):
        """画出食物"""
        position = 10 + 20 * i, 10 + 20 * j
        pygame.draw.circle(screen, self.color, position, self.radius, self.width)

    def update(self, screen, enlarge, snake):
        """随机产生食物"""
        if enlarge:
            self.item = np.random.randint(1, BOARD_WIDTH - 2), np.random.randint(1, BOARD_HEIGHT - 2)
            while self.item in snake.item:
                self.item = np.random.randint(1, BOARD_WIDTH - 2), np.random.randint(1, BOARD_HEIGHT - 2)
        self._draw(screen, self.item[0], self.item[1])

class Snake(object):

    def __init__(self):
        # self.item = [[3, 25], [2, 25], [1, 25], [1, 24]]  # 初始长度以及样子
        self.item = [[3, 25], [2, 25]]  # 初始长度以及样子
        self.x = 0
        self.y = -1
        self.speed = 0.5  # 蛇的速度

    def move(self, enlarge):
        """移动"""
        if not enlarge:
            self.item.pop()

        head = [self.item[0][0] + self.x, self.item[0][1] + self.y]
        self.item.insert(0, head)

    def eat(self, food):
        """吃到食物"""
        global score
        snake_x, snake_y = self.item[0]  # snake头的x和y的坐标
        food_x, food_y = food.item  # food的x和y坐标
        if (food_x == snake_x) and (food_y == snake_y):
            score += 1
            return 1
        else:
            return 0

    def toward(self, x, y):
        """改变蛇的朝向"""
        if self.x * x >= 0 and self.y * y >= 0:
            self.x = x
            self.y = y

    def get_head(self):
        """获取蛇头坐标"""
        return self.item[0]

    def draw(self, screen):
        """画出蛇"""
        radius = 15
        width = 15
        _color = 255, 0, 0
        position = 10 + 20 * self.item[0][0], 10 + 20 * self.item[0][1]
        pygame.draw.circle(screen, _color, position, radius, width)  # 画蛇头

        radius = 10
        width = 10
        _color = 255, 255, 0
        for i, j in self.item[1:]:
            position = 10 + 20 * i, 10 + 20 * j
            pygame.draw.circle(screen, _color, position, radius, width)  # 画蛇身体

class Board(object):
    """屏幕"""

    def __init__(self):
        self.board_width = BOARD_WIDTH
        self.board_height = BOARD_HEIGHT

    def draw_board(self, screen):
        """画出游戏区域"""
        _color = 0, 0, 0
        width = 0

        for i in range(self.board_width):
            pos = i * 20, 0, 20, 20
            pygame.draw.rect(screen, _color, pos, width)
            pos = i * 20, (self.board_height - 1) * 20, 20, 20
            pygame.draw.rect(screen, _color, pos, width)

        for i in range(self.board_height - 1):
            pos = 0, 20 + i * 20, 20, 20
            pygame.draw.rect(screen, _color, pos, width)
            pos = (self.board_width - 1) * 20, 20 + i * 20, 20, 20
            pygame.draw.rect(screen, _color, pos, width)

    @staticmethod
    def print_text(screen, font, x, y, text, _color=(255, 0, 0)):
        """屏幕打印字符"""
        img_text = font.render(text, True, _color)
        screen.blit(img_text, (x, y))

class Game(object):

    def __init__(self):
        """游戏初始化"""
        pygame.init()
        self.screen = pygame.display.set_mode((BOARD_WIDTH * 20, BOARD_HEIGHT * 20))
        pygame.display.set_caption("snake_game")

    def game_start(self, screen):
        """开始游戏"""
        snake = Snake()
        food = Food()
        board = Board()
        player = Player()
        font = pygame.font.SysFont("SimHei", 30)
        is_fail = 0
        text = "score: 0"

        while True:
            for event in pygame.event.get():
                if event.type == QUIT:
                    exit()

            screen.fill((0, 0, 100))
            board.draw_board(screen=screen)

            keys = pygame.key.get_pressed()
            player.press(keys, snake)

            if is_fail:
                font2 = pygame.font.Font(None, 40)
                board.print_text(screen, font, 0, 0, text)
                board.print_text(screen, font2, 400, 200, "GAME OVER")

            if not is_fail:
                enlarge = snake.eat(food)
                global score
                text = "score: {}".format(score)
                board.print_text(screen, font, 0, 0, text)
                food.update(screen, enlarge, snake)
                snake.move(enlarge)
                is_fail = self.game_over(snake=snake)
                snake.draw(screen)

            pygame.display.update()
            time.sleep(0.05)

    @staticmethod
    def game_over(snake):
        """判断游戏是否结束"""
        board_x, board_y = snake.get_head()
        flag = 0
        old = len(snake.item)
        # 多重列表去重
        tmp_list = []
        for i in snake.item:
            if i not in tmp_list:
                tmp_list.append(i)
        new = len(tmp_list)

        if new < old:
            flag = 1
        if board_x == 0 or board_x == BOARD_WIDTH - 1:
            flag = 1
        if board_y == 0 or board_y == BOARD_HEIGHT - 1:
            flag = 1

        if flag:
            return True
        else:
            return False

class Player(object):

    def __init__(self):
        pass

    @staticmethod
    def press(keys, snake):
        """按键控制"""
        global score
        if keys[K_w] or keys[K_UP]:
            snake.toward(0, -1)
        elif keys[K_s] or keys[K_DOWN]:
            snake.toward(0, 1)
        elif keys[K_a] or keys[K_LEFT]:
            snake.toward(-1, 0)
        elif keys[K_d] or keys[K_RIGHT]:
            snake.toward(1, 0)
        elif keys[K_r]:  # 重置游戏
            score = 0
            main()

def main():
    game = Game()
    game.game_start(game.screen)

if __name__ == '__main__':
    main()


from xai.brain.wordbase.nouns._dominion import _DOMINION

#calss header
class _DOMINIONS(_DOMINION, ):
	def __init__(self,):
		_DOMINION.__init__(self)
		self.name = "DOMINIONS"
		self.specie = 'nouns'
		self.basic = "dominion"
		self.jsondata = {}


from django.shortcuts import render
from django.views.generic import ListView
from .models import Assignment, Submission
from .forms import AssignmentForm, SubmissionForm
from django.shortcuts import render, redirect, get_object_or_404

def new_assignment(request):
    assignments = Assignment.objects.all()
    if request.method == 'POST':
        form = AssignmentForm(request.POST)
        if form.is_valid():
            assignment = form.save()
            return redirect('home')
    else:
        form = AssignmentForm()
    return render(request, 'home.html', {'form': form, 'assignments': assignments})

def show_assignment(request, pk):
    assignment = get_object_or_404(Assignment, pk=pk)
    submissions = assignment.submission_set.all()
    if request.method == 'POST':
        form = SubmissionForm(request.POST, request.FILES)
        if form.is_valid():
            submission = form.save(commit=False)
            submission.assignment = assignment
            submission.save()
            return redirect('show_assignment', pk=pk)
    else:
        form = SubmissionForm()
    return render(request, 'assignment.html', {'form': form, 'assignment': assignment, 'submissions': submissions})


import os
from train import get_network_and_environment_creator, bool_arg
import custom_logging
import argparse
import numpy as np
import time
from PIL import Image
import tensorflow as tf
from paac import PAACLearner

def get_save_frame(dest_folder, name):
    class Counter:
        def __init__(self):
            self.counter = 0
        def increase(self):
            self.counter += 1
        def get(self):
            return self.counter
    counter = Counter()

    def get_frame(frame):
        im = Image.fromarray(frame[:, :, ::-1])
        im.save("{}_{:05d}.png".format(os.path.join(dest_folder, name), counter.get()))
        counter.increase()
        return False
    return get_frame

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('-f', '--folder', type=str, help="Folder where to save the debugging information.", dest="folder")
    parser.add_argument('-tc', '--test_count', default='5', type=int, help="The amount of tests to run on the given network", dest="test_count")
    parser.add_argument('-re', '--random_eval', default=False, type=bool_arg, help="Whether or not to use 35 random steps", dest="random_eval")
    parser.add_argument('-s', '--show', default=False, type=bool_arg, help="Whether or not to show the run", dest="show")
    parser.add_argument('-gf', '--gif_folder', default=None, type=str, help="The folder to save the gifs", dest="gif_folder")
    parser.add_argument('-d', '--device', default='/gpu:0', type=str, help="Device to be used ('/cpu:0', '/gpu:0', '/gpu:1',...)", dest="device")

    args = parser.parse_args()
    arg_file = os.path.join(args.folder, 'args.json')
    device = args.device
    for k, v in custom_logging.load_args(arg_file).items():
        setattr(args, k, v)
    args.max_global_steps = 0
    df = args.folder
    args.debugging_folder = '/tmp/logs'
    args.device = device

    if args.random_eval:
        args.random_start = False
    args.single_life_episodes = False
    if args.show:
        args.visualize = 1

    args.actor_id = 0
    rng = np.random.RandomState(int(time.time()))
    args.random_seed = rng.randint(1000)

    network_creator, env_creator = get_network_and_environment_creator(args)
    network = network_creator()
    saver = tf.train.Saver()

    rewards = []
    environment = env_creator.create_environment(0)
    if args.gif_folder:
        if not os.path.exists(args.gif_folder):
            os.makedirs(args.gif_folder)
        environment.on_new_frame = get_save_frame(args.gif_folder, 'gif')
    print(args.random_eval)
    with tf.Session() as sess:
        checkpoints_ = os.path.join(df, 'checkpoints')
        network.init(checkpoints_, saver, sess)
        for i in range(args.test_count):
            state = environment.get_initial_state()
            if args.random_eval:
                for _ in range(35):
                    state, _, _ = environment.next(np.eye(environment.get_legal_actions().shape[0])[rng.randint(environment.get_legal_actions().shape[0])])

            episode_over = False
            reward = 0.0
            while not episode_over:
                action = PAACLearner.choose_next_actions(network, env_creator.num_actions, [state], sess)
                state, r, episode_over = environment.next(action[0])
                reward += r
            rewards.append(reward)
            print(reward)
        print(np.mean(rewards), np.min(rewards), np.max(rewards), np.std(rewards))


from AWSIoTPythonSDK.MQTTLib import AWSIoTMQTTClient
import logging
import time
import argparse
import yaml
import json
import datetime
import calendar

# General message notification callback
def customOnMessage(message):
    print("Received a new message: ")
    print(message.payload)
    print("from topic: ")
    print(message.topic)
    print("--------------\n\n")

# Suback callback
def customSubackCallback(mid, data):
    print("Received SUBACK packet id: ")
    print(mid)
    print("Granted QoS: ")
    print(data)
    print("++++++++++++++\n\n")

# Puback callback
def customPubackCallback(mid):
    print("Received PUBACK packet id: ")
    print(mid)
    print("++++++++++++++\n\n")

with open('pyDeviceConfig.yml') as file:
    config = yaml.load(file)

host = config['specificConfig']['ApNortheast1']['host']
rootca = config['specificConfig']['ApNortheast1']['rootCaPath']
clientcert = config['specificConfig']['ApNortheast1']['certificatePath']
clientkey = config['specificConfig']['ApNortheast1']['privateKeyPath']
clientId = config['CommonConfig']['clientid']
topic = config['topicConfig']['test']

print("Endpoint: " + host + "\n" \
        "RootCA path: " + rootca + "\n" \
        "clientcert path: " + clientcert + "\n" \
        "clientkey path: " + clientkey + "\n" \
        "clientId: " + clientId + "\n" \
        "topic: " + topic + "\n")

# Configure logging
logger = logging.getLogger("AWSIoTPythonSDK.core")
logger.setLevel(logging.DEBUG)
streamHandler = logging.StreamHandler()
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
streamHandler.setFormatter(formatter)
logger.addHandler(streamHandler)

# Init AWSIoTMQTTClient (Not use websocket)
myAWSIoTMQTTClient = None
myAWSIoTMQTTClient = AWSIoTMQTTClient(clientId)
myAWSIoTMQTTClient.configureEndpoint(host, 8883)
myAWSIoTMQTTClient.configureCredentials(rootca, clientkey, clientcert)

# AWSIoTMQTTClient connection configuration
myAWSIoTMQTTClient.configureAutoReconnectBackoffTime(1, 32, 20)
myAWSIoTMQTTClient.configureOfflinePublishQueueing(-1)  # Infinite offline Publish queueing
myAWSIoTMQTTClient.configureDrainingFrequency(2)  # Draining: 2 Hz
myAWSIoTMQTTClient.configureConnectDisconnectTimeout(10)  # 10 sec
myAWSIoTMQTTClient.configureMQTTOperationTimeout(5)  # 5 sec
myAWSIoTMQTTClient.onMessage = customOnMessage

# Connect and subscribe to AWS IoT
myAWSIoTMQTTClient.connect()
# Note that we are not putting a message callback here. We are using the general message notification callback.
myAWSIoTMQTTClient.subscribeAsync(topic, 1, ackCallback=customSubackCallback)
time.sleep(2)

# test configure
humidity = "50"
temperature = "20"

# Publish to the same topic in a loop forever
while True:
    # timestamp
    now = datetime.datetime.utcnow()
    recordat = str(now.strftime("%Y-%m-%d"))
    timeStamp = str(calendar.timegm(now.utctimetuple()))

    # publish
    publishPayload = json.dumps({"recordat": recordat, "time_stamp": timeStamp, "uuid": clientId,  "room_humidity": humidity, "room_temperature": temperature})
    myAWSIoTMQTTClient.publishAsync(topic, publishPayload, 1, ackCallback=customPubackCallback)
    time.sleep(1)


import argparse
from typing import Optional
from urllib.parse import urlparse
from flask import Flask, Response

from pipert.contrib.metrics_collectors.prometheus_collector import PrometheusCollector
from pipert.core.component import BaseComponent
from pipert.contrib.metrics_collectors.splunk_collector import SplunkCollector
from pipert.core.metrics_collector import NullCollector
from pipert.core.routine import Routine
import queue
from threading import Thread
import cv2
from pipert.utils.visualizer import VideoVisualizer
from pipert.utils.visualizer.catalog import MetadataCatalog
from pipert.core.message import message_decode, Message
from pipert.core.message_handlers import RedisHandler
from pipert.core import QueueHandler
import time
import os

def gen(q: QueueHandler):
    while True:
        encoded_frame = q.non_blocking_get()
        if encoded_frame:
            yield (b'--frame\r\n'
                   b'Pragma-directive: no-cache\r\n'
                   b'Cache-directive: no-cache\r\n'
                   b'Cache-control: no-cache\r\n'
                   b'Pragma: no-cache\r\n'
                   b'Expires: 0\r\n'
                   b'Content-Type: image/jpeg\r\n\r\n' + encoded_frame + b'\r\n\r\n')

class MetaAndFrameFromRedis(Routine):

    def __init__(self, in_key_meta, in_key_im, url, queue, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.in_key_meta = in_key_meta
        self.in_key_im = in_key_im
        self.url = url
        self.q_handler = QueueHandler(queue)
        self.msg_handler = None
        self.flip = False
        self.negative = False

    def receive_msg(self, in_key, most_recent=True):
        if most_recent:
            encoded_msg = self.msg_handler.read_most_recent_msg(in_key)
        else:
            encoded_msg = self.msg_handler.receive(in_key)
        if not encoded_msg:
            return None
        msg = message_decode(encoded_msg)
        msg.record_entry(self.component_name, self.logger)
        return msg

    def main_logic(self, *args, **kwargs):
        pred_msg = self.receive_msg(self.in_key_meta, most_recent=True)
        frame_msg = self.receive_msg(self.in_key_im, most_recent=False)
        if frame_msg:
            arr = frame_msg.get_payload()

            if self.flip:
                arr = cv2.flip(arr, 1)

            if self.negative:
                arr = 255 - arr

            frame_msg.update_payload(arr)
            success = self.q_handler.deque_non_blocking_put((frame_msg, pred_msg))
            return success
        else:
            time.sleep(0)
            return False

    def setup(self, *args, **kwargs):
        self.msg_handler = RedisHandler(self.url)

    def cleanup(self, *args, **kwargs):
        self.msg_handler.close()

class VisLogic(Routine):
    def __init__(self, in_queue, out_queue, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.in_queue = QueueHandler(in_queue)
        self.out_queue = QueueHandler(out_queue)
        self.vis = VideoVisualizer(MetadataCatalog.get("coco_2017_train"))
        self.latest_pred_msg = None

    def main_logic(self, *args, **kwargs):
        # TODO implement input that takes both frame and metadata
        messages = self.in_queue.non_blocking_get()
        if messages:
            frame_msg, pred_msg = messages
            if pred_msg is not None:
                self.latest_pred_msg = pred_msg
            pred_msg = self.latest_pred_msg
            self.draw_preds_on_frame(frame_msg, pred_msg)
            self.pass_frame_to_flask(frame_msg, pred_msg)
            return True
        else:
            return None

    def draw_preds_on_frame(self, frame_msg, pred_msg: Optional[Message]):
        if pred_msg is not None and not pred_msg.is_empty():
            frame = frame_msg.get_payload()
            pred = pred_msg.get_payload()
            image = self.vis.draw_instance_predictions(frame, pred, args_dict['names']) \
                .get_image()
            frame_msg.update_payload(image)

    def pass_frame_to_flask(self, frame_msg, pred_msg: Optional[Message]):
        image = frame_msg.get_payload()
        _, frame = cv2.imencode('.jpg', image)
        frame = frame.tobytes()
        frame_msg.record_exit(self.component_name, self.logger)
        if pred_msg is not None and not pred_msg.reached_exit:
            pred_msg.record_exit(self.component_name, self.logger)
            latency = pred_msg.get_end_to_end_latency(self.component_name)
            if latency is not None:
                self.metrics_collector.collect_latency(latency, self.component_name)
        success = self.out_queue.deque_non_blocking_put(frame)
        return success

    def setup(self, *args, **kwargs):
        self.state.dropped = 0

    def cleanup(self, *args, **kwargs):
        pass

class FlaskVideoDisplay(BaseComponent):

    def __init__(self, in_key_meta, in_key_im, redis_url, metrics_collector, endpoint,
                 name="FlaskVideoDisplay"):
        super().__init__(endpoint, name, metrics_collector)
        self.queue = queue.Queue(maxsize=1)
        self.t_get = MetaAndFrameFromRedis(in_key_meta, in_key_im, redis_url,
                                           self.queue,
                                           name="get_frames_and_preds",
                                           component_name=self.name,
                                           metrics_collector=self.metrics_collector)
        self.t_get.as_thread()
        self.register_routine(self.t_get)

        self.queue2 = queue.Queue(maxsize=1)
        self.t_vis = VisLogic(self.queue, self.queue2, name="vis_logic",
                              component_name=self.name,
                              metrics_collector=self.metrics_collector).as_thread()
        self.register_routine(self.t_vis)

        app = Flask(__name__)
        app.debug = False

        @app.route('/video')
        def video_feed():
            return Response(gen(QueueHandler(self.queue2)),
                            mimetype='multipart/x-mixed-replace; '
                                     'boundary=frame')

        self.server = Thread(target=app.run, kwargs={"host": '0.0.0.0'})
        self.register_routine(self.server)

    def flip_im(self):
        self.t_get.flip = not self.t_get.flip

    def negative(self):
        self.t_get.negative = not self.t_get.negative

if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument('-i', '--input_im', help='Input stream key name', type=str, default='camera:0')
    parser.add_argument('-m', '--input_meta', help='Input stream key name', type=str, default='camera:2')
    parser.add_argument('--names', type=str, default='pipert/contrib/YoloResources/coco.names',
                        help='coco.names file path')
    parser.add_argument('-u', '--url', help='Redis URL', type=str, default='redis://127.0.0.1:6379')
    parser.add_argument('-z', '--zpc', help='zpc port', type=str, default='4246')
    parser.add_argument('--monitoring', help='Name of the monitoring service', type=str, default='prometheus')

    args = parser.parse_args()
    args_dict = vars(args)
    # Set up Redis connection
    # url = urlparse(args.url)
    url = os.environ.get('REDIS_URL')
    url = urlparse(url) if url is not None else urlparse(args.url)

    if args.monitoring == 'prometheus':
        collector = PrometheusCollector(8082)
    elif args.monitoring == 'splunk':
        collector = SplunkCollector()
    else:
        collector = NullCollector()

    zpc = FlaskVideoDisplay(args.input_meta, args.input_im, url, collector, f"tcp://0.0.0.0:{args.zpc}")
    print(f"run {zpc.name}")
    zpc.run()
    print(f"Killed {zpc.name}")