import json
import zipfile
from django.shortcuts import render

from rest_framework.views import APIView
from rest_framework.generics import ListAPIView
from rest_framework.response import Response
from rest_framework import status
from .serializers import *
from rest_framework import permissions
from django.core import serializers
from django.http import HttpResponse, JsonResponse

from django.shortcuts import get_object_or_404
from django.http import Http404

from gardens.models import *
from .custom_renderers import *
from .flight_plan_algorithm import *
from gardens.functions import get_weather_data, TreeRowGetTrees
# scab detection 
from .functionsNeuralNetworks.scabDetectionModel import *
from .functionsNeuralNetworks.pearDetectionModel import *
from .functionsNeuralNetworks.objectDetectionModel import *
from .planformat import *
# Create your views here.

class DroneBasePingApiView(ListAPIView):
    def get_dronebase(self, device_auth_key):
        '''
        Helper method to check if drone base with the given authentication key exists
        '''
        try:
            return Devices.objects.get(key = device_auth_key)
        except Devices.DoesNotExist:
            return None
    def get_missions(self, block_id):
        '''
        Helper method to identify if the specific drone has an mission associated with it
        '''
        try:
            return Missions.objects.filter(block_id = block_id, status = 0).order_by('generated_date')
        except Missions.DoesNotExist:
            return None
    def get_flights(self,mission_id):
        '''
        Helper method to identify if the specific drone has a flight that has been scheduled
        (in the future //// just checks if there is a flight that been scheduled and its start_date is > than current time)
        '''
        try:
            return Flights.objects.filter(mission_id = mission_id, status = 1).order_by('start_date')
        except Flights.DoesNotExist:
            return None
    def get_weather_data(self, block_id):

        block = Blocks.objects.get(pk = block_id)

        if block.private_meteostation_id != None:
            try:
                import json
                blockMeteostation = Meteostations.objects.get(pk = block.private_meteostation_id)
                blockMeteostation.last_forecast = json.dumps(get_weather_data(blockMeteostation), indent=4, sort_keys=True, default=str)
                blockMeteostation.save()
                return get_weather_data(blockMeteostation)
            except Exception as e: 
                print(e)
                return None
        else:
            return None
        
    def get_queryset(self):
        return
    def get(self, request, device_auth_key, *args, **kwargs):
        '''
        Retrieves flights id's that are supposed to be flown out
        '''
        device = self.get_dronebase(device_auth_key)
        if not device:
            return Response(
                {"res": "Device with this device key does not exists"},
                status=status.HTTP_400_BAD_REQUEST
            )
        if device.block_id == None:
            return Response(
                {"res": "Device with this device key does not have a garden block associated with it"},
                status=status.HTTP_400_BAD_REQUEST
            )
        missions = self.get_missions(device.block_id)
        if not missions:
            return Response(
                {"res": "Device with this device key does not have any active missions associated with it"},
                status=status.HTTP_400_BAD_REQUEST
            )
        scheduledFlights = Flights.objects.none()
        for mission in missions:
            flights = self.get_flights(mission.id)
            if flights:
                scheduledFlights |= flights
        if not scheduledFlights:
            return Response(
                {"res": "Device with this device key does not have any flights planned at this time"},
                status=status.HTTP_400_BAD_REQUEST
            )
        #meteoData = self.get_weather_data(device.block_id)
        #if not meteoData:
        #    return Response(
        #        {"res": "This device has no meteostation data to use or meteostation was setup incorrectly"},
        #        status=status.HTTP_400_BAD_REQUEST
        #    )
        #if meteoData['precip_rate'] > 1:
        #    return Response(
        #        {"res": "Current Weather is not appropriate for flying a drone"},
        #        status=status.HTTP_400_BAD_REQUEST
        #    )
        serializer = FlightSerializer(scheduledFlights, many = True)

        return Response(serializer.data, status=status.HTTP_200_OK)

class FlightInfosApiView(APIView):
    def get_object(self, flight_id):
        '''
        Helper method to get the object with id
        '''
        try:
            flight = Flights.objects.get(pk = flight_id, status = 1)
            return FlightPlans.objects.get(pk = flight.plan_id)
        except Flights.DoesNotExist:
            return None

    def get_dronebase(self, device_auth_key):
        '''
        Helper method to check if drone base with the given authentication key exists
        '''
        try:
            return Devices.objects.get(key = device_auth_key)
        except Devices.DoesNotExist:
            return None

    def get(self, request, device_auth_key, flight_id, *args, **kwargs):
        '''
        Retrieves flight_plan with given blocks_id, sequence_num
        '''
        device = self.get_dronebase(device_auth_key)
        if not device:
            return Response(
                {"res": "Device with this device key does not exists"},
                status=status.HTTP_400_BAD_REQUEST
            )
        plan_instance = self.get_object(flight_id)
        if not plan_instance:
            return Response(
                {"res": "This flight either does not exist or it has been flown already"},
                status=status.HTTP_400_BAD_REQUEST
            )

        serializer = PlanSerializer(plan_instance)
        return Response(serializer.data, status=status.HTTP_200_OK)

class FlightInProgressApiView(APIView):
    def get_object(self, flight_id):
        '''
        Helper method to get the object with given id
        '''
        try:
            return Flights.objects.get(pk = flight_id, status = 1)
        except Flights.DoesNotExist:
            return None
    def get_dronebase(self, device_auth_key):
        '''
        Helper method to check if drone base with the given authentication key exists
        '''
        try:
            return Devices.objects.get(key = device_auth_key)
        except Devices.DoesNotExist:
            return None
    
    def get(self, request, device_auth_key, flight_id, *args, **kwargs):
        '''
        Changes the status of the flight to 2 (in progress)
        '''
        device = self.get_dronebase(device_auth_key)
        if not device:
            return Response(
                {"res": "Device with this device key does not exists"},
                status=status.HTTP_400_BAD_REQUEST
            )
        flight = self.get_object(flight_id)
        if not flight:
            return Response(
                {"res": "This flight either does not exist or it has been flown already"},
                status=status.HTTP_400_BAD_REQUEST
            )
        from datetime import datetime
        now = datetime.now()

        flight.status = 2
        flight.start_date = now
        flight.save()
        
        return Response({"res": "Flight start date has been updated and flight status changed"} , status=status.HTTP_200_OK)

class FlightCompletedApiView(APIView):
    def get_object(self, flight_id):
        '''
        Helper method to get the object with given id
        '''
        try:
            return Flights.objects.get(pk = flight_id, status = 2)
        except Flights.DoesNotExist:
            return None
    def get_dronebase(self, device_auth_key):
        '''
        Helper method to check if drone base with the given authentication key exists
        '''
        try:
            return Devices.objects.get(key = device_auth_key)
        except Devices.DoesNotExist:
            return None
    
        
    def get(self, request, device_auth_key, flight_id, *args, **kwargs):
        '''
        Retrieves flight_plan with given blocks_id, sequence_num
        '''
        device = self.get_dronebase(device_auth_key)
        if not device:
            return Response(
                {"res": "Device with this device key does not exists"},
                status=status.HTTP_400_BAD_REQUEST
            )
        flight = self.get_object(flight_id)
        if not flight:
            return Response(
                {"res": "This flight either either has not been started, has been finished or does not exist"},
                status=status.HTTP_400_BAD_REQUEST
            )
        from datetime import datetime
        now = datetime.now()

        flight.status = 3
        flight.end_date = now
        flight.save()

        if FlightPlans.objects.filter(mission_id = flight.mission_id).count() == Flights.objects.filter(mission_id = flight.mission_id, status = 3).count():
            mission = Missions.objects.get(pk = flight.mission_id)
            mission.status = 1
            mission.closed_date = now
            mission.save()
        
        
        return Response({"res": "Flight has been finished"} , status=status.HTTP_200_OK)

class FlightFailedApiView(APIView):
    def get_object(self, flight_id):
        '''
        Helper method to get the object with given id
        '''
        try:
            return Flights.objects.get(pk = flight_id, status = 2)
        except Flights.DoesNotExist:
            return None
    def get_dronebase(self, device_auth_key):
        '''
        Helper method to check if drone base with the given authentication key exists
        '''
        try:
            return Devices.objects.get(key = device_auth_key)
        except Devices.DoesNotExist:
            return None
    
    def get(self, request, device_auth_key, flight_id, *args, **kwargs):
        '''
        Retrieves flight_plan with given blocks_id, sequence_num
        '''
        device = self.get_dronebase(device_auth_key)
        if not device:
            return Response(
                {"res": "Device with this device key does not exists"},
                status=status.HTTP_400_BAD_REQUEST
            )
        flight = self.get_object(flight_id)
        if not flight:
            return Response(
                {"res": "This flight either either has not been started, has been finished or does not exist"},
                status=status.HTTP_400_BAD_REQUEST
            )

        flight.delete()
        
        return Response({"res": "Flight has been canceled therefore deleted"} , status=status.HTTP_200_OK)

