from rest_framework.response import Response
from rest_framework.views import APIView
from django.shortcuts import get_object_or_404
from dashboard.models import projects
from .models import AnalysisConfig, SolverResults, SolverProgress, DockerLogs
from rest_framework.parsers import FormParser, JSONParser, MultiPartParser, FileUploadParser
from rest_framework import status
import docker
import os
import json
from zipfile import ZipFile
from django.http import HttpResponse
from threading import Thread
from time import sleep
from datetime import datetime

class solverConfig(APIView):

    parser_classes = [FormParser, MultiPartParser]


    def get(self, request, *args, **kwargs):
        """
        return a list of entries within a given category
        """
        project = get_object_or_404(projects, id=kwargs['project_id'])
        category = request.query_params.get('category')
        parentConfig = AnalysisConfig.objects.get(project=project)
        jsonHelper = json.loads(parentConfig.config)

        if category in jsonHelper:
            return Response(data=jsonHelper[category], status=status.HTTP_200_OK)
        else:
            return Response(data="The category {} does not exist".format(category), status=status.HTTP_204_NO_CONTENT)

    def post(self, request, *args, **kwargs):
        """
        create a new category for solver configuration
        """
        project = get_object_or_404(projects, id=kwargs['project_id'])
        data = request.data.dict()
        category = request.query_params.get('category')
        parentConfig = AnalysisConfig.objects.get(project=project)
        jsonHelper = json.loads(parentConfig.config)

        # if request does not contain a name
        if not "Name" in data:
            return Response(data="Please provide a 'Name' for the entry", status=400)

        # if there is no category similar to the user request
        if category not in jsonHelper:
            jsonHelper[category] = []
            jsonHelper[category].append(data)

        # check if the entry with the same name exists
        elif not list(filter(lambda name: name["Name"] == data["Name"], jsonHelper[category])):
            jsonHelper[category].append(data)
        else:
            return Response(data="an entry with the same name exists", status=400)

        parentConfig.config = json.dumps(jsonHelper)
        parentConfig.save()
        return Response(data=jsonHelper[category], status=status.HTTP_201_CREATED)

    def put(self, request, *args, **kwargs):
        """
        Edit an existing category entry's data
        """
        project = get_object_or_404(projects, id=kwargs['project_id'])
        data = request.data.dict()

        # if request does not contain a name
        if not "Name" in data:
            return Response(data="Please provide a 'Name' for the entry", status=400)

        category = request.query_params.get('category')
        list_id = int(request.query_params.get('id'))
        parentConfig = AnalysisConfig.objects.get(project=project)
        jsonHelper = json.loads(parentConfig.config)
        if category in jsonHelper:
            if list_id >= 0 and list_id < len(jsonHelper[category]):

                # check if an entry with the same name exists
                if not list(filter(lambda name: name["Name"] == data["Name"], jsonHelper[category])) or jsonHelper[category][list_id]["Name"] == data["Name"]:
                    jsonHelper[category][list_id] = data
                    parentConfig.config = json.dumps(jsonHelper)
                    parentConfig.save()
                    return Response(data=jsonHelper[category], status=status.HTTP_200_OK)
                else:
                    return Response(data="an entry with the same name exists", status=400)
            else:
                return Response(data="No entry with the id={}".format(list_id), status=status.HTTP_204_NO_CONTENT)
        else:
            return Response(data="The category {} does not exist".format(category), status=status.HTTP_204_NO_CONTENT)

    def delete(self, request, *args, **kwargs):
        """
        Delete an entry from the category
        """
        project = get_object_or_404(projects, id=kwargs['project_id'])
        category = request.query_params.get('category')
        list_id = int(request.query_params.get('id'))
        parentConfig = AnalysisConfig.objects.get(project=project)
        jsonHelper = json.loads(parentConfig.config)
        if jsonHelper[category]:
            if list_id >= 0 and list_id < len(jsonHelper[category]):
                jsonHelper[category].pop(int(list_id))
                parentConfig.config = json.dumps(jsonHelper)
                parentConfig.save()
                return Response(data=jsonHelper[category], status=status.HTTP_200_OK)
            else:
                return Response(data="No entry with the id={}".format(list_id), status=status.HTTP_204_NO_CONTENT)
        else:
            return Response(data="The category {} does not exist".format(category), status=status.HTTP_204_NO_CONTENT)


