# -*- coding: UTF-8 -*-
import sys
import serial
import time
import re
import RPi.GPIO as GPIO
import RaspberryPi
from connectTCP import gestionnaireConnection
from curses import ascii
import os

url_webservice = "tempsreel.histoconso.com"

class bcolors:
    HEADER = '\033[95m'
    OKBLUE = '\033[94m'
    OKRED = '\033[91m'
    OKGREEN = '\033[92m'
    WARNING = '\033[93m'
    FAIL = '\033[91m'
    ENDC = '\033[0m'

    def disable(self):
        self.HEADER = ''
        self.OKBLUE = ''
        self.OKGREEN = ''
        self.WARNING = ''
        self.FAIL = ''
        self.ENDC = ''

class GestionnaireConnection3G :
    
    DEBUG_MODE = True
    
    FAMILLE_MODEM_ITEAD = 0
    FAMILLE_MODEM_COOKING_HACK = 1
    
    famille_modem = 0
    
    gpioBoot = 11
    gpioReset = 12
    
    portCOM = "??"
    
    def __init__(self, portCOM , gestionnaireConnection , famille_modem=FAMILLE_MODEM_ITEAD):
        self.ser = serial.Serial(portCOM, 115200 , timeout=0.1)  # open port   
        self.portCOM = portCOM 
        self.gestionnaireConnection = gestionnaireConnection
        
        if famille_modem == 1 :
            self.gpioBoot = 12
            self.gpioReset = 12
        
        
    def turnTheModuleOnOrOff(self):
        """        
        12 fonctionne avec le modem CookingHack
        11 fonctionne avec le modem Itead_v2
        """
            
        GPIO.setmode(GPIO.BOARD)
            
        GPIO.setup(self.gpioBoot , GPIO.OUT)
        GPIO.output(self.gpioBoot, GPIO.HIGH)
        time.sleep(1.2)
        GPIO.output(self.gpioBoot, GPIO.LOW)
        GPIO.cleanup()
    
    def resetTheModule(self):
        
        if self.famille_modem == self.FAMILLE_MODEM_ITEAD :
            GPIO.setmode(GPIO.BOARD)
                
            GPIO.setup(self.gpioReset, GPIO.OUT)
            GPIO.output(self.gpioReset, GPIO.HIGH)
            time.sleep(0.5)
            GPIO.output(self.gpioReset, GPIO.LOW)
            GPIO.cleanup()    
        
        else :
            self.turnTheModuleOnOrOff()
            if  'AT' not in self.sendATandWait('AT' , timeOut=10) :            
                self.turnTheModuleOnOrOff()
                            
    def sendATandWait(self, strAT , sleepTime=0.2 , timeOut=60):
     
        if self.DEBUG_MODE :
            print("%s" % strAT)
        self.ser.write('{0}\r\n'.format(strAT))
        time.sleep(sleepTime)
        timeLimit = time.time() + timeOut
        reponse = ""
        while len(reponse) == 0 and time.time() < timeLimit :
            reponse += self.ser.read(512)
            time.sleep(0.1)
        
        if self.DEBUG_MODE :
            print("%s%s%s" % (bcolors.OKBLUE , reponse.strip() , bcolors.ENDC))
        
        return reponse
    
    def sendCommande(self, strAT , sleepTime=0.4):
        print(strAT)
        self.ser.write('{0}\r\n'.format(strAT))
        time.sleep(sleepTime)
        reponse = self.ser.read(512)
        print(reponse)
        return reponse
    
    def reveillerModem(self):
        
        modemReveille = True
        
        result = self.sendATandWait('AT' , 0.5 , 3).split()
        
        if 'OK' not in result :
            
            modemReveille = False
                        
            if len(result) == 0 :
                print("{} : Modem {} muet, allumage du modem par GPIO".format(time.strftime("%Y-%m-%d %H:%M") , self.portCOM))
                self.turnTheModuleOnOrOff()
                # Tempo après le démarrage pour éviter les problèmes de sim par la suite
                time.sleep(5)
                
                result = self.sendATandWait('AT')
                if 'NORMAL POWER DOWN' in result :
                    # Oups, on vient de l'étendre alors qu'il fesait une truc
                    self.turnTheModuleOnOrOff()
                    # Tempo après le démarrage pour éviter les problèmes de sim par la suite
                    time.sleep(5)
                    result = self.sendATandWait('AT')
                elif 'AT' in result :
                    modemReveille = True
                    print("Le modem se réveille")
            else :
                print("{} Modem allumé, je le reset".format(time.strftime("%Y-%m-%d %H:%M"))) 
                self.resetTheModule()
                time.sleep(5)
                if 'OK' in self.sendATandWait('AT' , timeOut=5) :
                    modemReveille = True
                    
                    
        if modemReveille :
            
            result = self.sendATandWait('AT+CPIN?', timeOut=5)
            
            if 'SIM PIN' in result or 'CME ERROR: 11' in result :
    #             print("Il faut entrer un code pin pour la SIM, j'essaye 0000")
                self.sendATandWait('AT+CPIN=0000')
                time.sleep(0.5)
                    
        
        return modemReveille
    
    def sendSMS(self , numero , message):
        self.sendATandWait('AT+CMGS="{}"'.format(numero))
        time.sleep(1)
        self.sendATandWait(message)
        self.sendATandWait(ascii.ctrl('z'))
                            
    
    def readSMS(self):
        
        if self.reveillerModem() :
            
            self.sendATandWait('AT+CMGF=1')