class ActiveMissionsApiView(APIView):
    def get_dronebase(self, device_auth_key):
        '''
        Helper method to check if drone base with the given authentication key exists
        '''
        try:
            return Devices.objects.get(key = device_auth_key)
        except Devices.DoesNotExist:
            return None
    def get_queryset(self, device_block_id):
        try:
            return Missions.objects.filter(block_id = device_block_id, status = 0).order_by('generated_date')
        except Missions.DoesNotExist:
            return None

    def get(self, request, device_auth_key, *args, **kwargs):
        '''
        Retrieves flight_plan with given blocks_id, sequence_num
        '''
        device = self.get_dronebase(device_auth_key)
        if not device:
            return Response(
                {"res": "Device with this device key does not exists"},
                status=status.HTTP_400_BAD_REQUEST
            )
        if device.block_id == None:
            return Response(
                {"res": "Device with this device key does not have a garden block associated with it"},
                status=status.HTTP_400_BAD_REQUEST
            )
        missions = self.get_queryset(device.block_id)
        if not missions:
            return Response(
                {"res": "Device with this device key does not have any active missions associated with it"},
                status=status.HTTP_400_BAD_REQUEST
            )

        serializer = MissionSerializer(missions, many = True)

        return Response( serializer.data , status=status.HTTP_200_OK)

class MissionFlightPlanApiView(APIView):
    def get_dronebase(self, device_auth_key):
        '''
        Helper method to check if drone base with the given authentication key exists
        '''
        try:
            return Devices.objects.get(key = device_auth_key)
        except Devices.DoesNotExist:
            return None
    def get_queryset(self, mission_id):
        try:
            return FlightPlans.objects.filter(mission_id = mission_id)
        except FlightPlans.DoesNotExist:
            return None
    def get_status(self, plan_id):
        try:
            Flights.objects.get(plan_id = plan_id, status = 3)
            return True
        except Flights.DoesNotExist:
            return False
    def get(self, request, device_auth_key, mission_id, *args, **kwargs):
        '''
        Retrieves mission and its plan with given mission_id and device_auth_key
        '''
        device = self.get_dronebase(device_auth_key)
        if not device:
            return Response(
                {"res": "Device with this device key does not exists"},
                status=status.HTTP_400_BAD_REQUEST
            )
        FlightPlans = self.get_queryset(mission_id)
        if not FlightPlans:
            return Response(
                {"res": "Device with this device key does not have any active missions associated with it"},
                status=status.HTTP_400_BAD_REQUEST
            )
        FlightPlans = self.get_queryset(mission_id)
        plansEdited = []
        for plan in FlightPlans:
            plansEdited += [{"id": plan.id ,"sequence_num": plan.sequence_num, "completed" : self.get_status(plan.id)}]

                
        return Response( plansEdited , status=status.HTTP_200_OK)

class FlightCreateApiView(APIView):
    def get_dronebase(self, device_auth_key):
        '''
        Helper method to check if drone base with the given authentication key exists
        '''
        try:
            return Devices.objects.get(key = device_auth_key)
        except Devices.DoesNotExist:
            return None
    def get_flightPlan(self, plan_id):
        try:
            return FlightPlans.objects.get(pk = plan_id)
        except FlightPlans.DoesNotExist:
            return None
    def get_status(self, plan_id):
        try:
            Flights.objects.get(plan_id = plan_id)
            return True
        except Flights.DoesNotExist:
            return False
    
    def get(self, request, device_auth_key, plan_id, *args, **kwargs):
        '''
        Retrieves flight_plan with given blocks_id, sequence_num
        '''
        device = self.get_dronebase(device_auth_key)
        if not device:
            return Response(
                {"res": "Device with this device key does not exists"},
                status=status.HTTP_400_BAD_REQUEST
            )
        plan = self.get_flightPlan(plan_id)
        if not plan:
            return Response(
                {"res": "This plan does not exist"},
                status=status.HTTP_400_BAD_REQUEST
            )
        exists = self.get_status(plan.id)
        if exists == True:
            return Response(
                {"res": "This plan has already been flown or a flight has already been created"},
                status=status.HTTP_400_BAD_REQUEST
            )

        from datetime import datetime
        now = datetime.now()

        Flights.objects.create(plan_id = plan.id, start_date = now, mission_id = plan.mission_id, status = 1)
                
        return Response( {"res": "Flight created"} , status=status.HTTP_200_OK)

class MissionFlightPlanViewerApiView(APIView):
    def get_dronebase(self, device_auth_key):
        '''
        Helper method to check if drone base with the given authentication key exists
        '''
        try:
            return Devices.objects.get(key = device_auth_key)
        except Devices.DoesNotExist:
            return None
    def get_object(self, plan_id):
        try:
            return FlightPlans.objects.get(pk = plan_id)
        except FlightPlans.DoesNotExist:
            return None

    def get(self, request, device_auth_key, plan_id, *args, **kwargs):
        '''
        Retrieves flight_plan with given blocks_id, sequence_num
        '''
        device = self.get_dronebase(device_auth_key)
        if not device:
            return Response(
                {"res": "Device with this device key does not exists"},
                status=status.HTTP_400_BAD_REQUEST
            )
        plan_instance = self.get_object(plan_id)
        if not plan_instance:
            return Response(
                {"res": "This plan does not exist"},
                status=status.HTTP_400_BAD_REQUEST
            )
        serializer = PlanSerializer(plan_instance)

        return Response( serializer.data , status=status.HTTP_200_OK)

class MissionFlightPlanViewerApiKMLFormatView(APIView):
    def get_dronebase(self, device_auth_key):
        '''
        Helper method to check if drone base with the given authentication key exists
        '''
        try:
            return Devices.objects.get(key = device_auth_key)
        except Devices.DoesNotExist:
            return None
    def get_object(self, plan_id):
        try:
            return FlightPlans.objects.get(pk = plan_id)
        except FlightPlans.DoesNotExist:
            return None

    def get(self, request, device_auth_key, plan_id, *args, **kwargs):
        '''
        Retrieves flight_plan with given blocks_id, sequence_num
        '''
        device = self.get_dronebase(device_auth_key)
        if not device:
            return Response(
                {"res": "Device with this device key does not exists"},
                status=status.HTTP_400_BAD_REQUEST
            )
        plan_instance = self.get_object(plan_id)
        if not plan_instance:
            return Response(
                {"res": "This plan does not exist"},
                status=status.HTTP_400_BAD_REQUEST
            )
        coordinates = plan_instance.flight_plan['geometry']['coordinates'][0]
        trees = Trees.objects.filter(block_id = device.block_id)
        tree_coordinates_array = []
        for tree in trees:
            tree_coordinates_array.append([tree.latitude, tree.longitude])

        planformated = ConvertToKML(plan_instance, tree_coordinates_array, coordinates)

        return Response( planformated , status=status.HTTP_200_OK)
    
class MissionFlightPlanViewerApiArduPilotFormatView(APIView):
    def get_dronebase(self, device_auth_key):
        '''
        Helper method to check if drone base with the given authentication key exists
        '''
        try:
            return Devices.objects.get(key = device_auth_key)
        except Devices.DoesNotExist:
            return None
    def get_object(self, plan_id):
        try:
            return FlightPlans.objects.get(pk = plan_id)
        except FlightPlans.DoesNotExist:
            return None

    def get(self, request, device_auth_key, plan_id, *args, **kwargs):
        '''
        Retrieves flight_plan with given blocks_id, sequence_num
        '''
        device = self.get_dronebase(device_auth_key)
        if not device:
            return Response(
                {"res": "Device with this device key does not exists"},
                status=status.HTTP_400_BAD_REQUEST
            )
        plan_instance = self.get_object(plan_id)
        if not plan_instance:
            return Response(
                {"res": "This plan does not exist"},
                status=status.HTTP_400_BAD_REQUEST
            )
        coordinates = plan_instance.flight_plan['geometry']['coordinates'][0]
        trees = Trees.objects.filter(block_id = device.block_id)
        tree_coordinates_array = []
        for tree in trees:
            tree_coordinates_array.append([tree.latitude, tree.longitude])

        planformated = ConvertToArduPilot(plan_instance, tree_coordinates_array, coordinates)

        return Response( planformated , status=status.HTTP_200_OK)


class BlockTreesApiView(APIView):
    def get_dronebase(self, device_auth_key):
        '''
        Helper method to check if drone base with the given authentication key exists
        '''
        try:
            return Devices.objects.get(key = device_auth_key)
        except Devices.DoesNotExist:
            return None
    def get_queryset(self, block_id):
        try:
            return Trees.objects.filter(block_id = block_id)
        except Trees.DoesNotExist:
            return None
    def get(self, request, device_auth_key, *args, **kwargs):
        '''
        Retrieves flight_plan with given blocks_id, sequence_num
        '''
        device = self.get_dronebase(device_auth_key)
        if not device:
            return Response(
                {"res": "Device with this device key does not exists"},
                status=status.HTTP_400_BAD_REQUEST
            )
        if device.block_id == None:
            return Response(
                {"res": "Device with this device key does not have a garden block associated with it"},
                status=status.HTTP_400_BAD_REQUEST
            )
        trees_instance = self.get_queryset(device.block_id)
        if not trees_instance:
            return Response(
                {"res": "No trees added to block"},
                status=status.HTTP_400_BAD_REQUEST
            )
        serializer = TreeSerializer(trees_instance, many = True)
        
        return Response( serializer.data , status=status.HTTP_200_OK)

