#from django.shortcuts import render
from rest_framework.response import Response
from rest_framework.decorators import api_view
from rest_framework import status


from django.conf import settings
from django.http import HttpResponse
from django.shortcuts import render
from django.db.models import Count, F, Sum

from django.core.files import File
from django.core.files.temp import NamedTemporaryFile

import magic
from datetime import datetime
from time import mktime
import os
import uuid
import tempfile
import requests, json

import cv2
import imutils

import openfoodfacts

import base64

from django_q.tasks import async_task

from .models import *
from .serializers import *

# Create your views here.

@api_view(['GET', 'POST'])
def purchase_list(request):
    if request.method == 'GET':
        data = Purchase.objects.all().order_by('purchase_date')

        serializer = PurchaseSerializer(data, context={'request': request}, many=True)

        return Response(serializer.data)

    elif request.method == 'POST':
        serializer = PurchaseSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(status=status.HTTP_201_CREATED)
            
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

@api_view(['PUT', 'DELETE'])
def purchases_detail(request, pk):
    try:
        purchase = Purchase.objects.get(pk=pk)
    except Purchase.DoesNotExist:
        return Response(status=status.HTTP_404_NOT_FOUND)

    if request.method == 'PUT':
        print(request.data['data'])
        serializer = PurchaseSerializerDepth(purchase, data=request.data['data'],context={'request': request})
        if serializer.is_valid():
            serializer.save()
            return Response(status=status.HTTP_204_NO_CONTENT)
        else:
            print(serializer.errors)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    elif request.method == 'DELETE':
        purchase.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

@api_view(['GET','POST'])    
def purchase_articles(request, pk):
    try:
        purchaseArticles = PurchaseArticle.objects.filter(purchase_id=pk)
    except PurchaseArticle.DoesNotExist:
        return Response(status=status.HTTP_404_NOT_FOUND)
    
    if request.method == 'GET':
        #print(purchaseArticles)
        serializer = PurchaseArticlesSerializer(purchaseArticles, context={'request': request}, many=True)

        return Response(serializer.data)

    #Add existing article to the current purchase
    elif request.method == 'POST':
        inputData = request.data['data']
        inputData['purchase_id'] = pk

        print(inputData)

        #If we add a existing article to the current purchase, no map exists for it. Therefore we have to create it.
        purchaseImage = Purchase.objects.get(pk=pk).receipeImage
        article = Article.objects.get(pk=inputData['article_id']['pk'])
        knownArticleMaps = ArticleMaps.objects.filter(article=article).distinct('receipeString')
        print(knownArticleMaps)
        #a = ArticleMaps.objects.filter(article=article).annotate(count=Count('receipeString', distinct=True)).order_by()
        
        #for element in a:
        #    print(a[0])
        #    print(a[1])
        #If we have different strings, simply take the first occurence
        receipeString = knownArticleMaps[len(knownArticleMaps)-1].receipeString
        articleMap = ArticleMaps.objects.create(article=article, receipeImage=purchaseImage, receipeString=receipeString)
        
        print('Serialize data for response')
        serializer = PurchaseArticlesSerializer(data=inputData)
        if serializer.is_valid():
            serializer.save()
           
            respData = serializer.data
            return Response(respData, status=status.HTTP_201_CREATED)
            
        else:
            print(serializer.errors)
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

@api_view(['POST'])
def new_purchase_article(request,pk):
    try:
        purchase = Purchase.objects.get(pk=pk)
    except Purchase.DoesNotExist:
        return Response(status=status.HTTP_404_NOT_FOUND)
    if request.method == 'POST':
        inputData = request.data['data']
        # 1. Create first new article
        article = Article.objects.create(name=inputData['article_id']['name'],MwSt=inputData['MwSt'])
        # 2. Create new PurchaseArticle with the new article in the given purchase
        purchaseArticle = PurchaseArticle.objects.create(article_id=article,
                                                         purchase_id=purchase,
                                                         quantity=int(inputData['quantity']),
                                                         price=float(inputData['price']),
                                                         inSale=inputData['inSale']
                                                        )
        # 3. Every combination of purchase and article should have a map, so we create a new one
        purchaseImage = Purchase.objects.get(pk=pk).receipeImage
        articleMap = ArticleMaps.objects.create(article=article, receipeImage=purchaseImage)

        #4. Serialize the new data, for responding
        serializer = PurchaseArticlesSerializer(purchaseArticle)
        
        respData = serializer.data
        return Response(respData, status=status.HTTP_201_CREATED)