#             self.sendATandWait('AT+CMGL=?')
            all_sms = self.sendATandWait('AT+CMGL="ALL"')
            
            demandeCnx = False
            
            for message in all_sms.strip().split('\r\n\r\n') :
                result = re.search(r'\+CMGL: (?P<num_sms>[\d]+),"[A-Z \s]+","(?P<numero>[\+\d]+)"[\w\W]+\r\n(?P<command>[\w\W]+)', message.strip())
                if result != None :
                    numero = result.group('numero')
                    num_sms = result.group('num_sms')
                    
                    """ On efface d'abord """
                    self.sendATandWait('AT+CMGD=' + num_sms)
                                        
                    if "28251669" in result.group('numero') :                
                        command = result.group('command')
                        
                        if "connect" in command :
                            print("On me demande de me connecter")
                            demandeCnx = True
                            self.sendSMS(numero , RaspberryPi.getFBName() + ', Demande connection')
            
            if demandeCnx :
                self.envoyerNouvellesDonnee()
                            
#                             self.envoyerNouvellesDonnee()
                        
    def getInfoAPN(self, imsi):

        # On demande le IMSI de la carte SIM, qui correspond à MCC+MNC
        # MCC 208 correspond à la france
        # Puis 01 ou 02 pour Orange
        #      09 .. 13 pour SFR
        #      20 .. 21 ; 88 pour bouygues
        
        infoAPN = { 'APN' : '' , 'LOGIN' : '' , 'PASSWORD' : '' }
    
        # On cherche l'opérateur de la SIM    
        if '208' == imsi[0:3] :
            # Ca commence bien par le code de la France
            #
            
            if imsi[3:5] in ['20'] :
                print("C'est une sim Bouygues, non prise en compte...")
            elif imsi[3:5] in ['01', '02'] : 
                infoAPN['APN'] = 'orange.fr'
                infoAPN['LOGIN'] = 'orange'
                infoAPN['PASSWORD'] = 'orange'            
            elif imsi[3:5] in ['10'] :
                infoAPN['APN'] = 'websfr'
    
        return infoAPN
        
    def ouvrirCnxServeur(self):
        
        retour = self.sendATandWait('AT+CIMI', timeOut=5)
    
        if 'CME ERROR: 14' in retour :
            # SIM busy, on réessaye 1 fois
            time.sleep(1)
            retour = self.sendATandWait('AT+CIMI', timeOut=5)
                    
        result = re.search(r'(\d{15})', retour)
        
        if result != None :
            imsi = result.group(1)
            infoAPN = self.getInfoAPN(imsi)
            
            if 'IP INITIAL' not in self.sendATandWait('AT+CIPSTATUS') :
                # La dernière connection s'est mal passée
                self.sendATandWait('AT+CIPSHUT')
            
            self.sendATandWait('ATE0')  # Le modem ne répète pas ce qu'on dit !ATE1
                            
            timeout = 10
            while '2' in self.sendATandWait('AT+CREG?') and timeout > 0 :  # Enregistrement sur le réseau : +CREG: 0,1 bon, +CREG: 0,3 pas bon
                # Le modem est en train de se connecter au réseau
                time.sleep(2)
                timeout -= 1 
                
            reponse = self.sendATandWait('AT+CSQ')
            result = re.search(r'\+CSQ:\ ([0-9]+),\d+', reponse)
    
            if result != None :
                niveauReception = result.group(1)
                if self.DEBUG_MODE :
                    print("Niveau de reception de 0 à 30 : {0}".format(niveauReception))
            
            
            for _ in range(0 , 10) :
                reponse = self.sendATandWait('AT+CGATT=1')  # Attach to GPRS service
                if 'OK' in reponse :
                    break 
                time.sleep(1)
                
            self.sendATandWait('AT+CIPMODE=0')  # enable transparent mode
            
            for c in [
                      'AT+SAPBR=3,1,"APN","{}"'.format(infoAPN['APN']),
                      'AT+SAPBR=3,1,"USER","{}"'.format(infoAPN['LOGIN']),
                      'AT+SAPBR=3,1,"PWD","{}"'.format(infoAPN['PASSWORD']),
                      'AT+CIPCSGP=1,"{}"'.format(infoAPN['APN']),
                      'AT+CSTT="{}","{}","{}"'.format(infoAPN['APN'] , infoAPN['LOGIN'], infoAPN['PASSWORD']),  # Start task
                      'AT+CIICR',  # Bring up GPRS connection
                      'AT+CIFSR',  # Get IP address, nécessaire pour éviter erreur 3 ??
#             'AT+CIPSTATUS',
            ] :
                self.sendATandWait(c)
            return True
        
        return False
            
    def envoyerRequeteCIP(self, data2send):
                
        reponse = self.sendATandWait('AT+CIPSTART="TCP","' + url_webservice + '","80"')

        timeout = 30
        
        while 'CONNECT OK' not in reponse and timeout > 0 :
            if self.DEBUG_MODE :
                print("Attente de la connexion...")
            reponse = self.sendATandWait('AT+CIPSTATUS')
            if 'STATE: PDP DEACT' in reponse :
                print("J'abandonne")
                return (None, None)
            time.sleep(1)
            timeout -= 1
                
        self.sendATandWait('AT+CIPSTATUS')        
        self.sendATandWait('AT+CIPQSEND=1')  # Enable quick sending mode
        
        limitPaquet = 0
        
        result = re.search(r'(\d+)', self.sendATandWait('AT+CIPSEND?'))
        if result != None :
            limitPaquet = int(result.group(1))
                
        if limitPaquet > 0 :
                                    
            tailleData = len(data2send)
                            
            for i in range(0 , tailleData , limitPaquet) :
                tailleEnvoi = limitPaquet
        
                if i + tailleEnvoi > tailleData :
                    tailleEnvoi = tailleData - i 
                
                nbEssai = 10
                
                while '>' not in self.sendATandWait('AT+CIPSEND={}'.format(tailleEnvoi) , sleepTime=0.05) :
                    time.sleep(0.5)
                    if nbEssai == 0 :
                        print("Je break")
                        break 
                    else :
                        self.sendATandWait('AT+CIPSTATUS') 
                        nbEssai -= 1
                else :
                    print("J'ai lu \">\" !!")
                
                self.sendATandWait(data2send[i:i + tailleEnvoi])
                time.sleep(1)

            
            if self.DEBUG_MODE :
                print("Maintenant j'écoute le modem...")
            
            reponse = ""
            
            # On veut limiter la durée du d/l à 5 minutes sans se baser sur l'heure qui risque de changer ...
            duree_download = 60 * 5
            pause = 0.1
            timeout_download = int(round(duree_download / pause))
            
            for _ in range(0, timeout_download) :
                r = self.ser.read(1024 * 4)                
                reponse += r
                if len(r) > 0 :
                    print("Je lis {} octets".format(len(r)))
                if 'r:ok' in r or 'CLOSED' in r :
                    break
                time.sleep(pause)
                