class DroneBaseLocationApiView(APIView):
    def get_dronebase(self, device_auth_key):
        '''
        Helper method to check if drone base with the given authentication key exists
        '''
        try:
            return Devices.objects.get(key = device_auth_key)
        except Devices.DoesNotExist:
            return None
    def get_object(self, block_id):
        try:
            return  DroneStations.objects.get(block_id = block_id)
        except DroneStations.DoesNotExist:
            return None
    def get(self, request, device_auth_key, *args, **kwargs):
        '''
        Retrieves flight_plan with given blocks_id, sequence_num
        '''
        device = self.get_dronebase(device_auth_key)
        if not device:
            return Response(
                {"res": "Device with this device key does not exists"},
                status=status.HTTP_400_BAD_REQUEST
            )
        if device.block_id == None:
            return Response(
                {"res": "Device with this device key does not have a garden block associated with it"},
                status=status.HTTP_400_BAD_REQUEST
            )
        station_instance = self.get_object(device.block_id)
        if not station_instance:
            return Response(
                {"res": "DroneBase location has not been added for this devices block"},
                status=status.HTTP_400_BAD_REQUEST
            )
        serializer = DroneStationSerializer(station_instance)
        
        return Response( serializer.data , status=status.HTTP_200_OK)

class DroneBasePlanReceiverApiView(APIView):
    def get_dronebase(self, device_auth_key):
        '''
        Helper method to check if drone base with the given authentication key exists
        '''
        try:
            return Devices.objects.get(key = device_auth_key)
        except Devices.DoesNotExist:
            return None
    def post(self, request, device_auth_key, *args, **kwargs):

        plan_data = request.data
        try: 
            start_coordinates = plan_data[0]
            print(start_coordinates[:2])
        except:
            return Response({"res": "Bad request"}, status=status.HTTP_400_BAD_REQUEST)
        lastpoint = 0
        plans = []
        block_id = self.get_dronebase(device_auth_key).block_id

        monthsLV = ["Janvāris", "Februāris", "Marts", "Aprīlis", "Maijs", "Jūnijs", "Jūlijs", "Augusts", "Septembris", "Oktobris", "Decembris"]
        for x in range(1,len(plan_data)):
            if plan_data[x][:2] == start_coordinates[:2]:
                print(plan_data[lastpoint:x] + start_coordinates)
                plans += [plan_data[lastpoint:x] + [start_coordinates]]
                lastpoint = x
        if len(plans)>=1:
            if block_id != None:
                import datetime
                currentTime = datetime.datetime.now()
                mission = Missions.objects.create(
                    name = f"{monthsLV[currentTime.month-1]} {currentTime.year}",
                    block_id = block_id,
                    stage_id = 1
                )
                for x in range(0,len(plans)):
                    import json
                    geoJSON = {"type":"Feature",
                                "geometry": 
                                {"type": "Polygon",
                                "coordinates":[plans[x]]}}
                    FlightPlans.objects.create(
                        flight_plan = geoJSON,
                        sequence_num = x+1,
                        mission_id = mission.id,
                        block_id = block_id
                    )
                return Response({"res": "New Mission and Flight_plans for that mission have been created"},status=status.HTTP_201_CREATED)
        return Response({"res": "Bad request"}, status=status.HTTP_400_BAD_REQUEST)

class DroneBaseTreeReceiverApiView(APIView):
    def get_dronebase(self, device_auth_key):
        '''
        Helper method to check if drone base with the given authentication key exists
        '''
        try:
            return Devices.objects.get(key = device_auth_key)
        except Devices.DoesNotExist:
            return None
    def post(self, request, device_auth_key, *args, **kwargs):
        tree_data = request.data
        try:
            block_id = self.get_dronebase(device_auth_key).block_id
            if block_id != None:
                try:
                    Trees.objects.create(
                        latitude = tree_data['latitude'],
                        longitude =  tree_data['longitude'],
                        block_id = block_id,
                        width = tree_data['width'],
                        height = tree_data['height'],
                        breed = tree_data['breed'],
                        cultivares_id = tree_data['cultivares_id']
                    )
                except:
                    Trees.objects.create(
                        latitude = tree_data.get('latitude', '0'),
                        longitude =  tree_data.get('longitude', '0'),
                        block_id = block_id,
                        width = tree_data.get('width', '0'),
                        height = tree_data.get('height', '0'),
                        breed = tree_data.get('breed', '0').strip(),
                        cultivares_id = tree_data.get('cultivares_id', '0')
                    )
                return Response({"res": "New Tree Created"}, status=status.HTTP_201_CREATED)
        except:
            return Response({"res": "Bad request"}, status=status.HTTP_400_BAD_REQUEST)

class DroneLocationPingApiView(APIView):
    def get(self, request, flight_id, *args, **kwargs):
        '''
        Retrieves last location of Drone location
        '''
        currentFlight = Flights.objects.get(pk = flight_id)
        if currentFlight.diagnostics != None:
            serializer = FlightDiagnosticsSerializer(currentFlight)
            return Response( serializer.data , status=status.HTTP_200_OK)
        else:
            return Response( {"res": "diagnostic field is empty"}, status=status.HTTP_400_BAD_REQUEST)

    def post(self, request, flight_id, *args, **kwargs):
        drone_data = request.data
        try:
            currentFlight = Flights.objects.get(pk = flight_id)
            if  currentFlight.status == 2:
                try:
                    currentFlight = Flights.objects.get(pk = flight_id)
                    currentFlight.diagnostics = drone_data
                    currentFlight.save()
                except:
                    currentFlight = Flights.objects.get(pk = flight_id)
                    currentFlight.diagnostics = drone_data.get('data', '0')
                    currentFlight.save()
                return Response({"res": "diagnostics field updated"}, status= status.HTTP_200_OK)
        except:
            return Response({"res": "Bad request"}, status=status.HTTP_400_BAD_REQUEST)

class DroneLocationMissionPingApiView(APIView):

    def get(self, request, mission_id, *args, **kwargs):
        '''
        Retrieves last location of Drone location
        '''
        try:
            currentFlightBeingFlown = Flights.objects.get(mission_id = mission_id, status = 2)
        except:
            currentMission = Missions.objects.get(pk = mission_id)
            if currentMission.status == True:
                return Response({"res": "Mission Completed"}, status=status.HTTP_400_BAD_REQUEST)
            else:
                return Response({"res": "No flight is currently in progress on this mission or the mission doesnt exist"}, status=status.HTTP_400_BAD_REQUEST)
        if currentFlightBeingFlown.diagnostics != None:
            serializer = FlightDiagnosticsSerializer(currentFlightBeingFlown)
            return Response( serializer.data , status=status.HTTP_200_OK)
        else:
            return Response( {"res": "diagnostic field is empty"}, status=status.HTTP_400_BAD_REQUEST)
            
class MissionPhotosRecieveApiView(APIView):
    def get_dronebase(self, device_auth_key):
        '''
        Helper method to check if drone base with the given authentication key exists
        '''
        try:
            return Devices.objects.get(key = device_auth_key)
        except Devices.DoesNotExist:
            return None

    def post(self, request, device_auth_key, mission_id, *args, **kwargs):
        photo_data = request.data
        try:
            block_id = self.get_dronebase(device_auth_key).block_id
            try:
                CompletedMission = Missions.objects.get(pk = mission_id, status = True, block_id = block_id)
            except:
                return Response( {"res": "Mission does not exist or mission has not been completed"}, status=status.HTTP_400_BAD_REQUEST)
            print(photo_data)
            Photos.objects.create(
                            image_uri = photo_data.get('image_uri','0'), 

                            GPS = photo_data.get('gps','0'),

                            altitude = photo_data.get('altitude','0'),

                            photo_location_num = photo_data.get('photo_location_num','0'),

                            mission_id = mission_id,

                            tree_id = photo_data.get('tree_id','0'))
            return Response( {"res": "Photo received"}, status=status.HTTP_200_OK)
        except:
            return Response({"res": "either the data is invalid or the device it being sent from is not connected to any block"}, status=status.HTTP_400_BAD_REQUEST)

class MissionPhotoSendApiView(APIView):
    renderer_classes = [JPEGRenderer]
    def get(self, request, photo_id, *args, **kwargs):
        try:
            photo_instance = Photos.objects.get(pk = photo_id).image_uri
        except:
            return Response( {"res": "No photo found"}, status=status.HTTP_400_BAD_REQUEST)
        queryset = photo_instance
        data = queryset
        return Response(data , status=status.HTTP_200_OK)

class MissionPhotosSendApiView(APIView):
    def get(self, request, mission_id, *args, **kwargs):
        photo_instance = Photos.objects.filter(mission_id = mission_id)
        serializer = PhotoSerializer(photo_instance, many = True)
        return Response( serializer.data , status=status.HTTP_200_OK)

