###TODO: In Unix/Linux systems, put the hash bang here with the Python path

#PPM from RC transmitters Calibrate Version 2.0
#
#PPMRC - PPM interpreter, calibrator and remapper
#Copyright (C) 2017  Francesco Antonetti Lamorgese Passeri
#https://github.com/antlampas/ppmrc
#
#This program is free software: you can redistribute it and/or modify
#it under the terms of the GNU General Public License as published by
#the Free Software Foundation, either version 3 of the License, or
#(at your option) any later version.
#
#PPMRC is distributed in the hope that it will be useful,
#but WITHOUT ANY WARRANTY; without even the implied warranty of
#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#GNU General Public License for more details.
#
#You should have received a copy of the GNU General Public License
#along with this program.  If not, see <http://www.gnu.org/licenses/>.


import serial
import time

#Global variables
licenseText = "\n\nPPMRC  Copyright (C) 2017  Francesco Antonetti Lamorgese \
Passeri\nThis program comes with ABSOLUTELY NO WARRANTY; for details visit \
http://fsf.org/.\nThis is free software, and you are welcome to redistribute \
it under certain conditions; visit https://github.com/antlampas/ppmrc for \
details.\n\n"
serialIn = serial.Serial()
serialOut = serial.Serial()
outputType = ""
f = ""
userInterfaceType = ""
#End global variables

#Functions definition

#Remap function
def remap(x,in_min,in_max,out_min,out_max):
    # Maps the x value from a range [in_min,in_max] to another [out_min,out_max]
    # It is the same of map() function of Arduino's language
    return float((x-in_min)*(out_max-out_min)/(in_max-in_min)+out_min)          #Returns the remaped and converted to floating point value
#End remap

#Read values function
def readValues():
    # Reads the values from the serial port.
    # the input parameter is the serial port where read data and returns the
    # values array, converted in floating point variables.
    # The input comes in form of string of numbers separated by spaces
    while True:
        serialInput  = serialIn.readline()                                      #Reads the input string from serial port
        channelsArray = serialInput.split()                                     #Splits the string in an array
        channelsValue = []                                                      #Declares an array where put values converted in floating poing variables
        good = 1                                                                #This is needed to check if substrings are numbers or not
        for i in channelsArray:                                                 #For each number in the input string
            if i.isdigit():                                                     #Checks if the variable is a number
                channelsValue += [float(i)]                                     #And, if it is, puts it in the array
            else:                                                               #Otherwise
                good = 0                                                        #Report "bad input"
                break                                                           #and stop putting values in the array
        if good == 1:                                                           #If all values are correctly numbers
            return channelsValue                                                #Returns the array with the values converted in floating point and splited in an array
#End readValues

def minMax(ord,lowerBound,upperBound,tests,numberOfChannels,wait):
    #ord: "minimums" find minimums; "maximums" find maximums
    #arr: array with initial minimum and maximum values
    #wait: give some time to move axes in required positions
    boundaries = []                                                             #Vector for the minimum and maximum input values
    for i in range(numberOfChannels):                                           #Initialize the vector to host the minimum and maximum values of each channel
        boundaries.extend([float(lowerBound)])                                  #First n elements are for the minimum values of the n channels
    for i in range(numberOfChannels):                                           #The next n elemets (from n+1 to 2n) are for the maximum values for the n channels
        boundaries.extend([float(upperBound)])

    numberOfTests = range(1,tests)

    if ord == "minimum":                                                        #Advise to position the axes
        messageOutput(userInterfaceType,"Move all axes to minimum")
    elif ord == "maximum":
        messageOutput(userInterfaceType,"Move all axes to maximum")
    time.sleep(wait)                                                            #Ignore all the garbage that Arduino sends at the begin
    for i in numberOfTests:                                                     #Here begin the readings to find the minimum values
        chVal = readValues()                                                    #Read the string from the serial port
        if len(chVal) != numberOfChannels:                                      #Check that the number of values meets the number of channels
            i -= 1
            continue
        j = 0
        for ch in chVal:                                                        #For each channel
            if ord == "minimum":                                                #If minimums chosen, search and store the minimums
                leftSide = float(ch)
                rightSide = lowerBound
                index = j
            elif ord == "maximum":                                              #Else, if maximums chosen, search and store maximums
                leftSide = upperBound
                rightSide = float(ch)
                index = j+numberOfChannels
            if leftSide > rightSide:                                            #Checks that the value isn't below the lower or above the upper bound
                if ord == "minimum":
                    leftSide = float(ch)
                    rightSide = boundaries[index]
                elif ord == "maximum":
                    leftSide = boundaries[index]
                    rightSide = float(ch)
                if leftSide < rightSide:                                        #If the input value is smaller than minimum value or bigger than maximum value in the array
                    boundaries[index] = leftSide                                #Overwrite the value
                j += 1
    return boundaries