@api_view(['POST'])
def exchange_purchase_article(request):
    if request.method == 'POST':
        inputData = request.data['data']

        print(inputData)

        deleteOldArticle = inputData['deleteOld']

        newArticle = Article.objects.get(pk=inputData['newArticle']['pk'])
        oldArticle = Article.objects.get(pk=inputData['oldRow']['article_id']['pk'])

        purchase = Purchase.objects.get(pk=inputData['oldRow']['purchase_id'])

        purchaseArticle = PurchaseArticle.objects.get(article_id=oldArticle, purchase_id=purchase)
        articleMap = ArticleMaps.objects.get(pk=inputData['oldRow']['map']['pk'])

        #Assing new article to purchaseArticle and articleMap
        purchaseArticle.article_id = newArticle
        purchaseArticle.save()

        articleMap.article = newArticle
        articleMap.save()

        #If the old article is not in use anymore, delete it
        purchaseArticle = PurchaseArticle.objects.filter(article_id=oldArticle)
        print(purchaseArticle)
        if len(purchaseArticle) == 0 and deleteOldArticle:
            print('Old article deleted')
            oldArticle.delete()

        purchaseArticle = PurchaseArticle.objects.get(article_id=newArticle, purchase_id=purchase)
        serializer = PurchaseArticlesSerializer(purchaseArticle)

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


@api_view(['POST'])
def exchange_purchase_market(request,pk):
    try:
        purchase = Purchase.objects.get(pk=pk)
    except Purchase.DoesNotExist:
        return Response(status=status.HTTP_404_NOT_FOUND)

    if request.method == 'POST':
        inputData = request.data['data']

        print(inputData)

        deleteOldMarket = inputData['deleteOld']

        newMarket = Market.objects.get(pk=inputData['newMarket']['id'])
        oldMarket = purchase.market

        purchase.market = newMarket

        purchase.save()

        #If the old market is not in use anymore, delete it
        purchaseMarkets = Purchase.objects.filter(market=oldMarket)

        if len(purchaseMarkets) == 0 and deleteOldMarket:
            print('Old market deleted')
            oldMarket.delete()

        serializer = PurchaseSerializerDepth(purchase)

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

@api_view(['GET'])
def article_list(request):
    if request.method == 'GET':
        data = Article.objects.all().order_by('brand__name')

        serializer = ArticleSerializer(data, context={'request': request}, many=True)

        return Response(serializer.data)

@api_view(['PUT','DELETE'])
def purchaseArticleMod(request, pk):
    try:
        purchaseArticle = PurchaseArticle.objects.get(pk=pk)
    except PurchaseArticle.DoesNotExist:
        return Response(status=status.HTTP_404_NOT_FOUND)

    if request.method == 'PUT':
        print(request.data['newRow'])

        data = request.data['newRow']

        try:
            data['map']['location_x'] = int(data['map']['location_x'])
            data['map']['location_y'] = int(data['map']['location_y'])
            data['map']['location_w'] = int(data['map']['location_w'])
            data['map']['location_h'] = int(data['map']['location_h'])
        except TypeError:
            data['map']['location_x'] = None
            data['map']['location_y'] = None
            data['map']['location_w'] = None
            data['map']['location_h'] = None

        purchaseArticleSerializer = PurchaseArticlesSerializer(purchaseArticle, data=data,context={'request': request})

        mapData = ArticleMaps.objects.get(pk=data['map']['pk'])

        if data['map']['receipeString'] == '' or data['map']['receipeString'] == None:
            data['map']['receipeString'] = mapData.receipeString

        mapSerializer = ArticleMapSerzializer(mapData, data=data['map'],context={'request': request} )

        if purchaseArticleSerializer.is_valid() and mapSerializer.is_valid():
            purchaseArticleSerializer.save()
            mapSerializer.save()

            article = Article.objects.get(pk=data['article_id']['pk'])

            articleDataFromRequest = data['article_id']
            if articleDataFromRequest['name'] == '':
                articleDataFromRequest['name'] = 'Bitte Namen setzen.' 
            articleSerializer = ArticleSerializer(article, data=articleDataFromRequest,context={'request': request})

            if articleSerializer.is_valid():
                articleSerializer.save()
            else:
                print(articleSerializer.errors)
                return Response(articleSerializer.errors, status=status.HTTP_400_BAD_REQUEST)

            return Response(status=status.HTTP_204_NO_CONTENT)
        else:
            print(purchaseArticleSerializer.errors)
            print(mapSerializer.errors)
            return Response(purchaseArticleSerializer.errors, status=status.HTTP_400_BAD_REQUEST)

    elif request.method == 'DELETE':
        print(purchaseArticle.article_id)
        purchasesWithArticle = PurchaseArticle.objects.filter(article_id=purchaseArticle.article_id)
        purchase = Purchase.objects.get(pk=purchaseArticle.purchase_id)
        articleMap = ArticleMaps.objects.get(article=purchaseArticle.article_id, receipeImage=purchase.receipeImage)
        if len(purchasesWithArticle) > 1:
            purchaseArticle.delete()            
        else:
            # If the articel is only in the current purchase, we should delete it, to keep the database clean
            # If we delete the article, also the map and the purchaseArticel object will be deleted, because
            # of cascade option
            purchaseArticle.article_id.delete()

        articleMap.delete()
        #    purchaseArticle.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)
    