class MissionsScabDetectionView(APIView):
    def get_mission(self, mission_id):
        '''
        Helper method to check if mission exists
        '''
        try:
            return Missions.objects.get(pk = mission_id)
        except Missions.DoesNotExist:
            return None
    def get_photos(self, mission_id):
        '''
        Helper method to check if mission has photos associated with it
        '''
        try:
            return Photos.objects.filter(mission_id = mission_id)
        except Photos.DoesNotExist:
            return None
    def get(self, request, mission_id, *args, **kwargs):
        mission = self.get_mission(mission_id)
        if mission == None:
            return Response( {"res": "No Mission found with this mission id"}, status=status.HTTP_400_BAD_REQUEST)
        else:
            photos = self.get_photos(mission_id)
            if photos.exists():
                for photo in photos:
                    scabDetectionModel(photo)
                return Response({"res": "Scab Checked"}, status=status.HTTP_200_OK)
            else:
                return Response( {"res": "No Mission Photos found or it has already been checked for scab"}, status=status.HTTP_400_BAD_REQUEST)
            
class MissionsPearDetectionView(APIView):
    def get_mission(self, mission_id):
        '''
        Helper method to check if mission exists
        '''
        try:
            return Missions.objects.get(pk = mission_id)
        except Missions.DoesNotExist:
            return None
    def get_photos(self, mission_id):
        '''
        Helper method to check if mission has photos associated with it
        '''
        try:
            return Photos.objects.filter(mission_id = mission_id)
        except Photos.DoesNotExist:
            return None
    def get(self, request, mission_id, *args, **kwargs):
        mission = self.get_mission(mission_id)
        if mission == None:
            return Response( {"res": "No Mission found with this mission id"}, status=status.HTTP_400_BAD_REQUEST)
        else:
            photos = self.get_photos(mission_id)
            if photos.exists():
                for photo in photos:
                    pearDetectionModel(photo)
                return Response({"res": "Pear yield changed"}, status=status.HTTP_200_OK)
            else:
                return Response( {"res": "No Mission Photos found or it has already been checked for yield"}, status=status.HTTP_400_BAD_REQUEST)


class EnterpriseRecieveTreeBreedApiView(APIView):
    def get_dronebase(self, device_auth_key):
        '''
        Helper method to check if drone base with the given authentication key exists
        '''
        try:
            return Devices.objects.get(key = device_auth_key)
        except Devices.DoesNotExist:
            return None
    def post(self, request, device_auth_key, *args, **kwargs):
        '''
        Retrieves flights id's that are supposed to be flown out
        '''
        device = self.get_dronebase(device_auth_key)
        if not device:
            return Response(
                {"res": "Device with this device key does not exists",
                "resCode" : 1
                },
                status=status.HTTP_400_BAD_REQUEST
            )
        enterprise = Enterprises.objects.get(pk = device.enterprise.id)
        treeBreedSent = request.data
        if len(treeBreedSent) == 0:
            return Response(
                {"res": "Request does not contain any data",
                "resCode" : 2
                },
                status=status.HTTP_400_BAD_REQUEST
            )
        breedsTitle = treeBreedSent.get('title')
        breedsPlantingDistance = treeBreedSent.get('planting_distance')
        breedsWidth = treeBreedSent.get('width')
        breedsHeight = treeBreedSent.get('height')
        breedsCultivar = treeBreedSent.get('cultivar')
        if breedsTitle == None:
            return Response(
                {"res": "Request does not contain a breed title",
                "resCode" : 3,
                "missing" : 'title'
                },
                status=status.HTTP_400_BAD_REQUEST
            )
        if breedsPlantingDistance == None:
            return Response(
                {"res": "Request does not contain a planting distance",
                "resCode" : 3,
                "missing" : 'planting_distance'
                },
                status=status.HTTP_400_BAD_REQUEST
            )
        if breedsWidth == None:
            return Response(
                {"res": "Request does not contain the tree breeds width",
                "resCode" : 3,
                "missing" : 'width'
                },
                status=status.HTTP_400_BAD_REQUEST
            )
        if breedsHeight == None:
            return Response(
                {"res": "Request does not contain the tree breeds height",
                "resCode" : 3,
                "missing" : 'height'
                },
                status=status.HTTP_400_BAD_REQUEST
            )
        if breedsCultivar == None:
            return Response(
                {"res": "Request does not contain the tree breeds cultivar",
                "resCode" : 3,
                "missing" : 'cultivar'
                },
                status=status.HTTP_400_BAD_REQUEST
            )
        breedsInjection= treeBreedSent.get('injection')
        breedsYields = treeBreedSent.get('yields')
        try:
            EnterpriseSpecificBreeds.objects.create(
                title = breedsTitle,
                width = breedsWidth,
                height = breedsHeight,
                planting_distance = breedsPlantingDistance,
                cultivar_id = breedsCultivar,
                yields = breedsYields,
                injection = breedsInjection,
                enterprise_id = enterprise.id,

            )
            return Response(
                {"res": "Tree Breed was succesfully added",
                "resCode" : 5
                },
                status=status.HTTP_201_CREATED
            )
        except:
            return Response(
                {"res": "Request that has been sent has incorrect data types or blank values",
                "resCode" : 4
                },
                status=status.HTTP_400_BAD_REQUEST
            )

class EntepriseTreeBreedsApiView(APIView):
    def get_dronebase(self, device_auth_key):
        '''
        Helper method to check if drone base with the given authentication key exists
        '''
        try:
            return Devices.objects.get(key = device_auth_key)
        except Devices.DoesNotExist:
            return None
    def get_queryset(self, enterprise_id):
        try:
            return EnterpriseSpecificBreeds.objects.filter(enterprise_id = enterprise_id)
        except EnterpriseSpecificBreeds.DoesNotExist:
            return None
    def get(self, request, device_auth_key, *args, **kwargs):
        device = self.get_dronebase(device_auth_key)
        if not device:
                return Response(
                    {"res": "Device with this device key does not exists",
                    "resCode" : 1
                    },
                    status=status.HTTP_400_BAD_REQUEST
                )
        if device.block_id == None:
            return Response(
                {"res": "Device with this device key does not have a garden block associated with it",
                "resCode" : 2
                },
                status=status.HTTP_400_BAD_REQUEST
                )
        TreeBreeds = self.get_queryset(device.enterprise_id)
        if not TreeBreeds:
            return Response(
                {"res": "No Specific to Enterprise breeds found",
                "resCode" : 3
                },
                status=status.HTTP_400_BAD_REQUEST
                )
        serializer = EnterpriseTreeBreedSerializer(TreeBreeds, many = True)
        return Response( serializer.data , status=status.HTTP_200_OK)
        


class EnterpriseRecieveTreeRowApiView(APIView):
        def get_dronebase(self, device_auth_key):
            '''
            Helper method to check if drone base with the given authentication key exists
            '''
            try:
                return Devices.objects.get(key = device_auth_key)
            except Devices.DoesNotExist:
                return None
        def get_queryset(self, block_id):
            try:
                return EnterpriseSpecificBreeds.objects.filter(block_id = block_id)
            except EnterpriseSpecificBreeds.DoesNotExist:
                return None
        def post(self, request, device_auth_key, *args, **kwargs):
            device = self.get_dronebase(device_auth_key)
            if not device:
                return Response(
                    {"res": "Device with this device key does not exists",
                    "resCode" : 1
                    },
                    status=status.HTTP_400_BAD_REQUEST
                )
            if device.block_id == None:
                return Response(
                    {"res": "Device with this device key does not have a garden block associated with it",
                     "resCode" : 2
                     },
                    status=status.HTTP_400_BAD_REQUEST
                )
            treeRowSent = request.data

            if len(treeRowSent) == 0:
                return Response(
                    {"res": "Request does not contain any data",
                    "resCode" : 3
                    },
                    status=status.HTTP_400_BAD_REQUEST
                )
            startPoint = treeRowSent.get('startPoint')
            endPoint = treeRowSent.get('endPoint')
            breedID = treeRowSent.get('breed_id')
            if startPoint == None:
                return Response(
                    {"res": "Request does not contain a start point",
                    "resCode" : 3,
                    "missing" : 'startPoint'
                    },
                    status=status.HTTP_400_BAD_REQUEST
                )
            if endPoint == None:
                return Response(
                    {"res": "Request does not contain a end point",
                    "resCode" : 3,
                    "missing" : 'endPoint'
                    },
                    status=status.HTTP_400_BAD_REQUEST
                )
            if breedID  == None:
                return Response(
                    {"res": "Request does not contain a breed_id",
                    "resCode" : 3,
                    "missing" : 'breed_id'
                    },
                    status=status.HTTP_400_BAD_REQUEST
                )
            try:
                breed = EnterpriseSpecificBreeds.objects.get(pk = breedID)
            except:
                return Response(
                    {"res": "Tree Breed does not exist",
                    "resCode" : 4,
                    },
                    status=status.HTTP_400_BAD_REQUEST
                )
            try:
                import json
                json = {"type": "Feature", 
                    "properties": {}, 
                    "geometry": {
                        "type": "LineString", "coordinates":
                        [json.loads(startPoint),
                         json.loads(endPoint)]} }
                row = TreeRow.objects.create(
                    coordinates = json,
                    tree_breed_id = breedID,
                    block_id = device.block_id
                )
                TreeRowGetTrees(row)
                return Response(
                {"res": "TreeRow was Succesfully added",
                "resCode" : 6
                },
                status=status.HTTP_201_CREATED
            )
            except:
                return Response(
                {"res": "Request that has been sent has incorrect data types or blank values",
                "resCode" : 5
                },
                status=status.HTTP_400_BAD_REQUEST
            )
                
