import numpy as np
import time
import math
from datetime import datetime, timedelta


def time_taken(startTime, functionName, shouldRun=False):
    if shouldRun:
        endTime = time.time()
        timeTaken = endTime - startTime
        print(functionName, " took ", timeTaken)


def create_time_range(arrWithATs, rangeStep, addElecLightUpTime=True):
  
    global ATdim
    global electrodeLightUpTime

    minVal = (np.nanmin(arrWithATs) - rangeStep) 
    maxVal = (np.nanmax(arrWithATs) + rangeStep) 

    if addElecLightUpTime:
        maxVal += electrodeLightUpTime

    print("Min ", str(minVal))
    print("Max ", str(maxVal))

    timeRanger = np.arange(minVal, maxVal, rangeStep)

    return timeRanger, minVal, maxVal


def make_sure_larger_value_is_second( inPair ) :
    print("inPari: ", inPair)

    if inPair[0] < inPair[1] :
        return inPair
    else :
        return [inPair[1], inPair[0]]



def get_indices_range_of_data( paramRange, extremes, sampleRate ) :

    sampleRange = []
    limI = 0

    for limit in paramRange :

        thisSampleValue  = get_sample_value( limit, extremes, sampleRate )

        if (thisSampleValue == None) or (thisSampleValue == ''):
            thisSampleValue = extremes[limI]

        sampleRange.append( int(math.ceil(int(thisSampleValue))) )

        limI += 1


    indices, nIndices = enforce_pair_of_range_vals( sampleRange, extremes )

    return indices



def sample_val_to_time(inSampleVal, timeBetweenSamples, totalSamples) :
    seconds = inSampleVal * timeBetweenSamples
    totalTimeInSec = totalSamples * timeBetweenSamples
    if totalTimeInSec < 3600:
         return time.strftime("%Mm%Ss", time.gmtime(seconds))
    else :
        return time.strftime("%Hh%Mm%Ss", time.gmtime(seconds))



def enforce_pair_of_range_vals(rangeList, extremes) :

    if len(rangeList) < 2:
        if len(rangeList) == 0:
            rangeList = [extremes[0], extremes[1]]

        elif rangeList[0] > 0:
            rangeList.insert(0, extremes[0])
        elif rangeList[0] == 0:
            rangeList.append(extremes[1])

    if rangeList[0] == None:
        rangeList[0] = extremes[0]

    if rangeList[1] == None:
        rangeList[1] = extremes[1]

    rangeSize = rangeList[1] - rangeList[0]

    return rangeList, rangeSize



def get_sample_value(sampleValue, extremeValues, sampleRate):
 
    if not sampleValue == None:

        if isinstance(sampleValue, str):
            sampleValue = sampleValue.upper()

            if 'H' in sampleValue or 'M' in sampleValue or 'S' in sampleValue:
                newSampleValue = convert_time_str_to_sample_indices(sampleValue, sampleRate)

                print("extremeValues: ", extremeValues)

                if newSampleValue > extremeValues[1]:
                    newSampleValue = extremeValues[1]

                elif newSampleValue < extremeValues[0]:
                    newSampleValue = extremeValues[0]  

                return int(newSampleValue)

    return sampleValue



def convert_time_str_to_sample_indices(inputStr, sampleRate) :
    # In format "00h00m00s)"
    inStr = inputStr.upper()
    print("inStr: ", inStr)
    startIndex = 0
    if 'H' in inStr:
        hIndex = inStr.index('H')
        hoursInt = int(inStr[ startIndex : hIndex ])
        startIndex = hIndex + 1
    else:
        hoursInt = 0

    if 'M' in inStr :
        mIndex = inStr.index('M')
        minutesInt = int(inStr[ startIndex : mIndex ])
        startIndex = mIndex + 1
    else:
        minutesInt = 0

    if 'S' in inStr :
        sIndex = inStr.index('S')
        secondsInt = int(inStr[ startIndex : sIndex ])
        startIndex = sIndex + 1
    else:
        secondsInt = 0  
              
    # print("H: ", hoursInt, " M: ", minutesInt, " S: ", secondsInt)
    totalSeconds = hoursInt * 60 * 60 + minutesInt * 60 + secondsInt
    totalSamples = totalSeconds * sampleRate

    print(totalSamples)

    return totalSamples