@api_view(['PUT'])
def articleMapMod(request, pk):
    try:
        maps = ArticleMaps.objects.get(pk=pk)
    except ArticleMaps.DoesNotExist:
        return Response(status=status.HTTP_404_NOT_FOUND)   

    if request.method == 'PUT':
        data = request.data['data']
        try:
            data['location_x'] = int(data['location_x'])
            data['location_y'] = int(data['location_y'])
            data['location_w'] = int(data['location_w'])
            data['location_h'] = int(data['location_h'])
        except TypeError:
            data['location_x'] = None
            data['location_y'] = None
            data['location_w'] = None
            data['location_h'] = None
        if data['receipeString'] == '':
            data['receipeString'] = maps.receipeString
        print(data)
        mapSerializer = ArticleMapSerzializer(maps, data=data,context={'request': request}) 

        if mapSerializer.is_valid():
            mapSerializer.save()
            return Response(status=status.HTTP_204_NO_CONTENT) 
        else:
            print(mapSerializer.errors)
            return Response(mapSerializer.errors, status=status.HTTP_400_BAD_REQUEST)

@api_view(['GET','POST'])
def articleMapStrings_list(request):
    if request.method == 'GET':
        data = ReceipeString.objects.all()

        serializer = ReceipeStringSerializer(data, context={'request': request}, many=True)

        return Response(serializer.data)    

    elif request.method == 'POST':
        serializer = ReceipeStringSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(status=status.HTTP_201_CREATED)
            
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)       

@api_view(['GET', 'POST'])
def market_list(request):
    if request.method == 'GET':
        data = Market.objects.all()

        serializer = MarketSerializer(data, context={'request': request}, many=True)

        return Response(serializer.data)

    elif request.method == 'POST':
        serializer = MarketSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(status=status.HTTP_201_CREATED)
            
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

@api_view(['GET','PUT', 'DELETE'])
def markets_detail(request, pk):
    try:
        market = Market.objects.get(pk=pk)
    except Market.DoesNotExist:
        return Response(status=status.HTTP_404_NOT_FOUND)

    if request.method == 'GET':
        serializer = MarketSerializer(market, context={'request': request}, many=False)
        return Response(serializer.data)

    elif request.method == 'PUT':
        serializer = MarketSerializer(market, data=request.data,context={'request': request})
        if serializer.is_valid():
            serializer.save()
            return Response(status=status.HTTP_204_NO_CONTENT)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    elif request.method == 'DELETE':
        market.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

@api_view(['GET','PUT', 'DELETE'])
def article_detail(request, pk):
    try:
        article = Article.objects.get(pk=pk)
    except Article.DoesNotExist:
        return Response(status=status.HTTP_404_NOT_FOUND)

    if request.method == 'GET':
        serializer = ArticleSerializer(article, context={'request': request}, many=False)

        return Response(serializer.data)

    elif request.method == 'PUT':

        print(request.data['article'])
        serializer = ArticleSerializer(article, data=request.data['article'],context={'request': request},many=False)
        if serializer.is_valid():
            print(serializer.errors)
            serializer.save()
            return Response(status=status.HTTP_204_NO_CONTENT)
        else:
            print(serializer.errors)
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    elif request.method == 'DELETE':
        article.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

@api_view(['GET','PUT','POST','DELETE'])    
def articleArticlePackaging(request, pk):
    try:
        articlePackages = PackagingArticle.objects.filter(article_id=pk)
    except PackagingArticle.DoesNotExist:
        return Response(status=status.HTTP_404_NOT_FOUND)
    if request.method == 'GET':

        serializer = PackagingArticleSerializer(articlePackages, context={'request': request}, many=True)

        respData = serializer.data

        return Response(respData)
    elif request.method == 'PUT':
        # Hier ist etwas umweg notwendig, da ich nicht weiß, warum er id bzw pk nicht deserialzied bzw. validiert. So bekommt die
        # Routine aber nur die zu ändernden Daten.
        serializer = PackagingArticleSerializer(articlePackages.get(pk=request.data['updatedRow']['id']), data=request.data['updatedRow'],context={'request': request},many=False)
        if serializer.is_valid():
            serializer.save()
            return Response(status=status.HTTP_204_NO_CONTENT)
        else:
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    elif request.method == 'POST':
        serializer = PackagingArticleSerializer(data=request.data['newValue'])
        if serializer.is_valid():
            serializer.save()
            return Response(data=serializer.data,status=status.HTTP_201_CREATED)
        else:
            print(serializer.errors)
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    elif request.method == 'DELETE':
        try:
            id = int(request.body.decode())
        except ValueError:
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)


        articlePackages = PackagingArticle.objects.get(article_id=pk, pk= id)
        articlePackages.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)