#Calibrate function
def calibrate(tests,wait,lowerBound,upperBound,numberOfChannels):
    # Calibrates the serial input to be included in a range correctly readable
    # from FlightGear ([-1.0,1.0] for roll, pitch and yaw; [0,1.0] for throttle)
    boundaries = \
    minMax("minimum",lowerBound,upperBound,tests,numberOfChannels,wait)         #Find and store minimums
    boundaries = \
    minMax("maximum",lowerBound,upperBound,tests,numberOfChannels,wait)         #Find and store maximums

    return boundaries
#End calibrate function

#User interface functions

#Output to user
def messageOutput(outputType,message):                                          #Function needed unbind user interface from application logic
    if outputType == "command line" or outputType == "Command Line" or \
    outputType == "Command line" or outputType == "COMMAND LINE" or \
    outputType == "CLI" or outputType == "cli":
        print(message)
    elif outputType == "Graphical" or outputType == "graphical" or \
    outputType == "GRAPHICAL" or outputType == "GUI" or outputType == "gui":
        #TODO: Write code related to the creation of the GUI and print the
        #message on it
        doSomething()
#End output to user

#Read the user input
def userInput(inputType,message):                                               #Function needed unbind user interface from application logic
    if inputType == "command line" or inputType == "Command Line" or \
    inputType == "Command line" or inputType == "COMMAND LINE" or \
    inputType == "CLI" or inputType == "cli":
        return input(message)
    elif inputType == "Graphical" or inputType == "graphical" or \
    inputType == "GRAPHICAL" or inputType == "GUI" or inputType == "gui":
        #TODO: Write code related to the creation of the GUI and print the
        #message on it
        doSomething()
#End read the user input

#End user interface functions

#Initialize function
def initialize():
    global outputType
    global f
    global serialIn
    global serialOut
    global userInterfaceType

    userInterfaceType = "cli"

    messageOutput(userInterfaceType,licenseText)
    
    serialInPort      = userInput(userInterfaceType,"Input serial port: ")
    serialInBaud      = userInput(userInterfaceType,"Baudrate: ")
    serialIn.port     = serialInPort
    serialIn.baudrate = serialInBaud
    serialIn.timeout  = 0
    serialIn.open()

    while True:
        outputType = \
        userInput(userInterfaceType, \
        "Where sould i put the output data? (serial or file) ")

        if outputType == "Serial" or outputType == "serial":
            serialOutPort      = userInput(userInterfaceType, \
            "Output serial port: ")
            serialOutBaud      = userInput(userInterfaceType,"Baudrate: ")
            serialOut.port     = serialOutPort
            serialOut.baudrate = serialOutBaud
            serialOut.timeout  = 0
            serialOut.open()
            break
        elif outputType == "File" or outputType == "file":
            fileName = userInput(userInterfaceType,"Full file path: ")
            f = open(fileName,"w")
            f.close()
            f = open(fileName,"a")
            break
        else:
            messageOutput(userInterfaceType,"I don't understand...")

    ### TODO: PUT YOUR INITIALIZATION CODE HERE ###

#End initialize

#End functions definition

#Main program
initialize()
### TODO: PUT YOUR CODE HERE ###
#End main program
