import ast
import json
import requests
import base64
import struct
from struct import pack
import time
from django.contrib import messages
from django.db import models
from django.urls import reverse
from django.core.validators import MinValueValidator, MaxValueValidator

from apps.main.models import Configuration

MODE_VALUE = (
        ('position', 'Position'),
        ('speed', 'Speed'),
        ('table', 'Table')
    )

class PedestalConfiguration(Configuration):

    mode = models.CharField(
        verbose_name='Mode',
        max_length=10,
        choices=MODE_VALUE,
        null=False,
        blank=False
    )

    axis = models.CharField(
        verbose_name="Axis",
        max_length=100,
        default='az',
        blank=False, 
        null=False,
        help_text="Please separate the values with commas when using table mode"
    )

    speed = models.CharField(
        verbose_name='Speed',
        max_length=100,
        blank=False, 
        null=False,
        default=6
    )

    angle = models.CharField(
        verbose_name="Angle(s)",
        max_length=100,
        default='0',
        blank=False, 
        null=False,
        help_text="Please separate the values with commas when using table mode"
    )

    min_value = models.FloatField(
        verbose_name='Min angle',
        validators=[MinValueValidator(-5), MaxValueValidator(185)],
        blank=False, 
        null=False,
        default=0
    )

    max_value = models.FloatField(
        verbose_name='Max angle',
        validators=[MinValueValidator(-5), MaxValueValidator(185)],
        blank=False, 
        null=False,
        default=40
    )

    class Meta:
        db_table = 'pedestal_configurations'
    
    def __str__(self):
        if self.mode=='position':
            return u'Position: {}º {}'.format(self.angle, self.axis.upper())
        if self.mode=='speed':
            return u'Speed: {}º/s {}'.format(self.speed, self.axis.upper())
        if self.mode=='table':
            axis = [x.strip().upper() for x in self.axis.split(',')]
            speeds = [float(x.strip()) for x in self.speed.split(',')]
            table = [float(x.strip()) for x in self.angle.split(',')]
            return u'Table: Axis {}, Speed {}º/s, Steps {}'.format(axis, speeds, table)

    @property
    def label(self):
        return str(self)

    def get_absolute_url_plot(self):
        return reverse('url_plot_pedestal_pulses', args=[str(self.id)])

    def request(self, cmd, method='get', **kwargs):

        req = getattr(requests, method)(self.device.url(cmd), **kwargs)
        payload = req.json()

        return payload

    def status_device(self):

        try:
            payload = requests.get(self.device.url())
            
            if payload:
                self.device.status = 1
            elif payload['status']=='disable':
                self.device.status = 2
            else:
                self.device.status = 1
                self.device.save()
                self.message = 'Pedestal status: {}'.format(payload['status'])
                return False
        except Exception as e:
            if 'No route to host' not in str(e):
                self.device.status = 4
            self.device.save()
            self.message = 'Pedestal status: {}'.format(str(e))
            return False

        self.device.save()
        return True

    def reset_device(self, axi, angle):

        try:
            url = self.device.url() + "position?params="
            
            payload_el = {'axis': 'elevation'}
            payload_az = {'axis': 'azimuth'}

            if axi == 'elevation':
                payload_az['position'] = angle
                payload_el['position'] = 0
            elif axi == 'azimuth':
                payload_el['position'] = angle
                payload_az['position'] = 0
            else:
                payload_el['position'] = 0
                payload_az['position'] = 0
            
            json_data_el = json.dumps(payload_el)
            json_data_az = json.dumps(payload_az)

            base64_table_el = base64.standard_b64encode(json_data_el.encode('ascii'))
            base64_table_az = base64.standard_b64encode(json_data_az.encode('ascii'))
            
            r = requests.get(url + base64_table_el.decode('ascii'))
            r = requests.get(url + base64_table_az.decode('ascii'))
            
            if r:
                self.device.status = 3
                self.device.save()
                self.message = 'Pedestal reset'
            else:
                return False

        except Exception as e:
            self.message = 'Pedestal reset: {}'.format(str(e))
            return False

        return True

    def stop_device(self):

        try:
            command = self.device.url() + "stop"
            r = requests.get(command)
            if r:
                self.device.status = 4
                self.device.save()
                self.message = 'Pedestal stopped'
            else:
                self.device.status = 4
                self.device.save()
                return False
        except Exception as e:
            if 'No route to host' not in str(e):
                self.device.status = 4
            else:
                self.device.status = 0
            #self.message = 'Pedestal stop: {}'.format(str(e))
            self.message = "Pedestal can't start, please check network/device connection or IP address/port configuration"
            self.device.save()
            return False

        return True

    def start_device(self):

        AX = {'az':'azimuth', 'el':'elevation'}
        axis = [AX[x.lower().strip()] for x in self.axis.split(',')]
        if len(axis)==1:
            axis = axis[0]

        try:
            if self.mode == 'position':
                url = self.device.url() + "position?params="
                payload = {'axis': axis, 'position': float(self.angle)}
            elif self.mode == 'speed':
                url = self.device.url() + "speed?params="
                payload = {'axis': axis, 'speed': float(self.speed)}
            elif self.mode == 'table':
                url = self.device.url() + "combinedtable?params="
                list_of_floats = [float(x.strip()) for x in self.angle.split(",")]
                byte_table = []
                for x in list_of_floats:
                    temp = bytearray(struct.pack("f", x))
                    byte_table.append(temp[3])
                    byte_table.append(temp[2])
                    byte_table.append(temp[1])
                    byte_table.append(temp[0])
                
                coded_table = base64.standard_b64encode(bytes(byte_table))
                coded_table_ascii = coded_table.decode('ascii')
                speed = [float(x.strip()) for x in self.speed.split(',')]
                payload = {
                    'arraylength': len(speed), 
                    'axis': axis, 
                    'speed': speed, 
                    'bottom': self.min_value,
                    'top': self.max_value,
                    'table': coded_table_ascii
                    }
                self.reset_device(axis[0], list_of_floats[0])
                time.sleep(15)

            json_data = json.dumps(payload)
            print(json_data)
            base64_table = base64.standard_b64encode(json_data.encode('ascii'))
            url += base64_table.decode('ascii')
            print(url)
            r = requests.get(url)

            if r:
                self.device.status = 3
                self.device.save()
                self.message = 'Pedestal configured and started'
            else:
                return False
        except Exception as e:
            if 'No route to host' not in str(e):
                self.device.status = 4
            else:
                self.device.status = 0
            #self.message = 'Pedestal start: {}'.format(str(e))
            self.message = "Pedestal can't start, please check network/device connection or IP address/port configuration"
            self.device.save()
            return False

        return True

    def get_absolute_url_import(self):
        return reverse('url_import_pedestal_conf', args=[str(self.id)])