@api_view(['GET','PUT'])
def articlePackagingValues(request, pk):
    packaging = PackagingArticle.objects.filter(article_id=pk)

    if request.method == 'GET':
        serializer = PackagingArticleSerializer(packaging, context={'request': request}, many=True)
        
        return Response(serializer.data)

    elif request.method == 'PUT':
                  
        serializer = PackagingArticleSerializer(packaging, data=request.data,context={'request': request},many=False)
        if serializer.is_valid():
            serializer.save()
            return Response(status=status.HTTP_204_NO_CONTENT)
        else:
            print(serializer.errors)
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

@api_view (['GET'])
def articleNutrient_list(request):
    if request.method == 'GET':
        data = Nutrient.objects.all()

        serializer = NutrientSerializer(data, context={'request': request}, many=True)

        return Response(serializer.data)

@api_view(['GET','PUT','DELETE'])
def articleNutritionalValues(request, pk):
    nutriVals = NutrientArticle.objects.filter(article=pk)

    if request.method == 'GET':
        serializer = NutrientArticleSerializer(nutriVals, context={'request': request}, many=True)
        #Serializer data should always contain Makronutrimennts in the right order
        #Check if a specific nutrient is in the list, if not, add it with value 0
        for element in ['Energie' , 'Fett', 'gesättigte Fettsäuren', 'Kohlenhydrate', 'Zucker', 'Ballaststoffe', 'Eiweiß', 'Salz']:
            if not any([element in x for x in serializer.data]):
                nutrient = Nutrient.objects.get(name = element)
                nutrientSerializer = NutrientSerializer(nutrient, context={'request': request}, many=False)
                serializer.data.append([{'id': -1, 
                                        'article_id': pk,
                                        'nutrient': nutrientSerializer.data,
                                        'value':0,
                                        'unit':'kJ', 
                                        'isEstimated': False
                                        }])

        data = serializer.data
        print(type(data))
        print(serializer.data)
        return Response(serializer.data)

    elif request.method == 'PUT':
        print(request.data['nutriData'])
        try:
            oldData = NutrientArticle.objects.get(article=pk, nutrient=request.data['nutriData']['nutrient']['id'])
        except NutrientArticle.DoesNotExist:
            oldData = NutrientArticle.objects.create(article=Article.objects.get(pk=pk), 
                                                     nutrient=Nutrient.objects.get(pk=request.data['nutriData']['nutrient']['id']))
        serializer = NutrientArticleSerializer(oldData, data=request.data['nutriData'],context={'request': request},many=False)
        if serializer.is_valid():
            serializer.save()
            return Response(status=status.HTTP_204_NO_CONTENT)
        else:
            print(serializer.errors)
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    elif request.method == 'DELETE':
        try:
            id = int(request.body.decode())
        except ValueError:
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

        nutriVals = NutrientArticle.objects.get(article=pk, pk=id)
        nutriVals.delete()
        print(nutriVals)
        return Response(status=status.HTTP_204_NO_CONTENT)



@api_view(['POST'])
def post_receipe(request):
    if request.method == 'POST':      
        print(request.FILES)
        if "uploadedFile" in request.FILES and "fileTitle" in request.POST:
            fileTitle = request.POST["fileTitle"]
            applyBinarize = request.POST["applyBinarize"]
            debug = request.POST["debug"]
            print(applyBinarize)
            print(debug)
            uploadedFile = request.FILES["uploadedFile"]
            print('Receipe upload complete')
            
            t = int(mktime(datetime.now().timetuple()))

            os.makedirs(settings.SCRATCH_DIR, exist_ok=True)

            with tempfile.NamedTemporaryFile(prefix="receipe-upload-",
                                         suffix=".jpg",
                                         dir=settings.SCRATCH_DIR,
                                         delete=False) as f:
                for chunk in uploadedFile.chunks():
                    f.write(chunk)
                os.utime(f.name, times=(t, t))
                temp_filename = f.name

            task_id = str(uuid.uuid4())

            print("Posting job to queue")
    
            async_task("receipe.tasks.consume_file",
                       temp_filename,
                       applyBinarize=applyBinarize,
                       debug=True,
                       task_id=task_id,
                       task_name=os.path.basename(fileTitle)[:100])
            
            return Response(status=status.HTTP_204_NO_CONTENT)
        else:
            return Response(
                status=status.HTTP_400_BAD_REQUEST
            )
    else:
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

@api_view(['POST'])
def post_articleImage(request, pk):
    try:
        article = Article.objects.get(pk=pk)
    except Article.DoesNotExist:
        return Response(status=status.HTTP_404_NOT_FOUND)

    if request.method == 'POST':   
        print(request)
        if "uploadedFile" in request.FILES and "fileTitle" in request.POST:
            fileTitle = request.POST["fileTitle"]
            uploadedFile = request.FILES["uploadedFile"]
            print('Article image upload complete')   
            print(type(uploadedFile))

            if fileTitle == 'frontImage':
                article.frontImage.delete()
                article.frontImage = uploadedFile
            elif fileTitle == 'ingredientsImage':
                article.ingredientsImage.delete()
                article.ingredientsImage = uploadedFile
            elif fileTitle == 'nutritionImage':
                article.nutritionImage.delete()
                article.nutritionImage = uploadedFile
            else:
                return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

            article.save()

            serializedData = ArticleSerializer(article, context={'request': request}, many=False)
            # For visualisation we need the ids of all ancestors, in order to expand the tree. Therefore we fetch the data here
            # TODO: Find better place, maybe in a custom serializer...
            if serializedData.data['category'] is not None:
                id = serializedData.data['category']['id']
                node = Category.objects.get(id=id)
                ancestors = node.get_ancestors()
                ancestorIds = []
                for element in ancestors:
                    ancestorIds.append(element.id)
                serializedData.data['category']['ancestorIds'] = ancestorIds

            return Response(serializedData.data, status=status.HTTP_201_CREATED)
        else:
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    else:
        return Response(status=status.HTTP_400_BAD_REQUEST)    