#             print("Le modem m'écrit %s%s%s" % (bcolors.OKGREEN , reponse , bcolors.ENDC))
            
            self.sendATandWait('AT+CIPSHUT')
            
            contentType = body = ""
            
            reponse = reponse.strip()
            
#             print( reponse[0:500] )

#             with open('/tmp/workfile', 'a') as f:
#                 f.write(reponse)
                        
            if reponse[-6:] == 'CLOSED' :
                index_frontiere_header_body = reponse[0:3000].find('\r\n\r\n')
                
                if index_frontiere_header_body > 0 :
                    header = reponse[0:index_frontiere_header_body] + '\r\n'
                    body = reponse[index_frontiere_header_body + 4:len(reponse) - 8]
                    
                    print("Je vois header : {}{}{}<=".format(bcolors.OKRED , header , bcolors.ENDC))
                                    
                    result = re.search(r'[Cc]ontent-[Tt]ype: ([\w\W]+)\r\n', header)
                    if result != None :
                        contentType = result.group(1)
#                         print(contentType)
                    
            return (contentType , body)
        
        return (None, None)
    
    
    
    def fermerCnxServeur(self , etteindreModem=False):
        
        if etteindreModem :
            if 'OK' in self.sendATandWait('AT') :
                print("{} : Modem allumé, extinction par GPIO".format(time.strftime("%Y-%m-%d %H:%M")))
                self.turnTheModuleOnOrOff()
        
        self.ser.close()
        
    def envoyerNouvellesDonnee(self):
        
        if self.ouvrirCnxServeur() :
                
                timeout = time.time() + (60 * 15)        
                
                for _ in range (0 , 24):
                    
                    if time.time() > timeout :
                        break
                            
                    data2send = self.gestionnaireConnection.getRequeteRequeteServer()        
                    contentType , body = self.envoyerRequeteCIP(data2send)
                    newData = self.gestionnaireConnection.gereReponse(contentType, body , limiteNbReponse=1440 * 3)
                    
