# Copyright (c) 2022 Kyle Schouviller (https://github.com/kyle0654)

"""Views module."""
import json
import os
from queue import Queue
from flask import current_app, jsonify, request, Response, send_from_directory, stream_with_context, url_for
from flask.views import MethodView
from dependency_injector.wiring import inject, Provide

from server.models import DreamResult, JobRequest
from server.services import GeneratorService, ImageStorageService, JobQueueService
from server.containers import Container

class ApiJobs(MethodView):

  @inject
  def post(self, job_queue_service: JobQueueService = Provide[Container.generation_queue_service]):
    jobRequest = JobRequest.from_json(request.json)

    print(f">> Request to generate with prompt: {jobRequest.prompt}")

    # Push the request
    job_queue_service.push(jobRequest)

    return { 'jobId': jobRequest.id }
  

class WebIndex(MethodView):
  init_every_request = False
  __file: str = None
  
  def __init__(self, file):
    self.__file = file

  def get(self):
    return current_app.send_static_file(self.__file)


class WebConfig(MethodView):
  init_every_request = False

  def get(self):
    # unfortunately this import can't be at the top level, since that would cause a circular import
    from ldm.gfpgan.gfpgan_tools import gfpgan_model_exists
    config = {
        'gfpgan_model_exists': gfpgan_model_exists
    }
    js = f"let config = {json.dumps(config)};\n"
    return Response(js, mimetype="application/javascript")


class ApiCancel(MethodView):
  init_every_request = False
  
  @inject
  def get(self, generator_service: GeneratorService = Provide[Container.generator_service]):
    generator_service.cancel()
    return Response(status=204)


# TODO: Combine all image storage access
class ApiImages(MethodView):
  init_every_request = False
  __pathRoot = None
  __storage: ImageStorageService

  @inject
  def __init__(self, pathBase, storage: ImageStorageService = Provide[Container.image_storage_service]):
    self.__pathRoot = os.path.abspath(os.path.join(os.path.dirname(__file__), pathBase))
    self.__storage = storage

  def get(self, dreamId):
    name = self.__storage.path(dreamId)
    fullpath=os.path.join(self.__pathRoot, name)
    return send_from_directory(os.path.dirname(fullpath), os.path.basename(fullpath))
  
  def delete(self, dreamId):
    result = self.__storage.delete(dreamId)
    return Response(status=204) if result else Response(status=404)


class ApiImagesMetadata(MethodView):
  init_every_request = False
  __pathRoot = None
  __storage: ImageStorageService

  @inject
  def __init__(self, pathBase, storage: ImageStorageService = Provide[Container.image_storage_service]):
    self.__pathRoot = os.path.abspath(os.path.join(os.path.dirname(__file__), pathBase))
    self.__storage = storage

  def get(self, dreamId):
    meta = self.__storage.getMetadata(dreamId)
    j = {} if meta is None else meta.__dict__
    return j


class ApiIntermediates(MethodView):
  init_every_request = False
  __pathRoot = None
  __storage: ImageStorageService = Provide[Container.image_intermediates_storage_service]

  @inject
  def __init__(self, pathBase, storage: ImageStorageService = Provide[Container.image_intermediates_storage_service]):
    self.__pathRoot = os.path.abspath(os.path.join(os.path.dirname(__file__), pathBase))
    self.__storage = storage

  def get(self, dreamId, step):
    name = self.__storage.path(dreamId, postfix=f'.{step}')
    fullpath=os.path.join(self.__pathRoot, name)
    return send_from_directory(os.path.dirname(fullpath), os.path.basename(fullpath))

  def delete(self, dreamId):
    result = self.__storage.delete(dreamId)
    return Response(status=204) if result else Response(status=404)

    
class ApiImagesList(MethodView):
  init_every_request = False
  __storage: ImageStorageService

  @inject
  def __init__(self, storage: ImageStorageService = Provide[Container.image_storage_service]):
    self.__storage = storage

  def get(self):
    page = request.args.get("page", default=0, type=int)
    perPage = request.args.get("per_page", default=10, type=int)

    result = self.__storage.list_files(page, perPage)
    return result.__dict__