#This method seems to be not the fastest and for more user the image should be delivered by a webserver as a static file...
@api_view(['GET'])
def receipeImage(request, pk):
    try:
        image = ReceipeImage.objects.get(pk=pk)
    except image.DoesNotExist:
        return Response(status=status.HTTP_404_NOT_FOUND)

    if request.method == 'GET':
        wrapper      = image.source_file_trashed.read()  
        image_data = wrapper

        content_type = magic.from_buffer(wrapper, mime=True)
        response     = HttpResponse(image_data,content_type=content_type)  
        response['Content-Length']      = os.path.getsize(image.source_path_trashed)    

        return response

@api_view(['GET'])
def offsearch_get_by_ean(request,ean,pk):
    try:
        article = Article.objects.get(pk=pk)
    except Article.DoesNotExist:
        return Response(status=status.HTTP_404_NOT_FOUND)

    if request.method == 'GET':  
        search_result = openfoodfacts.products.get_product(ean)
        #ean = 4104420017849
        #url = "https://world.openfoodfacts.org/api/v0/product/"+str(ean)+".json"
        #with requests.Session() as s:
        #    r = s.get(url)
        #    print(r.content)

        #TODO: Handle wrong EAN / empty result

        try:
            image_url = search_result['product']['selected_images']['front']['display']['de']
            img_data = requests.get(image_url).content
            img_temp = NamedTemporaryFile(delete=True)
            img_temp.write(img_data)
            img_temp.flush()
            article.frontImage.delete()
            article.frontImage.save(str(pk)+"-front.jpg", File(img_temp), save=True)
        except KeyError:
            pass

        try:
            image_url = search_result['product']['selected_images']['ingredients']['display']['de']
            img_data = requests.get(image_url).content
            img_temp = NamedTemporaryFile(delete=True)
            img_temp.write(img_data)
            img_temp.flush()
            article.ingredientsImage.delete()
            article.ingredientsImage.save(str(pk)+"-ingredients.jpg", File(img_temp), save=True)
        except KeyError:
            pass

        try:
            image_url = search_result['product']['selected_images']['nutrition']['display']['de']
            img_data = requests.get(image_url).content
            img_temp = NamedTemporaryFile(delete=True)
            img_temp.write(img_data)
            img_temp.flush()
            article.nutritionImage.delete()
            article.nutritionImage.save(str(pk)+"-nutrition.jpg", File(img_temp), save=True)
        except KeyError:
            pass

        if search_result['product']['ecoscore_grade'] == 'unknown':
            score = ''
        else:
            score = search_result['product']['ecoscore_grade']
        article.ecoGrade = score.upper()

        try:
            score = search_result['product']['nova_group']
            article.novaGroup = int(score)
        except KeyError:
            pass
        
        try:
            if search_result['product']['nutriscore_grade'] == 'not-applicable':
                score = ''
            else:
                score = search_result['product']['nutriscore_grade']
                article.nutritionGrade = score.upper()
        except KeyError:    
            pass

        article.save()

        keyVal = [['Energie','energy-kj'],
                ['Fett','fat'],
                ['gesättigte Fettsäuren','saturated-fat'],
                ['Kohlenhydrate','carbohydrates'],
                ['Zucker','sugars'],
                ['Ballaststoffe','fiber'],
                ['Eiweiß','proteins'],
                ['Salz','salt'],
                ['Natrium','sodium'],
                ['Vitamin A','vitamin-a'],
                ['Vitamin B3','vitamin-b3'],
                ['Vitamin B1','vitamin-b1'],
                ['Vitamin B2','vitamin-b2'],
                ['Vitamin B5','vitamin-b5'],
                ['Vitamin B6','vitamin-b6'],
                ['Vitamin B7','vitamin-b7'],
                ['Vitamin B9','vitamin-b9'],
                ['Vitamin B12','vitamin-b12'],
                ['Vitamin C','vitamin-c'],
                ['Vitamin D','vitamin-d'],
                ['Vitamin E','vitamin-e'],
                ['Vitamin K','vitamin-k'],
                ['Kalium','potassium'],
                ['Kalzium','calcium'],
                ['Magnesium','magnesium'],
                ['Eisen','iron'],
                ['Zink','zinc'],
                ['Sulfat','sulfat'],
                ['Chlorid','chlorid'],
                ['Hydrogencarbonat','hydrogencarbonat'],
                ['Iod','iodine']
            ]
        
        for element in keyVal:
            try:
                nutrient = Nutrient.objects.get(name = element[0])

                try:
                    articleNutrient = NutrientArticle.objects.get(article=article, nutrient=nutrient)
                    articleNutrient.value = search_result['product']['nutriments'][element[1]]
                    articleNutrient.unit = search_result['product']['nutriments'][element[1]+'_unit']
                    articleNutrient.isEstimated = False
                    articleNutrient.save()
                except NutrientArticle.DoesNotExist:
                    nutrienArticle = NutrientArticle(article=article,
                                                    nutrient=nutrient,
                                                    value=search_result['product']['nutriments'][element[1]],
                                                    unit=search_result['product']['nutriments'][element[1]+'_unit'],
                                                    isEstimated=False
                                                    )
                    nutrienArticle.save()

            except KeyError:
                pass
       
        articleSerializer = ArticleSerializer(article, context={'request': request}, many=False)
       
        return Response(articleSerializer.data, status=status.HTTP_201_CREATED)
        