#                     print("Nouvelles données à envoyer {}".format(newData))
                    
                    if newData != None :
                        contentType , body = self.envoyerRequeteCIP(self.gestionnaireConnection.getRequeteRequeteServer(newData))
        #                 print("%s %s" % (contentType, body))
                    else :
                        # Pas de nouvelles données, je sors
                        break
        

class GestionnaireEnvoiData :
    
    portCOM = "/dev/ttyAMA0"
    
    def __init__(self , portCOM="/dev/ttyAMA0"):
        self.portCOM = portCOM
        self.gege = gestionnaireConnection()
        self.cnx = GestionnaireConnection3G(self.portCOM , gestionnaireConnection=self.gege)
                
    def testSiDemandeConnect(self):
        self.cnx.readSMS()        
        self.cnx.fermerCnxServeur()    
    
    def faitTonBoulot(self):
                
        if self.cnx.reveillerModem() :
            self.cnx.envoyerNouvellesDonnee()
        
        self.cnx.fermerCnxServeur()

if __name__ == '__main__':
    
    pid = str(os.getpid())
    pidfile = "/var/run/connect3G.pid"
    
    if os.path.isfile(pidfile):
        diff_ts_lock = abs(time.time() - os.path.getctime(pidfile))
        
#       Si le lock a plus de 10 minutes, on l'ignore        
                
        if diff_ts_lock < 600 * 2 :
            print "%s already exists (%s sec), exiting" % (pidfile, diff_ts_lock)
            sys.exit()
        else :
            print("Le fichier de lock existe mais {}".format(diff_ts_lock))
    else:
#         print("Pas de fichier de lock")
        file(pidfile, 'w').write(pid)
    
    if '-q' in sys.argv :
        GestionnaireEnvoiData().testSiDemandeConnect()
    else :
        GestionnaireEnvoiData().faitTonBoulot()

    if os.path.isfile(pidfile):
        os.unlink(pidfile)