def cell_within(arr, startRange,endRange):
 
    if ((arr >= startRange) & (arr <= endRange)):
        val = 1
        return val
    else:
        return 0



def cell_greater_than(arr, endRange):
    if (arr >= endRange):
        return 0
    else:
        return 1



## CONVERT TimeAction TO X FRAMES
def convert_arr_to_frames(arrToConvert, timeRangeList, rangeOfStep):
    nTimeSteps = int(len(timeRangeList))
    print("No. time steps: ", str(nTimeSteps))

    shapeTuple = (nTimeSteps,arrToConvert.shape[0],arrToConvert.shape[1])
    arrFrames = np.zeros(shape=shapeTuple, dtype="float")
    trailingEdgeMask = np.zeros(shape=shapeTuple, dtype="int8")

    for t in timeRangeList:
        tIndex = np.where(timeRangeList==t)

        startRange=float(t-rangeOfStep)
        endRange=float(t+rangeOfStep)
        # timeStepRange = (t-rangeOfStep,t+rangeOfStep)

        func = np.vectorize(cell_within, otypes=[np.float])
        tempArr = func(arrToConvert,startRange, endRange)

        tempIndices = np.where(tempArr==1)

        trailEdgeFunc = np.vectorize(cell_greater_than, otypes=[np.float])
        tempTrail = trailEdgeFunc(arrToConvert,endRange)

        arrFrames[tIndex,:,:] =  np.add(tempArr,arrFrames[tIndex,:,:])
      
        trailingEdgeMask[tIndex,:,:] = np.add(tempTrail, trailingEdgeMask[tIndex,:,:])

    return arrFrames, trailingEdgeMask






def get_frame_intersect_by_timeRange(timeRange, completeTimeRange):
    
    minIntersect = timeRange[0]
    maxIntersect = timeRange[1]
    npTimeRange = np.asarray(completeTimeRange)

  
    intersect = np.where((npTimeRange >= minIntersect) & (npTimeRange <= maxIntersect))[0]

    if (len(intersect) > 0):
        beginIntersect = intersect[0]
        endIntersect = intersect[(len(intersect)-1)]
    else:
        beginIntersect=0
        endIntersect=0

    return beginIntersect, endIntersect






def gaussian_filter_3d_mask(XYarr, trailingEdgeMask, sigmaVal):
    tempXYarr = gaussian_filter(XYarr, sigmaVal)# VY = gaussian_filter(VY, sigma=1)



## Put a nice gaussian filter around the 1
def make_gaussian_3d(timeXYarr, trailingEdgeMask, sigmaVal):
    # return timeXYarr
    gaussian3d = np.apply_along_axis(gaussian_filter, 0, timeXYarr, sigmaVal)# VY = gaussian_filter(VY, sigma=1)
    # gaussian3d = np.apply_along_axis(np.multiply, 0, timeXYarr, trailingEdgeMask)
    gaussian3d = np.multiply(gaussian3d, trailingEdgeMask)

    return gaussian3d



def make_gaussian_2d(XYarr, sigmaVal):
    return gaussian_filter(XYarr, sigmaVal)# VY = gaussian_filter(VY, sigma=1)



def make_gaussian_2d_multiframe(timeXYarr, sigmaVal):
    gaussian3d = np.copy(timeXYarr)
    nFrames = timeXYarr.shape[0]
    for i in range(0, nFrames):
        gaussian3d[i,:,:] = make_gaussian_2d(gaussian3d[i,:,:], 1)
    return gaussian_filter(timeXYarr, sigmaVal)  # VY = gaussian_filter(VY, sigma=1)    