@api_view(['GET', 'POST'])
def articleIngrediences_list(request):
    if request.method == 'GET':
        data = Ingredients.objects.all().order_by("name")
        #print(data)
        serializer = IngredientsSerializer(data, context={'request': request}, many=True)

        return Response(serializer.data)

    elif request.method == 'POST':
        print(request.data['newIngredient'])
        serializer = IngredientsSerializer(data=request.data['newIngredient'])
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        else:  
            print(serializer.errors)
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

@api_view(['GET','PUT'])
def articleIngredientValues(request, pk):
    ingredient = IngredientsArticle.objects.filter(article=pk)

    if request.method == 'GET':
        serializer = IngredientsArticleSerializer(ingredient, context={'request': request}, many=True)
        
        return Response(serializer.data)

    elif request.method == 'PUT':
                  
        serializer = IngredientsArticleSerializer(ingredient, data=request.data,context={'request': request},many=False)
        if serializer.is_valid():
            serializer.save()
            return Response(status=status.HTTP_204_NO_CONTENT)
        else:
            print(serializer.errors)
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)


@api_view(['GET','PUT','POST','DELETE'])    
def articleArticleIngrediences(request, pk):
    try:
        articleIngrediences = IngredientsArticle.objects.filter(article_id=pk).order_by('position')
    except IngredientsArticle.DoesNotExist:
        return Response(status=status.HTTP_404_NOT_FOUND)
    if request.method == 'GET':

        serializer = IngredientsArticleSerializer(articleIngrediences, context={'request': request}, many=True)

        respData = serializer.data

        return Response(respData)
    elif request.method == 'PUT':
        # Hier ist etwas umweg notwendig, da ich nicht weiß, warum er id bzw pk nicht deserialzied bzw. validiert. So bekommt die
        # Routine aber nur die zu ändernden Daten.
        data = request.data['updatedRow']
        print(data)
        serializer = IngredientsArticleSerializer(articleIngrediences.get(pk=request.data['updatedRow']['id']), 
                                                  data=data,
                                                  context=data['ingredient'],
                                                  many=False, 
                                                  partial= True)
        if serializer.is_valid():
            serializer.save()
            return Response(status=status.HTTP_204_NO_CONTENT)
        else:
            print(serializer.errors)
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    elif request.method == 'POST':
        #print(articleIngrediences.last())
        try:
            newIngredientPosition = articleIngrediences.last().position + 1
        except AttributeError:
            newIngredientPosition = 1
        data = request.data['newValue']
        data['position'] = newIngredientPosition
        print(data)
        serializer = IngredientsArticleSerializer(data=data, partial=True)
        if serializer.is_valid():
            serializer.save()
            return Response(data=serializer.data,status=status.HTTP_201_CREATED)
        else:
            print(serializer.errors)
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    elif request.method == 'DELETE':
        try:
            id = int(request.body.decode())
        except ValueError:
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)


        articleIngredience = IngredientsArticle.objects.get(article_id=pk, pk= id)
        articleIngredience.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)


@api_view(['GET', 'POST'])
def articleAllergenes_list(request):
    if request.method == 'GET':
        data = Allergenes.objects.all()

        serializer = AllergenesSerializer(data, context={'request': request}, many=True)

        return Response(serializer.data)

    elif request.method == 'POST':
        serializer = AllergenesSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(status=status.HTTP_201_CREATED)
            
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
    
@api_view(['GET', 'POST'])
def articleTraces_list(request):
    if request.method == 'GET':
        data = Traces.objects.all()

        serializer = TracesSerializer(data, context={'request': request}, many=True)

        return Response(serializer.data)

    elif request.method == 'POST':
        serializer = TracesSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(status=status.HTTP_201_CREATED)
            
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
    