class Categories(APIView):

    parser_classes = [FormParser, MultiPartParser]


    def get(self, request, *args, **kwargs):
        """
        Return the existing categories in the solver config
        """
        project = get_object_or_404(projects, id=kwargs['project_id'])
        config = json.loads(AnalysisConfig.objects.get(
            project=project).config).keys()
        return Response(data=config, status=status.HTTP_200_OK)

    def delete(self, request, *args, **kwargs):
        """
        DELETE the existing categories in the solver config
        """
        project = get_object_or_404(projects, id=kwargs['project_id'])
        category = request.query_params.get('category')
        parentConfig = AnalysisConfig.objects.get(project=project)
        jsonHelper = json.loads(parentConfig.config)
        if category in jsonHelper:
            del jsonHelper[category]
            parentConfig.config = json.dumps(jsonHelper)
            parentConfig.save()
            return Response(data=jsonHelper, status=status.HTTP_410_GONE)
        else:
            return Response(data="The category {} does not exist!".format(category), status=status.HTTP_404_NOT_FOUND)


class getConfiguration(APIView):

    parser_classes = [FormParser, MultiPartParser]


    def get(self, request, *args, **kwargs):
        """
        Get the solver config to be submitted to the analysis
        """
        project = get_object_or_404(projects, id=kwargs['project_id'])
        config = AnalysisConfig.objects.filter(project=project).values()[0]
        return Response(data=config["config"], status=status.HTTP_200_OK)

def streamDockerLog(container, project):
    for line in container.logs(stream=True):
        logs = get_object_or_404(DockerLogs, project=project)
        now = datetime.now()
        current_time = now.strftime("[%H:%M:%S]:  ")
        logs.log = current_time + str(line.strip(), 'utf-8') + "\n" + logs.log
        logs.save()


class solvers(APIView):

    parser_classes = [FormParser, MultiPartParser]


    def get(self, request, *args, **kwargs):
        """
        Runs the related solver defined in url parameters
        """
        project = get_object_or_404(projects, id=kwargs['project_id'])
        # set progress to initial
        SolverProgress.objects.get_or_create(
			project=project, 
			defaults={'progress' :json.dumps({"status": "", "message": ""})})
        progress = SolverProgress.objects.get( project=project )
        progress.progress = json.dumps({"state":{"status": "RECEIVED", "message": {"progress": "0.0"}}, "logs":""})
        progress.save()

        # initiate related solver
        solver = request.query_params.get('solver')

        client = docker.from_env()
        solverPath = os.path.abspath('./solvers')
        if DockerLogs.objects.filter(project=project).exists():
            DockerLogs.objects.filter(project=project).delete()
        DockerLogs.objects.create(project=project,log="")
        try:
            container = client.containers.run(
                "quay.io/fenicsproject/stable:current",
                volumes={solverPath: {
                    'bind': '/home/fenics/shared', 'mode': 'rw'}},
                working_dir="/home/fenics/shared",
                # runs solver.py with two arguments to be passed in to python file
                command=["`sudo pip3 install requests \n python3 solverHub.py {} {}`".format(
                    project.id, solver)],
                name="FEniCSDocker",
                auto_remove=False,
                detach=True)
            thread = Thread(target=streamDockerLog, args=(container, project))
            thread.start()
        except:
            message = '''please check if the docker is running, and if a container with the name FEniCSDocker does not exist.
            if you are using docker windows, make sure the file sharing setting for the main folder directory is on.
             If you are woking with WSL, make sure it has access to the windows docker. 
             Instructions can be found at: https://nickjanetakis.com/blog/setting-up-docker-for-windows-and-wsl-to-work-flawlessly'''
            print(message)
            return Response(data=message, status=500)
        return Response(data="submitted to analysis", status=status.HTTP_200_OK)

    def delete(self, request, *args, **kwargs):
        """
        kills the running docker container
        """
        client = docker.from_env()
        try:
            container = client.containers.get("FEniCSDocker")
            container.stop()
            return Response(data="container stopped successfully", status=200)
        except:
            return Response(data="No container running", status=404)

