# Released under the MIT license (http://choosealicense.com/licenses/mit/).
# For more information see https://github.com/DexterInd/GrovePi/blob/master/LICENSE

import grovepi
import serial, time, sys
import re

en_debug = False

def debug(in_str):
    if en_debug:
        print(in_str)

patterns=["$GPGGA",
    "/[0-9]{6}\.[0-9]{2}/", # timestamp hhmmss.ss
    "/[0-9]{4}.[0-9]{2,/}", # latitude of position
    "/[NS]",  # North or South
    "/[0-9]{4}.[0-9]{2}", # longitude of position
    "/[EW]",  # East or West
    "/[012]", # GPS Quality Indicator
    "/[0-9]+", # Number of satellites
    "/./", # horizontal dilution of precision x.x
    "/[0-9]+\.[0-9]*/" # altitude x.x
    ]

class GROVEGPS():
    def __init__(self, port='/dev/ttyAMA0', baud=9600, timeout=0):
        self.ser = serial.Serial(port, baud, timeout=timeout)
        self.ser.flush()
        self.raw_line = ""
        self.gga = []
        self.validation =[] # contains compiled regex

        # compile regex once to use later
        for i in range(len(patterns)-1):
            self.validation.append(re.compile(patterns[i]))

        self.clean_data()
        # self.get_date()  # attempt to gete date from GPS.

    def clean_data(self):
        '''
        clean_data:
        ensures that all relevant GPS data is set to either empty string
        or -1.0, or -1, depending on appropriate type
        This occurs right after initialisation or
        after 50 attemps to reach GPS
        '''
        self.timestamp = ""
        self.lat = -1.0    # degrees minutes and decimals of minute
        self.NS = ""
        self.lon = -1.0
        self.EW = ""
        self.quality = -1
        self.satellites = -1
        self.altitude = -1.0

        self.latitude = -1.0  #degrees and decimals
        self.longitude = -1.0
        self.fancylat = ""  #

    # def get_date(self):
    #     '''
    #     attempt to get date from GPS data. So far no luck. GPS does
    #     not seem to send date sentence at all
    #     function is unfinished
    #     '''
    #     valid = False
    #     for i in range(50):
    #         time.sleep(0.5)
    #         self.raw_line = self.ser.readline().strip()
    #         if self.raw_line[:6] == "GPZDA":  # found date line!
    #             print (self.raw_line)


    def read(self):
        '''
        Attempts 50 times at most to get valid data from GPS
        Returns as soon as valid data is found
        If valid data is not found, then clean up data in GPS instance
        '''
        valid = False
        for _ in range(50):
            time.sleep(0.5)
            self.raw_line = self.ser.readline()
            try:
                self.line = self.raw_line.decode('utf-8')
                self.line = self.line.strip()
            except:
                self.line = ""
            debug(self.line)
            if self.validate(self.line):
                valid = True
                break

        if valid:
            return self.gga
        else:
            self.clean_data()
            return []

    def validate(self, in_line):
        '''
        Runs regex validation on a GPGAA sentence.
        Returns False if the sentence is mangled
        Return True if everything is all right and sets internal
        class members.
        '''
        if in_line == "":
            return False
        if in_line[:6] != "$GPGGA":
            return False

        self.gga = in_line.split(",")
        debug (self.gga)

        #Sometimes multiple GPS data packets come into the stream. Take the data only after the last '$GPGGA' is seen
        try:
            ind=self.gga.index('$GPGGA', 5, len(self.gga))
            self.gga=self.gga[ind:]
        except ValueError:
            pass

        if len(self.gga) != 15:
            debug ("Failed: wrong number of parameters ")
            debug (self.gga)
            return False

        for i in range(len(self.validation)-1):
            if len(self.gga[i]) == 0:
                debug ("Failed: empty string %d"%i)
                return False
            test = self.validation[i].match(self.gga[i])
            if test == False:
                debug ("Failed: wrong format on parameter %d"%i)
                return False
            else:
                debug("Passed %d"%i)

        try:
            self.timestamp = self.gga[1]
            self.lat = float(self.gga[2])
            self.NS = self.gga[3]
            self.lon = float(self.gga[4])
            self.EW = self.gga[5]
            self.quality = int(self.gga[6])
            self.satellites = int(self.gga[7])
            self.altitude = float(self.gga[9])

            self.latitude = self.lat // 100 + self.lat % 100 / 60
            if self.NS == "S":
                self.latitude = - self.latitude
            self.longitude = self.lon // 100 + self.lon % 100 / 60
            if self.EW == "W":
                self.longitude = -self.longitude
        except ValueError:
            debug( "FAILED: invalid value")

        return True


if __name__ =="__main__":
    gps = GROVEGPS()
    while True:
        time.sleep(1)
        in_data = gps.read()
        if in_data != []:
            print (in_data)