@api_view(['GET', 'POST'])
def articleLabels_list(request):
    if request.method == 'GET':
        data = Labels.objects.all()

        serializer = LabelsSerializer(data, context={'request': request}, many=True)

        return Response(serializer.data)

    elif request.method == 'POST':
        serializer = LabelsSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(status=status.HTTP_201_CREATED)
            
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)    

@api_view(['GET', 'POST'])
def articlePackaging_list(request):
    if request.method == 'GET':
        data = Packaging.objects.all()

        serializer = PackagingSerializer(data, context={'request': request}, many=True)

        return Response(serializer.data)

    elif request.method == 'POST':
        serializer = PackagingSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(status=status.HTTP_201_CREATED)
            
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)    

@api_view(['GET', 'POST'])
def articleBrand_list(request):
    if request.method == 'GET':
        data = Brand.objects.all()

        serializer = BrandSerializer(data, context={'request': request}, many=True)

        return Response(serializer.data)

    elif request.method == 'POST':
        serializer = BrandSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(status=status.HTTP_201_CREATED)
            
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)    

@api_view(['GET', 'POST'])
def articleEMB_list(request):
    if request.method == 'GET':
        data = EMBs.objects.all()

        serializer = EMBSerializer(data, context={'request': request}, many=True)

        return Response(serializer.data)

    elif request.method == 'POST':
        serializer = EMBSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(status=status.HTTP_201_CREATED)
            
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)    
    

@api_view(['GET', 'POST'])
def articleOriginIngredients_list(request):
    if request.method == 'GET':
        data = OriginIngredients.objects.all()

        serializer = OriginIngredientsSerializer(data, context={'request': request}, many=True)

        return Response(serializer.data)

    elif request.method == 'POST':
        serializer = EMBSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(status=status.HTTP_201_CREATED)
            
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
       
@api_view(['GET', 'POST'])
def articleManufacturingPlaces_list(request):
    if request.method == 'GET':
        data = ManufacturingPlaces.objects.all()

        serializer = ManufacturingPlacesSerializer(data, context={'request': request}, many=True)

        return Response(serializer.data)

    elif request.method == 'POST':
        serializer = EMBSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(status=status.HTTP_201_CREATED)
            
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

@api_view(['GET'])
def categories_list(request):
    if request.method == 'GET':
        #data = Category.objects.all()
        #serializer = CategorySerializer(data, context={'request': request}, many=True)

        treeDict = Category.dump_bulk()
        #return Response(serializer.data)
        return Response(treeDict[0])

@api_view(['GET'])
def singleCategory(request, pk):
    try:
        category = Category.objects.get(pk=pk)
    except Category.DoesNotExist:
        return Response(status=status.HTTP_404_NOT_FOUND)

    if request.method == 'GET':
        serializer = CategorySerializer(category, context={'request': request}, many=False)
        # For visualisation we need the ids of all ancestors, in order to expand the tree. Therefore we fetch the data here
        # TODO: Find better place, maybe in a custom serializer...
        # Also copy paste from above ...
        data = serializer.data
        id = data['id']
        ancestors = category.get_ancestors()
        ancestorIds = []
        for element in ancestors:
            ancestorIds.append(element.id)
        data['ancestorIds'] = ancestorIds

        return Response(data)

@api_view(['POST'])
def categories_default(request):
    if request.method == 'POST':
        #get = lambda node_id: Category.objects.get(pk=node_id)
        #root = Category.add_root(name='Artikel')
        #node = get(root.pk).add_child(name='Lebensmittel')

        #Category.dump_bulk()
        basepath = os.path.dirname(os.path.abspath(__file__))
        with open(basepath+'/categories.json', 'r') as file:
            data = [json.load(file)]


        # Das hier wirft den Fehler TypeError: Model.save() missing 1 required positional argument: 'self', es wird aber in die Datenbank geschrieben, von daher fange ich einfach den Fehler und
        # hoffe es geht alles gut xD
        try:
            Category.load_bulk(data, None, keep_ids=True)
            Category.save()
        except TypeError:
            pass
        return Response(status=status.HTTP_201_CREATED)

#----------------------------------------------------------------------------------------------------------------------
# --------------------- Scanner API calls -----------------------------------------------------------------------------
@api_view(['POST'])
def receiveImageFromScanner(request):
    if request.method == 'POST':
        if "uploadedFile" in request.FILES:
            uploadedFile = request.FILES["uploadedFile"]
            print('[Info] Received upload from scanner.')
            print(uploadedFile)

            os.makedirs(settings.SCRATCH_DIR, exist_ok=True)
            os.makedirs(settings.SCRATCH_DIR+'/ScannerUploads/', exist_ok=True)

            with tempfile.NamedTemporaryFile(prefix="receipe-upload-",
                                         suffix=".jpg",
                                         dir=settings.SCRATCH_DIR+'/ScannerUploads/',
                                         delete=False) as f:
                for chunk in uploadedFile.chunks():
                    f.write(chunk)
            return Response(status=status.HTTP_204_NO_CONTENT)
        else:
            return Response(
                status=status.HTTP_400_BAD_REQUEST
            )
    else:
        return Response(
                status=status.HTTP_400_BAD_REQUEST
            )

