import cv2
import numpy as np
import torch
import argparse
from scipy.ndimage.filters import gaussian_filter
from torch.nn.functional import interpolate
from scipy.ndimage.measurements import center_of_mass
import SimpleITK as sitk

np.random.seed(0)

class Normalize(object):

    def __call__(self, image):
        mean, std = image.mean(), image.std()
        image = (image - mean) / std
        return image

class Interpolate(object):
    """Reduces size of image"""

    def __init__(self, window_size):
        self.window_size = window_size

    def __call__(self, image):
        image = interpolate(image.unsqueeze(0), size=(self.window_size, self.window_size))
        image = image.squeeze(0)

        return image

class GaussianBlur(object):
    # Implements Gaussian blur as described in the SimCLR paper
    def __init__(self, kernel_size=5, min=0.1, max=2.0, input_channels=3):
        self.min = min
        self.max = max
        # kernel size is set to be 10% of the image height/width
        self.kernel_size = kernel_size
        self.input_channels = input_channels

    def __call__(self, sample):
        sample = np.array(sample)

        # blur the image with a 50% chance
        prob = np.random.random_sample()

        if prob < 0.5:
            sigma = (self.max - self.min) * np.random.random_sample() + self.min
            if self.input_channels == 3:
                sample = cv2.GaussianBlur(sample, (self.kernel_size, self.kernel_size), sigma)
            elif self.input_channels == 1: # in case of dental surgery dataset
                sample = gaussian_filter(sample, sigma=sigma)


        return sample


def cuda(tensor, uses_cuda):
    return tensor.cuda() if uses_cuda else tensor

def str2bool(v):
    # codes from : https://stackoverflow.com/questions/15008758/parsing-boolean-values-with-argparse

    if v.lower() in ('yes', 'true', 't', 'y', '1'):
        return True
    elif v.lower() in ('no', 'false', 'f', 'n', '0'):
        return False
    else:
        raise argparse.ArgumentTypeError('Boolean value expected.')

def eval_str(str):
    try:
        st = eval(str)
    except:
        st = str
    return st


#####################################################################
#####################################################################
#####################################################################

def extract_ROI(image, mask, window_size):

    # Get mask centroid
    c1, c2 = center_of_mass(mask)
    c1, c2 = int(c1), int(c2)
    # Reshape image according to window_size
    image = image[c1 - window_size:c1 + window_size, c2 - window_size:c2 + window_size]

    return image

#####################################################################
#####################################################################
#####################################################################

# import os
# import sys
# import time
# import math
#
# import torch.nn as nn
# import torch.nn.init as init
#
# _, term_width = os.popen('stty size', 'r').read().split()
# term_width = int(term_width)
#
# TOTAL_BAR_LENGTH = 65.
# last_time = time.time()
# begin_time = last_time
def progress_bar(current, total, msg=None):
    import os
    import sys
    import time

    _, term_width = os.popen('stty size', 'r').read().split()
    term_width = int(term_width)

    TOTAL_BAR_LENGTH = 65.
    last_time = time.time()
    begin_time = last_time


    #global last_time, begin_time
    if current == 0:
        begin_time = time.time()  # Reset for new bar.

    cur_len = int(TOTAL_BAR_LENGTH*current/total)
    rest_len = int(TOTAL_BAR_LENGTH - cur_len) - 1

    sys.stdout.write(' [')
    for i in range(cur_len):
        sys.stdout.write('=')
    sys.stdout.write('>')
    for i in range(rest_len):
        sys.stdout.write('.')
    sys.stdout.write(']')

    cur_time = time.time()
    step_time = cur_time - last_time
    last_time = cur_time
    tot_time = cur_time - begin_time

    L = []
    L.append('  Step: %s' % format_time(step_time))
    L.append(' | Tot: %s' % format_time(tot_time))
    if msg:
        L.append(' | ' + msg)

    msg = ''.join(L)
    sys.stdout.write(msg)
    for i in range(term_width-int(TOTAL_BAR_LENGTH)-len(msg)-3):
        sys.stdout.write(' ')

    # Go back to the center of the bar.
    for i in range(term_width-int(TOTAL_BAR_LENGTH/2)+2):
        sys.stdout.write('\b')
    sys.stdout.write(' %d/%d ' % (current+1, total))

    if current < total-1:
        sys.stdout.write('\r')
    else:
        sys.stdout.write('\n')
    sys.stdout.flush()

def format_time(seconds):
    days = int(seconds / 3600/24)
    seconds = seconds - days*3600*24
    hours = int(seconds / 3600)
    seconds = seconds - hours*3600
    minutes = int(seconds / 60)
    seconds = seconds - minutes*60
    secondsf = int(seconds)
    seconds = seconds - secondsf
    millis = int(seconds*1000)

    f = ''
    i = 1
    if days > 0:
        f += str(days) + 'D'
        i += 1
    if hours > 0 and i <= 2:
        f += str(hours) + 'h'
        i += 1
    if minutes > 0 and i <= 2:
        f += str(minutes) + 'm'
        i += 1
    if secondsf > 0 and i <= 2:
        f += str(secondsf) + 's'
        i += 1
    if millis > 0 and i <= 2:
        f += str(millis) + 'ms'
        i += 1
    if f == '':
        f = '0ms'
    return f