class DroneBaseGeneratePlanApiView(APIView):
    def get_dronebase(self, device_auth_key):
        '''
        Helper method to check if drone base with the given authentication key exists
        '''
        try:
            return Devices.objects.get(key = device_auth_key)
        except Devices.DoesNotExist:
            return None
    def get(self, request, device_auth_key, *args, **kwargs):
        device = self.get_dronebase(device_auth_key)
        if not device:
            return Response(
                    {"res": "Device with this device key does not exists",
                    "resCode" : 1
                    },
                    status=status.HTTP_400_BAD_REQUEST
                )
        if not DroneStations.objects.filter(block_id = device.block_id).exists():
            return Response(
                    {"res": "The current block does not have a Drone Station placed",
                    "resCode" : 2
                    },
                    status=status.HTTP_400_BAD_REQUEST
                )
        if not TreeRow.objects.filter(block_id = device.block_id).exists():
            return Response(
                    {"res": "The current block has no Tree Rows placed",
                    "resCode" : 3
                    },
                    status=status.HTTP_400_BAD_REQUEST
                )

        linePoints = pd.DataFrame(columns=['startPoint', 'endPoint'])
        TreeRowsBlock = TreeRow.objects.filter(block_id = device.block_id)
        import json
        for row in TreeRowsBlock:
            rowCoordinates = row.coordinates['geometry']['coordinates']
            row_start_Coordinates = {'longitude': rowCoordinates[0][1], 'latitude':rowCoordinates[0][0]}
            row_end_Coordinates = {'longitude': rowCoordinates[1][1], 'latitude':rowCoordinates[1][0]}
            line = {'startPoint':row_start_Coordinates, 'endPoint':row_end_Coordinates}
            linePoints = linePoints.append(line, ignore_index=True)
        
        Dronestation = DroneStations.objects.get(block_id = device.block_id)
        drone_station_coordinates = [Dronestation.latitude, Dronestation.longitude]

        path = path_calculations(linePoints, drone_station_coordinates, 2, 15)

        print(path)

        start_coordinates = [Dronestation.longitude, Dronestation.latitude]

        def Reverse(tuples):
            new_tup = tuples[::-1]
            return new_tup


        path_corrected = []
        for x in path:
            path_corrected.append(Reverse(x))

        lastpoint = 0
        plans = []
        block_id = self.get_dronebase(device_auth_key).block_id

        monthsLV = ["Janvāris", "Februāris", "Marts", "Aprīlis", "Maijs", "Jūnijs", "Jūlijs", "Augusts", "Septembris", "Oktobris", "Decembris"]
        for x in range(1,len(path_corrected)):
            if path_corrected[x][:2] == start_coordinates[:2]:
                print(path_corrected[lastpoint:x] + start_coordinates)
                plans += [path_corrected[lastpoint:x] + [start_coordinates]]
                lastpoint = x
        if len(plans)>=1:
            if block_id != None:
                import datetime
                currentTime = datetime.datetime.now()
                mission = Missions.objects.create(
                    name = f"{monthsLV[currentTime.month-1]} {currentTime.year}",
                    block_id = block_id,
                    stage_id = 1
                )
                for x in range(0,len(plans)):
                    import json
                    geoJSON = {"type":"Feature",
                                "geometry": 
                                {"type": "Polygon",
                                "coordinates":[plans[x]]}}
                    FlightPlans.objects.create(
                        flight_plan = geoJSON,
                        sequence_num = x+1,
                        mission_id = mission.id,
                        block_id = block_id
                    )
                return Response({"res": "New Mission and Flight_plans for that mission have been created"},status=status.HTTP_201_CREATED)
        return Response({"res": "Bad request"}, status=status.HTTP_400_BAD_REQUEST)
            
class DroneBaseCheckIfAutomatedApiView(APIView):
    def get_dronebase(self, device_auth_key):
        '''
        Helper method to check if drone base with the given authentication key exists
        '''
        try:
            return Devices.objects.get(key = device_auth_key)
        except Devices.DoesNotExist:
            return None
    def get(self, request, device_auth_key, *args, **kwargs):
        device = self.get_dronebase(device_auth_key)
        if not device:
            return Response(
                    {"res": "Device with this device key does not exists",
                    "resCode" : 1
                    },
                    status=status.HTTP_400_BAD_REQUEST
                )
        if device.block_id != None:
            if Blocks.objects.filter(id = device.block_id, is_autonomous = True).exists():
                return Response(
                    {"is_autonomous" : True },
                    status=status.HTTP_200_OK
                    )
            else:
                return Response(
                        {"is_autonomous" : False },
                status=status.HTTP_200_OK)
        else:
            return Response(
                    {"res": "Device is not assigned to any block",
                    "resCode" : 2
                    },
                    status=status.HTTP_400_BAD_REQUEST
                )
        
def reccomendationsGarden(request, garden_id):
    try:
        garden = Gardens.objects.get(pk = garden_id)
    except:
        return JsonResponse({"res": "No Garden found with this garden id"}, status=400)
    
    # check if garden has any blocks
    blocks = Blocks.objects.filter(garden_id = garden_id)
    if not blocks.exists():
        return JsonResponse({"res": "No Blocks found in this garden"}, status=400)
    
    # get trees from the blocks
    trees = []
    for block in blocks:
        trees_block = Trees.objects.filter(block_id = block.id)
        if trees_block.exists():
            for tree in trees_block:
                trees.append(tree)
    
    # find the latest mission for each block
    missions = []
    for block in blocks:
        # take the last 3 to 5 missions if they exist
        missions_block = Missions.objects.filter(block_id = block.id).order_by('closed_date')[:5]
        if missions_block.exists():
            for mission in missions_block:
                missions.append(mission)
    
    # check if any of the missions are done
    done_missions = []
    for mission in missions:
        if mission.status == True:
            done_missions.append(mission)
    
    # check tree states for these missions and return reccomendations
    tree_states2D = []
    for mission in done_missions:
        tree_state_mission = TreeState.objects.filter(mission_id = mission.id)
        tree_states2D.append(tree_state_mission)
    
    # return reccomendations
    # recommendations will be messages that will pop up for the user in the garden page
    
    # state = Ziedēšana
    apple_tree_yields_flowering = 0
    pear_tree_yields_flowering = 0
    cherry_tree_yields_flowering = 0

    # state = Auglaizmentņi 
    apple_tree_yields_fruit = 0
    pear_tree_yields_fruit = 0
    cherry_tree_yields_fruit = 0

    # state = Augļi
    apple_tree_yields = 0
    pear_tree_yields = 0
    cherry_tree_yields = 0

    trees_with_scab = 0

    for tree_state1D in tree_states2D:
        for tree_state in tree_state1D:
            tree = Trees.objects.get(pk = tree_state.tree_id)
            cultivar = Cultivares.objects.get(pk = tree.cultivares_id)
            mission = Missions.objects.get(pk = tree_state.mission_id)
            stage = mission.stage_id
            if cultivar.id == 1:
                if stage == 1:
                    apple_tree_yields_flowering += tree_state.yields
                if stage == 2:
                    apple_tree_yields_fruit += tree_state.yields
                if stage == 3:
                    apple_tree_yields += tree_state.yields
            if cultivar.id == 2:
                if stage == 1:
                    pear_tree_yields_flowering += tree_state.yields
                if stage == 2:
                    pear_tree_yields_fruit += tree_state.yields
                if stage == 3:
                    pear_tree_yields += tree_state.yields
            
            if cultivar.id == 3:
                if stage == 1:
                    cherry_tree_yields_flowering += tree_state.yields
                if stage == 2:
                    cherry_tree_yields_fruit += tree_state.yields
                if stage == 3:
                    cherry_tree_yields += tree_state.yields
            
            if tree_state.has_scab == True:
                trees_with_scab += 1
        
    recommendations = []

    #if apple_tree_yields_flowering > 0:
    #    recommendations.append({ "message" : "Apple trees are flowering", "alert": "success"})
    #if pear_tree_yields_flowering > 0:
    #    recommendations.append( { "message" :"Pear trees are flowering", "alert": "success"})

    #if cherry_tree_yields_flowering > 0:
    #    recommendations.append( { "message" :"Cherry trees are flowering" , "alert": "success"})

    #if apple_tree_yields_fruit > 0:
    #    recommendations.append( { "message" :"Apple trees have fruitlets" , "alert": "success"})

    #if pear_tree_yields_fruit > 0:
    #    recommendations.append( { "message" :"Pear trees have fruitlets" , "alert": "success"})

    #if cherry_tree_yields_fruit > 0:
    #    recommendations.append( { "message" :"Cherry trees have fruitlets" , "alert": "success"})

    #if apple_tree_yields > 0:
    #    recommendations.append( { "message" :"Apple trees have fruit" , "alert": "success"})

    #if pear_tree_yields > 0:
    #    recommendations.append( { "message" :"Pear trees have fruit" , "alert": "success"})

    #if cherry_tree_yields > 0:
    #    recommendations.append( { "message" :"Cherry trees have fruit" , "alert": "success"})


    if trees_with_scab > 0 and apple_tree_yields > 0:
        recommendations.append({ "message" : "Plānot ražas novākšanas stratēģiju: uzglabāšanai (šķirošana pēc uzglabāšanas, daļēji uzglabāšanas šķirošana ražas novākšanas laikā, tikai sulai) un pārdošanai." , "alert": "success"})
    
    
    if trees_with_scab > 0:
        recommendations.append({ "message" : f"{trees_with_scab} kokiem ir kraupis. Izlemt un veikt augu aizsardzības pasākumus pret kraupi. (skatīt mapē)" , "alert": "danger"})


    
    return JsonResponse({"res": recommendations}, status=200)