class saveResults(APIView):

    parser_classes = [FileUploadParser]

    def put(self, request, filename, format=None, *args, **kwargs):
        """
        save results to media folder. a query will be created to make it available for download
        """
        project = get_object_or_404(projects, id=kwargs['project_id'])
        fileType = request.query_params.get('fileType')
        data = request.data['file']
        folderPath = os.path.abspath(
            "../FEniCSUI/media/{}/results/".format(kwargs['project_id']))
        os.makedirs(folderPath, exist_ok=True)
        filePath = '{}/{}.{}'.format(folderPath, filename, fileType)
        with open(filePath, 'wb+') as destination:
            for chunk in data.chunks():
                destination.write(chunk)
        if not SolverResults.objects.filter(project=project).exists():
            SolverResults.objects.create(project=project, path=folderPath)


        return Response(data="results updated at {}".format(filePath), status=status.HTTP_201_CREATED)


class downloadResults(APIView):

    def get(self, request, *args, **kwargs):
        """
        Get the results saved in the database
        """
        project = get_object_or_404(projects, id=kwargs['project_id'])
        if (SolverResults.objects.filter(project=project).exists()):
            resutls = SolverResults.objects.filter(project=project).values()[0]
            folderPath = resutls['path']
            # create a ZipFile object
            with ZipFile('{}/results.zip'.format(folderPath), 'w') as zipObj:
                # Iterate over all the files in directory
                for folderName, subfolders, filenames in os.walk(folderPath):
                    for filename in filenames:
                        if not filename == 'results.zip':
                            filePath = os.path.join(folderName, filename)
                            # Add file to zip
                            zipObj.write(filePath, os.path.basename(filePath))
            zipFile = open('{}/results.zip'.format(folderPath), 'rb')
            response= HttpResponse(zipFile,content_type='application/zip')
            response['Content-Disposition'] = 'attachment; filename=results.zip'
            return response
        else:
            return Response(data="not found", status=404)


class solverProgress(APIView):
    parser_classes = [JSONParser]


    def get(self, request, *args, **kwargs):
        """
        Get the progress
        """
        project = get_object_or_404(projects, id=kwargs['project_id'])
        if (SolverProgress.objects.filter(project=project).exists()):
            progress = json.loads(get_object_or_404(SolverProgress, project=project).progress)
            logs = get_object_or_404(DockerLogs, project=project).log
        else:
            progress = "null"
            logs=""
        return Response(data=json.dumps({"state":progress,"logs":logs}), status=status.HTTP_200_OK)

    def post(self, request, *args, **kwargs):
        """
        Update the progress from solver
        """
        project = get_object_or_404(projects, id=kwargs['project_id'])
        data = request.data
        if SolverProgress.objects.filter(project=project).exists():
            progress = get_object_or_404(SolverProgress, project=project)
            progress.progress = json.dumps(data)
            progress.save()
        else:
            SolverProgress.objects.create(project=project, progress=data)

        return Response(data=get_object_or_404(SolverProgress, project=project).progress, status=status.HTTP_201_CREATED)