@api_view(['POST'])
def imageFromScannerComplete(request):
    if request.method == 'POST':
        
        files = os.listdir(settings.SCRATCH_DIR+'/ScannerUploads/')
        files.sort(key=lambda f: os.path.getmtime(os.path.join(settings.SCRATCH_DIR+'/ScannerUploads/',f)))

        if len(files) == 0:
            return Response(status=status.HTTP_400_BAD_REQUEST)


        images = []
        for file in files:
            image = cv2.imread(os.path.join(settings.SCRATCH_DIR+'/ScannerUploads/',file)) 
            image = cv2.rotate(image, cv2.ROTATE_90_COUNTERCLOCKWISE)
            images.append(image)

        # initialize OpenCV's image stitcher object and then perform the image stitching
        print("[INFO] Stitching images...")
        stitcher = cv2.createStitcher() if imutils.is_cv3() else cv2.Stitcher_create()
        (statusStiched, stitched) = stitcher.stitch(images)

        # if the status is '0', then OpenCV successfully performed image
        # stitching
        if statusStiched == 0:
            
            stitched = cv2.rotate(stitched, cv2.ROTATE_90_CLOCKWISE)
            now = datetime.now()

            temp_filename = settings.SCRATCH_DIR+'/scanner-upload-'+now.strftime("%Y-%m-%d_%H-%M")+'.png'

            cv2.imwrite(temp_filename, stitched)
        
        #temp_filename = settings.SCRATCH_DIR+'/scanner-upload-2023-07-14_13-40.png'
        task_id = str(uuid.uuid4())

        print("[Info] Posting job to queue")

        async_task("receipe.tasks.consume_file",
                    temp_filename,
                    applyBinarize=True,
                    debug=False,
                    task_id=task_id,
                    task_name=os.path.basename(temp_filename)[:100],
                    scannerFile=True)

        print("[INFO] Image saved.")

        for file in files:
            os.remove(os.path.join(settings.SCRATCH_DIR+'/ScannerUploads/',file))

        return Response(status=status.HTTP_201_CREATED)

    else:
        return Response(status=status.HTTP_400_BAD_REQUEST)

@api_view(['POST'])
def imageFromScannerAgain(request):
    if request.method == 'POST':
        print(request.data)
        temp_filename = settings.SCRATCH_DIR+'/'+request.data['filename']
        task_id = str(uuid.uuid4())

        print("[Info] Posting job to queue")

        async_task("receipe.tasks.consume_file",
                    temp_filename,
                    applyBinarize=True,
                    debug=False,
                    task_id=task_id,
                    task_name=os.path.basename(temp_filename)[:100],
                    scannerFile=True)

        print("[INFO] Image saved.")

        return Response(status=status.HTTP_201_CREATED)

    else:
        return Response(status=status.HTTP_400_BAD_REQUEST)


#----------------------------------------------------------------------------------------------------------------------
# --------------------- Analyses API calls -----------------------------------------------------------------------------

@api_view(['POST'])
def articleCountForInterval(request):
    if request.method == 'POST':
        data = request.data['data']
        print(data)
        #purchases = Purchase.objects.filter(purchase_date__range=[data['fromDate'], data['toDate']])
        #print(purchases)

        articles = PurchaseArticle.objects.filter(purchase_id__purchase_date__range=[data['fromDate'], data['toDate']])
        #articles = articles.order_by('article_id').values('article_id').annotate(count=Count('article_id')).order_by('count') 
        # Aggregate over quantity field
        articles = articles.order_by('article_id').values('article_id').annotate(count=Sum('quantity')).order_by('count')

        print(articles.query)
        print(articles[0])

        for article in articles:
            dbarticle = Article.objects.get(pk=article['article_id'])
            #dbbrand = Brand(pk=article.brand)
            try:
                article['article_name'] = dbarticle.brand.name + ' ' + dbarticle.name 
            except AttributeError:
                article['article_name'] = dbarticle.name 
            

        return Response(articles)

@api_view(['POST'])
def brandCountForInterval(request):
    if request.method == 'POST':
        data = request.data['data']
        print(data)

        # Aggregate brands
        brands = PurchaseArticle.objects.filter(purchase_id__purchase_date__range=[data['fromDate'], data['toDate']])
        brands = brands.order_by('article_id__brand').values('article_id__brand').annotate(count=Sum('quantity')).order_by('count')

        print(brands.query)
        print(brands[0])

        for brand in brands:
            if brand['article_id__brand'] is None:
                brand['brand_name'] = 'Keine Marke'
            else:
                dbbrand = Brand.objects.get(pk=brand['article_id__brand'])
            brand['brand_name'] = dbbrand.name
            
        return Response(brands)