class GenerateFakeDataGarden(APIView):
    # this view is used to generate fake TreeYields and a mission for a garden
    # this is used for testing purposes
    def get(self, request, garden_id):
        garden = Gardens.objects.get(pk = garden_id)
        if garden == None:
            return JsonResponse({"res": "No Garden found with this garden id"}, status=400)

        import random

        # choose a random block from the garden
        blocks = Blocks.objects.filter(garden_id = garden_id)
        random_block = random.choice(blocks)

        # trees from the block
        trees = Trees.objects.filter(block_id = random_block.id)

        
        # create a mission for the garden flowering stage
        mission = Missions.objects.create(
            name = "Fake Mission - flowering stage",
            block_id = random_block.id,
            stage_id = 1,
            status = True,
        )
        # create tree yields and scab for each tree in the garden randomly except the yields should be realistic
        for tree in trees:
            # create tree state for the tree
            tree_state = TreeState.objects.create(
                tree_id = tree.id,
                mission_id = mission.id,
                yields = random.randint(0, 20),
                has_scab = random.choice([True, False]),
                yield_stage = YieldStages.objects.get(pk = 1)
            )
        
        # create another mission but for the fruiting stage take into account the yields from the flowering stage
        mission2 = Missions.objects.create(
            name = "Fake Mission - fruiting stage",
            block_id = random_block.id,
            stage_id = 2,
            status = True,
        )
        # create tree yields and scab for each tree in the garden randomly except the yields should be realistic
        tree_states = TreeState.objects.filter(mission_id = mission.id)
        for tree_state in tree_states:
            tree_state2 = TreeState.objects.create(
                tree_id = tree_state.tree_id,
                mission_id = mission2.id,
                yields = random.randint(tree_state.yields, 30),
                has_scab = random.choice([True, False]),
                yield_stage = YieldStages.objects.get(pk = 2)
            )
        
        mission3 = Missions.objects.create(
            name = "Fake Mission - fruiting stage",
            block_id = random_block.id,
            stage_id = 2,
            status = True,
        )

        # create tree yields and scab for each tree in the garden randomly except the yields should be realistic
        tree_states = TreeState.objects.filter(mission_id = mission2.id)
        for tree_state in tree_states:
            tree_state2 = TreeState.objects.create(
                tree_id = tree_state.tree_id,
                mission_id = mission3.id,
                yields = random.randint(tree_state.yields, 50),
                has_scab = random.choice([True, False]),
                yield_stage = YieldStages.objects.get(pk = 2)
            )


        # create another mission but for the fruit stage take into account the yields from the fruiting stage
        mission4 = Missions.objects.create(
            name = "Fake Mission - fruit stage",
            block_id = random_block.id,
            stage_id = 3,
            status = True,
        )

        # create tree yields and scab for each tree in the garden randomly except the yields should be realistic
        tree_states2 = TreeState.objects.filter(mission_id = mission3.id)
        for tree_state in tree_states2:
            tree_state3 = TreeState.objects.create(
                tree_id = tree_state.tree_id,
                mission_id = mission4.id,
                yields = random.randint(tree_state.yields, 100),
                has_scab = random.choice([True, False]),
                yield_stage = YieldStages.objects.get(pk = 3)
            )
        

        
        return JsonResponse({"res": "Fake data generated for garden"}, status=200)
    
        
from django.utils import timezone
import torch
import h5py
from django.core.files.storage import default_storage
from ultralytics import YOLO
import tensorflow as tf
import shutil
class ProcessImagesAPIView(APIView):
    def get(self, request):
        photos = Photos.objects.all()
        if not photos.exists():
            raise Http404("No Photos found for given mission.")
        models = MLModels.objects.all()
        stages = YieldStages.objects.all()
        for photo in photos:
            # get cultivar
            tree = Trees.objects.get(pk=photo.tree_id)
            cultivar = Cultivares.objects.get(pk=tree.cultivares_id)
            mission_id = photo.mission_id
            # next get mode
            # active_classes = [{"cultivar_id": "1", "stage_id": "1", "class": "ApplesBBCH76"}, {"cultivar_id": "1", "stage_id": "2", "class": "ApplesBBCH81"}, {"cultivar_id": "2", "stage_id": "2", "class": "PFruitlets640"}, {"cultivar_id": "2", "stage_id": "3", "class": "Pear640"}, {"cultivar_id": "3", "stage_id": "1", "class": "CherriesBBCH72"}, {"cultivar_id": "3", "stage_id": "2", "class": "CherriesBBCH81"}]
            for model in models:
                if model.active_classes != None:
                    active_classes = model.active_classes
                    get_needed_classes = []
                    for active_class in active_classes:
                        if int(active_class["cultivar_id"]) == cultivar.id:
                            if active_class["stage_id"]:
                                get_needed_classes.append(active_class)
                    if get_needed_classes:
                        # check model type
                        if model.model_type == "YOLO":
                            try:
                                modelLoaded = YOLO(model.file_uri.path)
                            except TypeError:
                                # Fallback for older YOLOv5 models
                                modelLoaded = torch.hub.load('ultralytics/yolov5', 'custom', path=model.file_uri.path)
                        elif model.model_type == "MobileNet":
                            modelLoaded = tf.keras.models.load_model(model.model_file.path)
                        else:
                            return JsonResponse({"error": "Unsupported model type."}, status=400)
                        # get photo
                        photo_path = default_storage.path(photo.image_uri.path)
                        # divide the photo into smaller photos
                        # open image and divide it into smaller photos
                        image = Image.open(photo_path).convert("RGB")
                        photo_array = divide_picture(image, 640)

                        if model.model_type == "YOLO":
                            results = modelLoaded.predict(photo_array, conf = 0.2)
                            # what we need to save to the database: TreeState object if it already exists then add the yields to the existing yields
                            # if it does not exist then create a new TreeState object
                            for result in results:
                                classes= result.names
                                boxes_ids = list(map(int, result.boxes.cls.cpu().numpy().tolist()))
                                # create a dictionary of the results { "boxes_id" : "number of these ids in the boxes_ids"}
                                boxes_ids_dict = {}
                                for box_id in boxes_ids:
                                    if box_id in boxes_ids_dict:
                                        boxes_ids_dict[box_id] += 1
                                    else:
                                        boxes_ids_dict[box_id] = 1
                                # get the class name from the classes list
                                for key, value in boxes_ids_dict.items():
                                    class_name = classes[key]
                                    for needed_class in get_needed_classes:
                                        if needed_class["class"] == class_name:
                                            # get the stage
                                            stage = stages.get(pk=int(needed_class["stage_id"]))
                                            # get the number of yields
                                            yields = value
                                            # check if tree state already exists
                                            tree_state = TreeState.objects.filter(tree_id=tree.id, mission_id=mission_id, yield_stage=stage)
                                            if tree_state.exists():
                                                tree_state = tree_state.first()
                                                tree_state.yields += yields
                                                tree_state.save()
                                            else:
                                                TreeState.objects.create(
                                                    tree_id = tree.id,
                                                    mission_id = mission_id,
                                                    yields = yields,
                                                    yield_stage = stage,
                                                    has_scab = False,
                                                    last_updated = timezone.now()
                                                )
                        elif model.model_type == "MobileNet":
                            results = modelLoaded.predict(photo_array, conf = 0.2)
                            for result in results:
                                classes= result.names
                                boxes_ids = list(map(int, result.boxes.cls.cpu().numpy().tolist()))
                                # create a dictionary of the results { "boxes_id" : "number of these ids in the boxes_ids"}
                                boxes_ids_dict = {}
                                for box_id in boxes_ids:
                                    if box_id in boxes_ids_dict:
                                        boxes_ids_dict[box_id] += 1
                                    else:
                                        boxes_ids_dict[box_id] = 1
                                # get the class name from the classes list
                                for key, value in boxes_ids_dict.items():
                                    class_name = classes[key]
                                    for needed_class in get_needed_classes:
                                        if needed_class["class"] == class_name:
                                            # get the stage
                                            stage = stages.get(pk=int(needed_class["stage_id"]))
                                            # get the number of yields
                                            yields = value
                                            # check if tree state already exists
                                            tree_state = TreeState.objects.filter(tree_id=tree.id, mission_id=mission_id, yield_stage=stage)
                                            if tree_state.exists():
                                                tree_state = tree_state.first()
                                                tree_state.yields += yields
                                                tree_state.save()
                                            else:
                                                TreeState.objects.create(
                                                    tree_id = tree.id,
                                                    mission_id = mission_id,
                                                    yields = yields,
                                                    yield_stage = stage,
                                                    has_scab = False,
                                                    last_updated = timezone.now()
                                                )
            # delete the photo
            photo.delete()
        return JsonResponse({"message": "Photos processed successfully."}, status=200)


                        #for cropped_photo in photo_array:
                            # process the photo
                        #    if model.model_type == "YOLO":
                                #
                                #
                                #   you can give an array of photos to the predict method!!!!!
                                #
                                #
                                #
                        #        results = modelLoaded.predict(cropped_photo, conf = 0.2)
                        #    elif model.model_type == "MobileNet":
                        #        results = modelLoaded.predict(cropped_photo, conf = 0.2)
                        #    
                        #    for result in results:
                        #        classes= result.names
                        #        boxes_ids = list(map(int, result.boxes.cls.cpu().numpy().tolist()))
                                # create a dictionary of the results { "boxes_id" : "number of these ids in the boxes_ids"}
                        #        boxes_ids_dict = {}
                        #        for box_id in boxes_ids:
                        #            if box_id in boxes_ids_dict:
                        #                boxes_ids_dict[box_id] += 1
                        #            else:
                        #                boxes_ids_dict[box_id] = 1
                                # get the class name from the classes list
                        #        for key, value in boxes_ids_dict.items():
                        #            class_name = classes[key]
                        #            for needed_class in get_needed_classes:
                        #                if needed_class["class"] == class_name:
                        #                    # get the stage
                        #                    stage = stages.get(pk=int(needed_class["stage_id"]))
                        #                    # get the number of yields
                        #                    yields = value
                        #                    array_of_results_of_one_photo.append({
                        #                        "photo": photo.id,
                        #                        "cultivar": cultivar.id,
                        #                        "stage": stage.id,
                        #                       "yields": yields
                        #                    })
                        # if stage_id and cultivar_id are the same in array_of_results_of_one_photo then sum the yields and remove the duplicates
                        #for i in range(len(array_of_results_of_one_photo)):
                        #    for j in range(i+1, len(array_of_results_of_one_photo)):
                        #        if array_of_results_of_one_photo[i]["cultivar"] == array_of_results_of_one_photo[j]["cultivar"] and array_of_results_of_one_photo[i]["stage"] == array_of_results_of_one_photo[j]["stage"] and array_of_results_of_one_photo[i]["photo"] == array_of_results_of_one_photo[j]["photo"]:
                        #            array_of_results_of_one_photo[i]["yields"] += array_of_results_of_one_photo[j]["yields"]
                        #            array_of_results_of_one_photo.pop(j)
                        #results_of_photos.append(array_of_results_of_one_photo)
                        # save results to the database
                        #for result in array_of_results_of_one_photo:
                        #    TreeState.objects.create(
                        #        tree_id = tree.id,
                        #        mission_id = mission_id,
                        #        yields = result["yields"],
                        #        yield_stage = stages.get(pk=result["stage"]),
                        #        has_scab = False,
                        #        last_updated = timezone.now()
                        #    )
                        #photo.delete()

        # return JsonResponse({"message": "Photos processed successfully."}, status=200)
    