[((26, 18, 26, 70), 'django.shortcuts.get_object_or_404', 'get_object_or_404', (), '', False, 'from django.shortcuts import get_object_or_404\n'), ((29, 21, 29, 52), 'json.loads', 'json.loads', ({(29, 32, 29, 51): 'parentConfig.config'}, {}), '(parentConfig.config)', False, 'import json\n'), ((40, 18, 40, 70), 'django.shortcuts.get_object_or_404', 'get_object_or_404', (), '', False, 'from django.shortcuts import get_object_or_404\n'), ((44, 21, 44, 52), 'json.loads', 'json.loads', ({(44, 32, 44, 51): 'parentConfig.config'}, {}), '(parentConfig.config)', False, 'import json\n'), ((61, 30, 61, 52), 'json.dumps', 'json.dumps', ({(61, 41, 61, 51): 'jsonHelper'}, {}), '(jsonHelper)', False, 'import json\n'), ((63, 15, 63, 82), 'rest_framework.response.Response', 'Response', (), '', False, 'from rest_framework.response import Response\n'), ((69, 18, 69, 70), 'django.shortcuts.get_object_or_404', 'get_object_or_404', (), '', False, 'from django.shortcuts import get_object_or_404\n'), ((79, 21, 79, 52), 'json.loads', 'json.loads', ({(79, 32, 79, 51): 'parentConfig.config'}, {}), '(parentConfig.config)', False, 'import json\n'), ((100, 18, 100, 70), 'django.shortcuts.get_object_or_404', 'get_object_or_404', (), '', False, 'from django.shortcuts import get_object_or_404\n'), ((104, 21, 104, 52), 'json.loads', 'json.loads', ({(104, 32, 104, 51): 'parentConfig.config'}, {}), '(parentConfig.config)', False, 'import json\n'), ((126, 18, 126, 70), 'django.shortcuts.get_object_or_404', 'get_object_or_404', (), '', False, 'from django.shortcuts import get_object_or_404\n'), ((129, 15, 129, 63), 'rest_framework.response.Response', 'Response', (), '', False, 'from rest_framework.response import Response\n'), ((135, 18, 135, 70), 'django.shortcuts.get_object_or_404', 'get_object_or_404', (), '', False, 'from django.shortcuts import get_object_or_404\n'), ((138, 21, 138, 52), 'json.loads', 'json.loads', ({(138, 32, 138, 51): 'parentConfig.config'}, {}), '(parentConfig.config)', False, 'import json\n'), ((157, 18, 157, 70), 'django.shortcuts.get_object_or_404', 'get_object_or_404', (), '', False, 'from django.shortcuts import get_object_or_404\n'), ((159, 15, 159, 73), 'rest_framework.response.Response', 'Response', (), '', False, 'from rest_framework.response import Response\n'), ((163, 15, 163, 61), 'django.shortcuts.get_object_or_404', 'get_object_or_404', (), '', False, 'from django.shortcuts import get_object_or_404\n'), ((164, 14, 164, 28), 'datetime.datetime.now', 'datetime.now', ({}, {}), '()', False, 'from datetime import datetime\n'), ((179, 18, 179, 70), 'django.shortcuts.get_object_or_404', 'get_object_or_404', (), '', False, 'from django.shortcuts import get_object_or_404\n'), ((185, 28, 185, 115), 'json.dumps', 'json.dumps', ({(185, 39, 185, 114): "{'state': {'status': 'RECEIVED', 'message': {'progress': '0.0'}}, 'logs': ''}"}, {}), "({'state': {'status': 'RECEIVED', 'message': {'progress': '0.0'}},\n    'logs': ''})", False, 'import json\n'), ((191, 17, 191, 34), 'docker.from_env', 'docker.from_env', ({}, {}), '()', False, 'import docker\n'), ((192, 21, 192, 49), 'os.path.abspath', 'os.path.abspath', ({(192, 37, 192, 48): '"""./solvers"""'}, {}), "('./solvers')", False, 'import os\n'), ((217, 15, 217, 80), 'rest_framework.response.Response', 'Response', (), '', False, 'from rest_framework.response import Response\n'), ((223, 17, 223, 34), 'docker.from_env', 'docker.from_env', ({}, {}), '()', False, 'import docker\n'), ((239, 18, 239, 70), 'django.shortcuts.get_object_or_404', 'get_object_or_404', (), '', False, 'from django.shortcuts import get_object_or_404\n'), ((244, 8, 244, 46), 'os.makedirs', 'os.makedirs', (), '', False, 'import os\n'), ((262, 18, 262, 70), 'django.shortcuts.get_object_or_404', 'get_object_or_404', (), '', False, 'from django.shortcuts import get_object_or_404\n'), ((291, 18, 291, 70), 'django.shortcuts.get_object_or_404', 'get_object_or_404', (), '', False, 'from django.shortcuts import get_object_or_404\n'), ((304, 18, 304, 70), 'django.shortcuts.get_object_or_404', 'get_object_or_404', (), '', False, 'from django.shortcuts import get_object_or_404\n'), ((32, 19, 32, 81), 'rest_framework.response.Response', 'Response', (), '', False, 'from rest_framework.response import Response\n'), ((48, 19, 48, 85), 'rest_framework.response.Response', 'Response', (), '', False, 'from rest_framework.response import Response\n'), ((74, 19, 74, 85), 'rest_framework.response.Response', 'Response', (), '', False, 'from rest_framework.response import Response\n'), ((141, 34, 141, 56), 'json.dumps', 'json.dumps', ({(141, 45, 141, 55): 'jsonHelper'}, {}), '(jsonHelper)', False, 'import json\n'), ((143, 19, 143, 73), 'rest_framework.response.Response', 'Response', (), '', False, 'from rest_framework.response import Response\n'), ((208, 21, 208, 78), 'threading.Thread', 'Thread', (), '', False, 'from threading import Thread\n'), ((227, 19, 227, 78), 'rest_framework.response.Response', 'Response', (), '', False, 'from rest_framework.response import Response\n'), ((276, 22, 276, 74), 'django.http.HttpResponse', 'HttpResponse', (), '', False, 'from django.http import HttpResponse\n'), ((280, 19, 280, 57), 'rest_framework.response.Response', 'Response', (), '', False, 'from rest_framework.response import Response\n'), ((307, 23, 307, 73), 'django.shortcuts.get_object_or_404', 'get_object_or_404', (), '', False, 'from django.shortcuts import get_object_or_404\n'), ((308, 32, 308, 48), 'json.dumps', 'json.dumps', ({(308, 43, 308, 47): 'data'}, {}), '(data)', False, 'import json\n'), ((59, 19, 59, 82), 'rest_framework.response.Response', 'Response', (), '', False, 'from rest_framework.response import Response\n'), ((108, 38, 108, 60), 'json.dumps', 'json.dumps', ({(108, 49, 108, 59): 'jsonHelper'}, {}), '(jsonHelper)', False, 'import json\n'), ((110, 23, 110, 85), 'rest_framework.response.Response', 'Response', (), '', False, 'from rest_framework.response import Response\n'), ((216, 19, 216, 53), 'rest_framework.response.Response', 'Response', (), '', False, 'from rest_framework.response import Response\n'), ((229, 19, 229, 68), 'rest_framework.response.Response', 'Response', (), '', False, 'from rest_framework.response import Response\n'), ((269, 57, 269, 76), 'os.walk', 'os.walk', ({(269, 65, 269, 75): 'folderPath'}, {}), '(folderPath)', False, 'import os\n'), ((294, 19, 294, 65), 'django.shortcuts.get_object_or_404', 'get_object_or_404', (), '', False, 'from django.shortcuts import get_object_or_404\n'), ((298, 29, 298, 71), 'json.dumps', 'json.dumps', ({(298, 40, 298, 70): "{'state': progress, 'logs': logs}"}, {}), "({'state': progress, 'logs': logs})", False, 'import json\n'), ((86, 42, 86, 64), 'json.dumps', 'json.dumps', ({(86, 53, 86, 63): 'jsonHelper'}, {}), '(jsonHelper)', False, 'import json\n'), ((88, 27, 88, 89), 'rest_framework.response.Response', 'Response', (), '', False, 'from rest_framework.response import Response\n'), ((90, 27, 90, 90), 'rest_framework.response.Response', 'Response', (), '', False, 'from rest_framework.response import Response\n'), ((183, 25, 183, 66), 'json.dumps', 'json.dumps', ({(183, 36, 183, 65): "{'status': '', 'message': ''}"}, {}), "({'status': '', 'message': ''})", False, 'import json\n'), ((293, 34, 293, 84), 'django.shortcuts.get_object_or_404', 'get_object_or_404', (), '', False, 'from django.shortcuts import get_object_or_404\n'), ((313, 29, 313, 79), 'django.shortcuts.get_object_or_404', 'get_object_or_404', (), '', False, 'from django.shortcuts import get_object_or_404\n'), ((272, 39, 272, 73), 'os.path.join', 'os.path.join', ({(272, 52, 272, 62): 'folderName', (272, 64, 272, 72): 'filename'}, {}), '(folderName, filename)', False, 'import os\n'), ((274, 51, 274, 77), 'os.path.basename', 'os.path.basename', ({(274, 68, 274, 76): 'filePath'}, {}), '(filePath)', False, 'import os\n')]