class TestMessageForm(forms.Form):
    
    topic = forms.CharField(label='topic', max_length=100)
    message = forms.CharField(label='message', max_length=200)
    

    def __init__(self, *args, **kwargs):
        self.user = kwargs.pop('user', None)
        super(TestMessageForm, self).__init__(*args, **kwargs)

    def clean_topic(self):
        topic = self.cleaned_data.get('topic')
        try:
            split= topic.split(".")
            account= split[0]
            device = split[1]
            channel= split[3]
            msgformat= split[2]
            
            
        
            
        except:
            raise forms.ValidationError(_('%(string)s is not of at least 4 elements separated by .'),
                  params={'string': topic},
                  )   
        
                   
        if not str(self.user.id) == account:
                raise forms.ValidationError(_('%(string)s does not start with your accountID .'),
                  params={'string': topic},
                  )
        
       
        return topic class Device(models.Model):
    account = models.ForeignKey(User, on_delete=models.CASCADE,editable=False)
    device_id=models.CharField(unique=True,max_length=50,validators=[alphanumeric],help_text="device should publish to mqtt on account_name/deviceID/appID/channelID")
    device_desc = models.CharField(max_length=50,blank=True)
    section = models.ForeignKey(Section, on_delete=models.CASCADE)
    group=models.CharField(max_length=50,blank=True)
    subgroup=models.CharField(max_length=50,blank=True)
    latitude=models.FloatField(blank=True,null=True)
    longitude=models.FloatField(blank=True,null=True)
    model_name = models.CharField(max_length=50,blank=True)
    install_date = models.DateTimeField('install_date',default=timezone.now)


    def __str__(self):
        return self.device_id def constructStatusList(request):
    #statusList is list of status with error messages also used in device/testMessage view

    rabbitMQTest=testConnectToRabbitMQ 
    grafanaUpTest=testGrafanaUp 
    grafana_user=GrafanaUser(request.user.id, request.user.username,"not_used",request.user.email)
    grafanaUserTest=grafana_user.exists()
    grafanaUserTest=testObj("Grafana User",grafana_user.exists(),"")
    influxTest=createInfluxDB(request.user) #creates influxdb if doesnt exist
        #grafanaDataSourceTest=addDataBaseToGrafana(influxTest,grafanaUserTest.message,request.user) 
    if not(grafana_user.get_orgID):
        grafana_user.add_to_own_org()
        grafana_user.fix_permissions()
    grafanaDataSourceTest=testObj("GrafanaDataSource",grafana_user.add_datasource(),"")
        
    status_list=(rabbitMQTest,grafanaUpTest,grafanaUserTest,influxTest,grafanaDataSourceTest)
    
    return status_list class MqttData(object):
    def __init__(self, topic,message):
        self.topic=topic
        self.message=message class TopicData(object):
    def __init__(self, topic):
        
        split= topic.split(".")
        self.account= split[0]
        self.device = split[1]
        self.channel= split[3]
        self.msgformat= split[2] def processMessages(mqttData):
    '''
    this function must be sufficiently robust to process any message
    tests for various formats. Looks for timestamp, checks for presence
    of valid channel or hooks on database will be used to further enrich data  
    
    '''
    
    #result=MqttErrors()
    mqttChecksList=[]
    
    
    try:
        t=TopicData(mqttData.topic)
        #result.topicFormat="OK"
        output=MqttCheck("TopicFormat","OK","")
        mqttChecksList.append(output)
    except:
        LOGGER.warning('Bad topic format %s', mqttData.topic)
        output=MqttCheck("TopicFormat","Failed","Topic must be of format */*/*/#")
        mqttChecksList.append(output)
        return mqttChecksList
    data={}
    #first check for simple numeric value (it would also parse succesfully as json!)
    
   
    
    try:
        value=float (mqttData.message)
        data['value']=value
        #result.format='float'
        LOGGER.debug('message float format%s', mqttData.topic)
        output=MqttCheck("Message Format","OK","Float")
        mqttChecksList.append(output)
    except:
        #then check for json   
        
        try:
            #eliminate data=json.loads in production...
            str_response = mqttData.message.decode('utf-8')
            data=json.loads(str_response)
            LOGGER.debug('message json format %s', mqttData.topic)
            output=MqttCheck("Message Format","OK","JSON")
            mqttChecksList.append(output)
        except:
            #test for Kura messages
            try:
                data=decodeKuraPayload(mqttData.message)
                #result.format='KURA'
                LOGGER.debug('kura decoding complete %s', mqttData.topic)
                output=MqttCheck("Message Format","OK","Kura")
                mqttChecksList.append(output)
            except:
                #finally for simple text
                try:
                    data['text']= str(strip_tags(mqttData.message))
                    #result.format='text'  
                    LOGGER.debug('message text format %s', mqttData.topic) 
                    output=MqttCheck("Message Format","Failed","Message will be stored as text")
                    mqttChecksList.append(output)
                except:
                    LOGGER.warning('invalid message format %s', mqttData.topic)
                    output=MqttCheck("Message Format","Failed","Unable to parse message")
                    mqttChecksList.append(output)
                    return mqttChecksList
    
              
    tags={}
    #this defines the elasticsearch index or influxDatabase we will send to.       
    username=User.objects.get(id=t.account)
    print ("USERNAME IS %s",username)
    index= "dab"+str(username)
    config=settings.DATASTORE
    if (config=='ELASTICSEARCH'):
        initializeElasticIndex(index)
    #try to parse timestamp from message, if fails current time
 
    try:
        timeStr=data['timestamp']
    except:
        timeStr=None 
    
    time_data=TimeData(timeStr)
    mqttChecksList.append(time_data)
    #result.timestamp= time_data.errorMessage 
    data['timestamp']= str(time_data.t_object)   
    #parse hooks from json message
    try:
        listOfHooks=data['hooks']
        LOGGER.debug('Hooks found %s', mqttData.topic)
        tags.update(processHooks(listOfHooks))
        #remove hooks list from message data array, leave hooks=True as data field, to ensure we have at least one data field
        data['hooks']="True"
        output=MqttCheck("Hooks found","OK","")
        mqttChecksList.append(output)
    except:
        LOGGER.debug('No hooks found %s', mqttData.topic)
        output=MqttCheck("No Hooks found","OK","")
        mqttChecksList.append(output)
    #retrieve channel and device id from topic
    tags['device_id']=t.device
    tags['channel_id']=t.channel   
    #retrieve channel object from database
    
    ch=getChannel(t)
    
    if not (ch=="false"):
        tags.update(enrichChannel(ch))
        tags.update(addChannel_Tags(ch))
        checkAlarms(data,"value",ch)
    
        #add time related tags, hour,week etc
        tags.update(addTimeTags(time_data.t_object,ch))
        #add custom time elapsed time data for rfid 
        if (ch.elapsed_since_same_ch=='True'):
            data.update(getTimeElapsedInflux(index,t.device,t.channel,data))
        if (ch.elapsed_since_diff_ch=='True'):
            data.update(getTimeElapsedTagOnDifferentInflux(index,t.device,t.channel,data))
    
    result=len(tags)
    output=MqttCheck("Tags found","OK",result)
    mqttChecksList.append(output)
    LOGGER.debug("sending to index %s,", index)
    result=sendToDB(index,data,tags)
    if result==True:
        output=MqttCheck("Send Data","OK",result)  
    else:
        output=MqttCheck("Send Data","Failed",result)
    mqttChecksList.append(output) 
    return mqttChecksList def createLDAPDevice(new_device,password):
    #returns true or false uses LDAP3  
    
    try:
        uidNumber= str(new_device.id + 1000000)
        con= getLDAPConn()
        dn= str("cn=")+str(new_device.device_id)+str(",")+str(settings.AUTH_LDAP_USERS_OU_DN)
        result=con.add(dn, 'inetOrgPerson', {
            'givenName': 'device', 
            'sn': 'device',
            'mail': 'dontuse@mail.com',
            'cn': new_device.device_id,
            'displayName': new_device.id,
            'uid': new_device.device_id,
            'userPassword':password,
            })
        con.unbind()
        if result:
            logger.info('create ldap device %s',result)
        else:
            logger.info('unable to create device (possibly device already exists)')
        return result
    except:
        logger.info('unable to create ldap device ')
        return False def getLDAPConn():
    #ldap3
    dn=settings.AUTH_LDAP_BIND_DN
    password= settings.AUTH_LDAP_BIND_PASSWORD
    conn=getLDAPConnWithUser(dn, password)
    return conn def addToLDAPGroup(user_name,group_name):
    
    #returns true or false uses LDAP3  
    con= getLDAPConn()
    group_dn= str("cn=")+str(group_name)+str(",")+str(settings.AUTH_LDAP_GROUPS_OU_DN)
    logger.info('add user %s ToLdapgroup %s',user_name,group_name)
    
    result=con.modify(group_dn,
                      {'memberUid': (MODIFY_ADD, [user_name])
    })
    
    logger.info ("add group result is %s", result )
    con.unbind()
    return result def resetLDAPpassword(username,new_password):
    #returns True or False uses LDAP3  
    user_dn= str("cn=")+str(username)+str(",")+str(settings.AUTH_LDAP_USERS_OU_DN)
    try:
        con= getLDAPConn()
        #group_dn= str("cn=")+str(group_name)+str(",")+str(settings.AUTH_LDAP_GROUPS_OU_DN)
        result=con.modify(user_dn,
                      {'userPassword': (MODIFY_REPLACE, [new_password])
                       })
    
   
        logger.info('reset LDAP password for: %s', user_dn) 
        logger.info('result of psw reset: %s', result)
        con.unbind()
        return result
    
    except: 
        logger.info('unable to reset password: %s')
        return False def getLDAPConnWithUser(dn,password):
    #ldap3
    #returns bound connection object if user authenticated
    if settings.LDAP3['validate_certs']:
        validate=ssl.CERT_REQUIRED
    else:
        validate=ssl.CERT_NONE
        
    tls = Tls(validate=validate, version=ssl.PROTOCOL_TLSv1, ca_certs_file=settings.LDAP3['path_to_ca_cert'])
    server = Server(settings.LDAP3['host'],port=settings.LDAP3['port'], get_info=ALL,tls=tls)
    logger.info('Connecting to ldap as %s at %s',dn,settings.LDAP3['host'] )
   
    con = Connection(server,dn,password,auto_bind=False,raise_exceptions=settings.DEBUG)
    con.open()
    
    if settings.LDAP3['use_start_tls']:
        con.start_tls()
    con.bind()  
        
    return con def getLastInflux(index,device_id,channel_id):
    #returns last influx line sent 
    query="SELECT * from \"mqttData\" WHERE \"device_id\" = \'"+str(device_id)+"\' AND \"channel_id\" = \'"+str(channel_id)+"\' ORDER BY time DESC LIMIT 1"
    results=getQueryInflux(index,query)
    return results def connectToMqtt(username,password,topic,payload):
    #connects to MQTT (for testing)
    global loop_flag
    global outputRC
    mqttc=mqtt.Client()
    if settings.MQTT['verify_certs']:
        cert_reqs=ssl.CERT_REQUIRED
    else:
        cert_reqs=ssl.CERT_NONE
        mqttc.tls_insecure_set(True)
    
    mqttc.connected_flag=False #
    if settings.MQTT['use_ssl']:
        mqttc.tls_set(ca_certs=settings.MQTT['path_to_ca_cert'], certfile=None, keyfile=None, cert_reqs=cert_reqs,tls_version=ssl.PROTOCOL_SSLv23, ciphers=None)
    else:
        tls=None
    print("username:%s,password:%s",username,password)
    mqttc.username_pw_set(username, password)
    mqttc.on_message = on_message
    mqttc.on_connect = on_connect
    mqttc.on_publish = on_publish
    mqttc.on_subscribe = on_subscribe
    
    loop_flag=1
    
    try:
        mqttc.connect(settings.MQTT['host'], port=settings.MQTT['port'], keepalive=60, bind_address="")
    except Exception as e: 
        logger.error("connection error %s",e)
        loop_flag=0
        outputRC=100
    mqttc.loop_start()#Stop loop     
    #loop until we receive connack
    while (loop_flag==1):
        time.sleep(0.01)
    mqttc.disconnect()
    mqttc.loop_stop()
    
    #infot=mqttc.publish(topic, payload=None, qos=0, retain=False)
    ''' CONNACK CODES
    0    Connection Accepted
    1    Connection Refused, unacceptable protocol version
    2    Connection Refused, identifier rejected
    3    Connection Refused, Server unavailable
    4    Connection Refused, bad user name or password
    5    Connection Refused, not authorized
    '''
    
    return outputRC
