############################################################################JEJUS############################
#  [O2U.py]                                                                                                 #
#                                                                                                           #
#  Function : "O2U.py" is a header file for EEG signal processing in "Python Scripting" Box.                #
#             There exist the basic methods which are used in a main script for the signal processing.      #
#             You can try to process the EEG signal in the various ways by adding a method that you want    #
#             to use in this header file.                                                                   #
#                                                                                                           #
# --------------------------------------------------------------------------------------------------------- #
#                                                                                                           #
# Author:                                                                                                   #                                                                 
#  Seongjoon Jeong (jdd01299@naver.com)                                                                     #
#  Brain-Computer Interface (BCI) Lab.                                                                      #
#  School of Computer Science and Electrical Engineering,                                                   #
#  Handong Global University, Pohang, Korea                                                                 #
#                                                                                                           #
#                                                                                                           #
# Histroy: (1.0) 2018.12.08 by Seongjoon Jeong                                                              #
#                                                                                                           #
#############################################################################################################

import numpy
import math


### The Methods for Signal Processing ###


# 1. stimInputCheck(...) ( Output : result{=bool} )
#    : To check that the interested stimulation's input is coming in.
def stimInputCheck(input, OVStimulationSet, index, identifier): 
    result = False
    for chunkIndex in range( len(input[index]) ):
        chunk = input[index].pop()
        if(type(chunk) == OVStimulationSet):
             for stimIdx in range(len(chunk)):
                 stim=chunk.pop();
                 # print('Received stim: ', stim.identifier, 'stamped at', stim.date, 's')
                 if(stim.identifier == identifier):
                      result = True
                      
    return result



# 2. readClassifierTxt(...) ( Output : classifier[1*numOfClass] )
#    : To read a classifier from a text file which is generated during the training time.
def readClassifierTxt(filename):
    f = open(filename)
    lines = f.readlines()
    classifier = lines[len(lines)-1]
    classifier = classifier[1:len(classifier)-2]
    classifier = classifier.split(', ')
    classifier = map(float, classifier)
    f.close()
    
    return classifier



# 3. returnMeanOfAll(...) ( Output : mean{=float} )
#    : To retrun a mean of the raw data in all chennels.
def returnMeanOfAll(rawData, dimensionSizes):                       # rawData   = 1D array [1 * signals]      
    rawData2D = numpy.array(rawData).reshape(tuple(dimensionSizes)) # rawData2D = 2D array [channels * signals]
    rawData2D = rawData2D.mean(axis=0)                              # rawData2D = 2D array [channels * 1] (Only one value per channel)
    mean = (numpy.mean(rawData2D))                                  # mean      = float    [1*1]
    return mean



# 4. makeDevClassifierTxt(...) ( Output : classifier[1*numOfClass] )
#    : To make a text file that has a classifier, which is generated by using an array of means and its standard deviation,
#       or return the classifier.
def makeDevClassifierTxt(meanArr, filename, numOfClass):
    lines =[]
    classifier = [0] * numOfClass
    
    f = open(filename, 'w')
    lines.append('---meanArr ( len=' + str(len(meanArr)) + ' )---\n')
    lines.append(str(meanArr) + '\n')
    
    N=len(meanArr)
    sumM=0  
    sumV=0

    # Average
    for i in range(0,N):
        sumM += meanArr[i]
        avg = sumM/N
        
    # Variance
    for i in range(0,N):
        sumV += math.pow(meanArr[i]-avg, 2)
        myVar = sumV/(N-1)
        
    # Deviation
    myDev = math.sqrt(myVar)
    lines.append('- Average: ' + str(avg) + '\n')
    lines.append('- Variance: ' + str(myVar) + '\n')
    lines.append('- Deviation: ' + str(myDev) + '\n')
                        
    # To make the classifier based uisng the deviation. 
    # If you want to change the interval of the classifiers, you can change it by editting this follow part.
    for i in range(len(classifier)):
        classifier[i] = avg + (myDev * (-0.4 + (i * 0.4)))         

    lines.append('---Classifier ( ' + str(numOfClass) + 'classes)---\n')
    lines.append(str(classifier) + '\n')
    f.writelines(lines)
    f.close()
    
    return classifier



# 5. determineClass(...) ( Output : classNum{=Int} )
#    : To determine a class among N+1 classes.
def determineClass(classifier, mean):
    for i in range(len(classifier)-1):
        if classifier[i] <= mean < classifier[i+1]: # Among 1 ~ N-1
            classNum = i+1
    if classifier[0] > mean:
        classNum = 0  # Default state, 0
    if classifier[len(classifier)-1] <= mean:
        classNum = len(classifier) # Maximum state, N

    return classNum



# 6. applyWindowSize(...) ( Output : previousMeansArr[1*?], mean{=float} )
#    : To apply the fixed window size.
def applyWindowSize(previousMeansArr, mean):
    # Update the 'previousMeansArr' using a current mean.
    previousMeansArr.pop()
    previousMeansArr.insert(0, mean)
    
    # Also, compute a new mean using the array for the fixed window size.
    mean = sum(previousMeansArr)/len(previousMeansArr)

    return previousMeansArr, mean

# 7. readConfigFile(...) ( Output : classifier[1*numOfClass] )
#    : To read a classifier from a text file which is generated during the training time.
def readConfigFile(filename):
    f = open(filename)
    while True:
        line = f.readline()
        if not line: break

        if line[0] == 'O':
            tokens = line.split(' ')
            tokens[1] = tokens[1][0:len(tokens[1])-1]
            if tokens[0] == 'O2U_FILE_NAME':
                filename = tokens[1]
            elif tokens[0] == 'O2U_TRAINING_TIME':
                trainingTime = int(tokens[1])
            elif tokens[0] == 'O2U_ClASS_NUMBER':
                classNum = int(tokens[1])                
        
    f.close()

    return filename, trainingTime, classNum