# scab info for map
class ScabInfoMapView(APIView):
    def get(self, request, block_id):
        trees = Trees.objects.filter(block_id=block_id)
        if not trees.exists():
            return JsonResponse({"res": "No Trees found for given block."}, status=400)
        
        newest_mission_ended = Missions.objects.filter(block_id=block_id, status=True).order_by('-closed_date').first()
        if not newest_mission_ended:
            return JsonResponse({"res": "No Missions found for given block."}, status=400)
        
        tree_states = TreeState.objects.filter(mission=newest_mission_ended)
        if not tree_states.exists():
            return JsonResponse({"res": "No Tree States found for given block."}, status=400)
        
        scab_trees = tree_states.filter(has_scab=True)

        # make an array of the trees with scab
        scab_trees_info = []
        for tree in scab_trees:
            tree = trees.get(id=tree.tree_id)
            scab_trees_info.append({
                "latitude": tree.latitude,
                "longitude": tree.longitude,
                "breed": tree.breed,
                "cultivares_id": tree.cultivares_id
            })
        
        return JsonResponse({"scab_trees": scab_trees_info}, status=200)
    

class YieldInfoMapView(APIView):
    def get(self,request, block_id):
        trees = Trees.objects.filter(block_id=block_id)
        if not trees.exists():
            return JsonResponse({"res": "No Trees found for given block."}, status=400)
        
        newest_mission_ended = Missions.objects.filter(block_id=block_id, status=True).order_by('-closed_date').first()
        if not newest_mission_ended:
            return JsonResponse({"res": "No Missions found for given block."}, status=400)
        
        tree_states = TreeState.objects.filter(mission=newest_mission_ended)
        if not tree_states.exists():
            return JsonResponse({"res": "No Tree States found for given block."}, status=400)
        
        # make an array of the trees with scab
        yield_trees_info = []
        for tree_state in tree_states:
            tree = trees.get(id=tree_state.tree_id)
            yield_trees_info.append({
                "latitude": tree.latitude,
                "longitude": tree.longitude,
                "breed": tree.breed,
                "cultivares_id": tree.cultivares_id,
                "yields": tree_state.yields,
                "yield_stage": tree_state.yield_stage.title
            })

        return JsonResponse({"yield_trees": yield_trees_info}, status=200)
    
class CheckMLFile(APIView):
    def post(self, request, *args, **kwargs):
        file = request.FILES.get('modelFile')
        if not file:
            return Response({"error": "No file provided."}, status=status.HTTP_400_BAD_REQUEST)
        temp_dir = default_storage.path("t/")
        if not os.path.exists(temp_dir):
            os.makedirs(temp_dir)
        file_path = os.path.join(temp_dir, file.name)
        with open(file_path, 'wb+') as destination:
            for chunk in file.chunks():
                destination.write(chunk)
        file_extension = file.name.split('.')[-1]

        classes = []
        if file_extension == 'pt':
            try:
                model = YOLO(file_path)
                if model.names:
                    classes = model.names
                    # clear folder t contents
                    shutil.rmtree(temp_dir)
                else:
                    return Response({"error": "No classes found in the .pt file."}, status=status.HTTP_400_BAD_REQUEST)
            except TypeError:
                # Fallback for older YOLOv5 models
                model = torch.hub.load('ultralytics/yolov5', 'custom', path=file_path)
                if model.names:
                    classes = model.names
                    # clear folder t contents
                    shutil.rmtree(temp_dir)
                else:
                    return Response({"error": "No classes found in the .pt file."}, status=status.HTTP_400_BAD_REQUEST)
        elif file_extension == 'h5':
            try:
                with h5py.File(file_path, 'r') as f:
                    classes = [str(c) for c in f['classes']]
                    # clear folder t contents
                    shutil.rmtree(temp_dir)
            except:
                return Response({"error": "No classes found in the .h5 file."}, status=status.HTTP_400_BAD_REQUEST)
        elif file_extension == 'pth':
            # because of the fact that mobilenet does not store classes in the model file, we need to output id = id
            classes = [str(i) for i in range(10)]
            # clear folder t contents
            shutil.rmtree(temp_dir)
        else:
            return Response({"error": "Unsupported file type."}, status=status.HTTP_400_BAD_REQUEST)
        return Response({"classes": classes}, status=status.HTTP_200_OK)
    
class CurrentMLModels(APIView):
    def get(self, request, *args, **kwargs):
        models = MLModels.objects.all()
        serializer = MLModelsSerializer(models, many=True)
        return Response(serializer.data, status=status.HTTP_200_OK)    

class SaveMLModel(APIView):
    # file
    # classes
    # active_classes
    # model_type
    def post(self, request, *args, **kwargs):
        file = request.FILES.get('modelFile')
        classes = request.data.get('modelClasses')
        file_extension = file.name.split('.')[-1]
        if file_extension == 'pt':
            model_type = 'YOLO'
        elif file_extension == 'pth':
            model_type = 'MobileNet'
        else:
            return Response({"error": "Unsupported file type."}, status=status.HTTP_400_BAD_REQUEST)

        if not file:
            return Response({"error": "No file provided."}, status=status.HTTP_400_BAD_REQUEST)
        if not classes:
            return Response({"error": "No classes provided."}, status=status.HTTP_400_BAD_REQUEST)
        if not model_type:
            return Response({"error": "No model type provided."}, status=status.HTTP_400_BAD_REQUEST)
        
        # create a new ML model
        new_model = MLModels.objects.create(
            title = file.name,
            file_uri = file,
            model_type = model_type,
            classes = classes
        )
        return Response({"message": "New ML model file uploaded."}, status=status.HTTP_200_OK)
    