from builtins import str
from builtins import range
from django.http import HttpResponse,HttpResponseNotFound
from django.template import loader
from django.shortcuts import get_list_or_404
from django.shortcuts import render
from .forms import TestMessageForm
from .models import Device
from django.conf import settings
from django.contrib.auth.decorators import login_required
from stack_configs.stack_functions import constructStatusList
from stack_configs.mqtt_functions import MqttData,TopicData,processMessages
from stack_configs.ldap_functions import createLDAPDevice,getLDAPConn,addToLDAPGroup,resetLDAPpassword,getLDAPConnWithUser
from stack_configs.influx_functions import getLastInflux
from stack_configs.mqtt_paho_functions import connectToMqtt
import string
import random
import logging
       
        
        output=""
        topicFormat= str(mydevice.account.id)+"."+str(mydevice.device_id)+".*.*"
        topic=str(request.user.id)+"."+str(mydevice.device_id)+".test.1"
        logger.debug("trying to send mqtt for topic %s",topic)
        payload={"test"}
      
        mqttConnResult=connectToMqtt(mydevice.device_id,password,topic,payload)
        
        context = {
            'content':output,
            'username': mydevice.device_id,
            'password': password,
            'mqttConnResult': mqttConnResult,
            'mqttHost':settings.MQTT['host'],
            'mqttPort':settings.MQTT['port'],
            'caFilePath':settings.MQTT['path_to_ca_cert'],
            'topicFormat': topicFormat,
            'has_permission':request.user.is_authenticated,
            'is_popup':False,
            'title':'Reset device password',
            'site_title':'zibawa',
            'site_url':settings.SITE_URL
         
        }
    elif(resetLDAPpassword(mydevice.device_id,password)):
                      
                        
        output=""
        topicFormat= str(mydevice.account.id)+"."+str(mydevice.device_id)+".*.*"
        topic=str(mydevice.account.id)+"."+str(mydevice.device_id)+".test.1"
        logger.debug("trying to send mqtt for topic %s",topic)
        payload={"test"}
        mqttConnResult=connectToMqtt(mydevice.device_id,password,topic,payload)
        
        
        context = {
            'content':output,
            'username': mydevice.device_id,
            'password': password,
            'mqttConnResult': mqttConnResult,
            'mqttHost':settings.MQTT['host'],
            'mqttPort':settings.MQTT['port'],
            'caFilePath':settings.MQTT['path_to_ca_cert'],
            'topicFormat': topicFormat,
            'has_permission':request.user.is_authenticated,
            'is_popup':False,
            'title':'Reset device password',
            'site_title':'zibawa',
            'site_url':settings.SITE_URL
         
        }
    else:    
        context = {
            'content':"We were unable to create your device password. Try a different device ID or contact your administrator",
            'username': mydevice.device_id,
            'password': "",
            'mqttConnResult': None,
            'mqttHost':settings.MQTT['host'],
            'mqttPort':settings.MQTT['port'],
            'caFilePath':settings.MQTT['path_to_ca_cert'],
            'topicFormat': "",
            'has_permission':request.user.is_authenticated,
            'is_popup':False,
            'title':'Unable to reset device password',
            'site_title':'zibawa',
            'site_url':settings.SITE_URL
         
        }
    return HttpResponse(template.render(context, request))




def id_generator(size=10, chars=string.ascii_uppercase + string.digits):
    
    return ''.join(random.choice(chars) for _ in range(size))


    


@login_required(login_url='/admin/login/?next=/admin/')
def testMessage(request):
    #this allows user to feed messages into the same process
    #functions that we use in the automatic mqtt processing
    status_list=constructStatusList(request)
    # if this is a POST request we need to process the form data
    if request.method == 'POST':
        # create a form instance and populate it with data from the request:
        form = TestMessageForm(request.POST,user=request.user)
        # check whether it's valid:
        if form.is_valid():
            #check that user is sending data on their own account
            logger.debug('form valid, processing message')
            #encode utf-8 because this is the way text messages are received from rabbitmq 
            testMsg = MqttData(form.cleaned_data['topic'],form.cleaned_data['message'].encode('utf-8'))
            mqttChecksList=processMessages(testMsg)
            myTopic=TopicData(form.cleaned_data['topic'])
            
            lastInflux= getLastInflux(request.user,myTopic.device,myTopic.channel)
            context = {
                
                'has_permission':request.user.is_authenticated,
                'is_popup':False,
                'form':form,
                'mqttChecksList':mqttChecksList,
                'title':'Send test message',
                'site_title':'zibawa',
                'status_list':status_list,
                'site_url':settings.SITE_URL
         
                       }
                        
            return render(request,'devices/testMessageForm.html',context)
        logger.debug('form not valid, reprinting form')

    # if a GET (or any other method) we'll create a blank form
    else:

        form = TestMessageForm(user=request.user)