class ActiveClasses(APIView):    
    def post(self, request, *args, **kwargs):
        ActiveClasses = request.data.get('data')
        # convert json to python object
        
        ActiveClasses_py = json.loads(ActiveClasses)
        
        # cultivar id
        # stage id
        # then save according to each model into the database, ie active_classes = [{cultivar_id: x, stage_id: y, class = name_of_class}, ...]
        for active_class in ActiveClasses_py:
            print(active_class)
            cultivar_id = active_class.get('cultivarId')
            stage_id = active_class.get('stageId')
            class_name = active_class.get('class')
            if not cultivar_id or not stage_id:
                return Response({"error": "Invalid data provided."}, status=status.HTTP_400_BAD_REQUEST)
            
            # check if the class exists in the model classes
            models = MLModels.objects.all()
            # class MLModels(models.Model):
            #title = models.CharField(max_length=50)
            #file_uri = models.FileField(upload_to='ML_models/')
            #classes = models.JSONField()
            #active_classes = models.JSONField(blank=True, null=True)
            #model_type = models.TextField(choices=[('yolo', 'YOLO'), ('mobilenet', 'MobileNet')])

            # check if class_name is blank if so, check each ml model active class and if it exists, remove it
            if class_name == "":
                for model in models:
                    active_classes = model.active_classes
                    if active_classes:
                        for active_class in active_classes:
                            if active_class.get('cultivar_id') == cultivar_id and active_class.get('stage_id') == stage_id:
                                active_classes.remove(active_class)
                    model.active_classes = active_classes
                    model.save()
            else:
                modelId = active_class.get('modelId')
                model = MLModels.objects.get(pk=modelId)
                if not model:
                    return Response({"error": "Model not found."}, status=status.HTTP_400_BAD_REQUEST)
                if class_name not in model.classes:
                    return Response({"error": "Class not found in the model."}, status=status.HTTP_400_BAD_REQUEST)
                active_classes = model.active_classes
                if not active_classes:
                    active_classes = [{
                        'cultivar_id': cultivar_id,
                        'stage_id': stage_id,
                        'class': class_name
                    }]
                else:
                    # check if the class is already in the active classes
                    found = False
                    for active_class in active_classes:
                        if active_class.get('cultivar_id') == cultivar_id and active_class.get('stage_id') == stage_id:
                            active_class['class'] = class_name
                            found = True
                            break
                    if not found:
                        active_classes.append({
                        'cultivar_id': cultivar_id,
                        'stage_id': stage_id,
                        'class': class_name
                        })
                model.active_classes = active_classes
                model.save()
        return Response({"message": "New active classes saved."}, status=status.HTTP_200_OK)
    
class DeleteMLModel(APIView):
    def post(self, request, *args, **kwargs):
        model_id = request.data.get('modelId')
        if not model_id:
            return Response({"error": "No model id provided."}, status=status.HTTP_400_BAD_REQUEST)
        model = MLModels.objects.get(pk=model_id)
        if not model:
            return Response({"error": "Model not found."}, status=status.HTTP_400_BAD_REQUEST)
        model.delete()
        return Response({"message": "Model deleted."}, status=status.HTTP_200_OK)
    

class FakeCompleteMission(APIView):
    def get(self, request, mission_id):
        mission = Missions.objects.get(pk=mission_id)
        if not mission:
            return Response({"error": "Mission not found."}, status=status.HTTP_400_BAD_REQUEST)
        # cultivar only apples
        trees = Trees.objects.filter(block_id=mission.block_id, cultivares_id=1)
        if not trees.exists():
            return Response({"error": "No trees found for this mission."}, status=status.HTTP_400_BAD_REQUEST)
        # load fake photos from media/picturesALApples folder
        all_photos = os.listdir(default_storage.path("picturesAIApples"))
        for tree in trees:
            random_photo = random.choice(all_photos)
            # image_uri is a image field
            # load the image so as the image_uri is an image field
            # image_uri = models.ImageField(upload_to='uploads/')
            with open(default_storage.path(f"picturesAIApples/{random_photo}"), 'rb') as f:
                photo = Photos.objects.create(
                    image_uri = default_storage.save(f"uploads/{random_photo}", f),
                    tree_id = tree.id, 
                    mission_id = mission.id,
                    GPS = {"latitude": tree.latitude, "longitude": tree.longitude},
                    altitude = 0,
                    photo_location_num = 0,
                )
        # finish all scheduled flights
        flights = Flights.objects.filter(mission_id=mission.id)
        for flight in flights:
            flight.status = 3
            flight.start_date = timezone.now()
            flight.end_date = timezone.now()
            flight.save()

        mission.status = True
        mission.save()
        return Response({"message": "Fake photos added to the mission."}, status=status.HTTP_200_OK)

class YieldDataMonitoring(APIView):
    def get(self,request,enterprise_id):
        # get all the gardens of the enterprise
        gardens = Gardens.objects.filter(enterprise_id=enterprise_id)
        if not gardens.exists():
            return JsonResponse({"res": "No Gardens found for given enterprise."}, status=400)
        # get all the blocks of the gardens
        blocks = Blocks.objects.filter(garden_id__in=gardens.values_list('id', flat=True))
        if not blocks.exists():
            return JsonResponse({"res": "No Blocks found for given enterprise."}, status=400)
        # get all the trees of the blocks
        trees = Trees.objects.filter(block_id__in=blocks.values_list('id', flat=True))
        if not trees.exists():
            return JsonResponse({"res": "No Trees found for given enterprise."}, status=400)
        # get the latest mission for each block
        missions = Missions.objects.filter(block_id__in=blocks.values_list('id', flat=True)).order_by('-closed_date')
        if not missions.exists():
            return JsonResponse({"res": "No Missions found for given enterprise."}, status=400)
        # get the tree states for each mission
        tree_states = TreeState.objects.filter(mission_id__in=missions.values_list('id', flat=True))
        if not tree_states.exists():
            return JsonResponse({"res": "No Tree States found for given enterprise."}, status=400)
        # get the yield stages
        yield_stages = YieldStages.objects.all()
        if not yield_stages.exists():
            return JsonResponse({"res": "No Yield Stages found for given enterprise."}, status=400)
        # get the cultivares
        cultivares = Cultivares.objects.all()
        if not cultivares.exists():
            return JsonResponse({"res": "No Cultivares found for given enterprise."}, status=400)
        # combine the data by cultivar and stage
        data_by_cultivar = []
        for cultivar in cultivares:
            for stage in yield_stages:
                total_yields = 0
                for tree_state in tree_states:
                    if tree_state.yield_stage == stage and tree_state.tree.cultivares_id == cultivar.id:
                        total_yields += tree_state.yields
                data_by_cultivar.append({
                    "cultivar": cultivar.id,
                    "cultivar_title": cultivar.title,
                    "stage": stage.id,
                    "stage_title": stage.title,
                    "yields": total_yields
                })
        data_by_garden = []
        for garden in gardens:
            total_yields = 0
            for tree_state in tree_states:
                if tree_state.tree.block.garden_id == garden.id:
                    total_yields += tree_state.yields
            data_by_garden.append({
                "garden": garden.id,
                "garden_name": garden.name,
                "yields": total_yields
            })
        return JsonResponse({"data_by_cultivar": data_by_cultivar, "data_by_garden": data_by_garden}, status=200)

class UploadPhotos(APIView):
    # Gets zip file with photos and extracts them to the media folder
    def post(self, request, *args, **kwargs):
        file = request.FILES.get('photoZipFile')
        mission_id = request.data.get('missionId')
        if not file:
            return Response({"error": "No file provided."}, status=status.HTTP_400_BAD_REQUEST)
        # check if there are photos in this zip file
        with zipfile.ZipFile(file, 'r') as zip_ref:
            zip_ref.extractall(default_storage.path("photos/"))
        # get all the photos
        photos = os.listdir(default_storage.path("photos/"))
        # check if there are photos in the folder
        if not photos:
            return Response({"error": "No photos found in the zip file."}, status=status.HTTP_400_BAD_REQUEST)
        # check if the photos are in the correct format
        for photo in photos:
            if not photo.endswith('.jpg') and not photo.endswith('.jpeg') and not photo.endswith('.png'):
                return Response({"error": "Unsupported photo format."}, status=status.HTTP_400_BAD_REQUEST)
        # move the photos to the media folder
        for photo in photos:
            shutil.move(default_storage.path(f"photos/{photo}"), default_storage.path(f"uploads/{photo}"))
        # delete the photos folder
        shutil.rmtree(default_storage.path("photos/"))
        # assign photos to the mission and trees of that block
        mission = Missions.objects.get(pk=mission_id)
        if not mission:
            return Response({"error": "Mission not found."}, status=status.HTTP_400_BAD_REQUEST)
        # get the block
        block = Blocks.objects.get(pk=mission.block_id)
        if not block:
            return Response({"error": "Block not found."}, status=status.HTTP_400_BAD_REQUEST)
        # get the trees
        trees = Trees.objects.filter(block_id=block.id)
        if not trees.exists():
            return Response({"error": "No trees found for this block."}, status=status.HTTP_400_BAD_REQUEST)
        # assign photos to the trees
        for photo in photos:
            tree = random.choice(trees)
            Photos.objects.create(
                image_uri = default_storage.path(f"uploads/{photo}"),
                tree_id = tree.id,
                mission_id = mission.id,
                GPS = {"latitude": tree.latitude, "longitude": tree.longitude},
                altitude = 0,
                photo_location_num = 0,
            )

        return Response({"message": "Photos uploaded."}, status=status.HTTP_200_OK)