import asyncio
import os.path

from aiohttp import web

import paths
from web_interface import handlers, auth


CAMERA_SWITCH_TIMEOUT = 10
URLS_READ_WRITE_RETRIES = 3


def read_camera_urls(retries=URLS_READ_WRITE_RETRIES):
    try:
        if os.path.isfile(paths.CAMERA_URLS_FILE):
            with open(paths.CAMERA_URLS_FILE, "r") as f:
                return f.read()
        else:
            return ""
    except OSError:
        if retries:
            retries -= 1
            read_camera_urls(retries=retries)


def write_camera_urls(urls, retries=URLS_READ_WRITE_RETRIES):
    try:
        with open(paths.CAMERA_URLS_FILE, "w") as f:
            f.write(urls)
        return True
    except OSError:
        if retries:
            retries -= 1
            write_camera_urls(urls, retries=retries)
        else:
            return False


async def camera_urls(request):
    auth.check_auth(request)
    urls = read_camera_urls()
    if urls is None:
        raise web.HTTPInternalServerError(text='Error reading camera URLs file')
    if request.method == 'GET':
        return web.Response(body=urls)
    if request.method == 'POST':
        try:
            post_data = await request.post()
            new_urls = post_data['urls']
        except:
            try:
                post_data = await request.json()
                new_urls = post_data['urls']
            except:
                raise web.HTTPBadRequest(text='This post should have field name urls with value of a new line separated camera URLs')
        if not write_camera_urls(new_urls):
            raise web.HTTPInternalServerError(text='Error writing camera URLs file')
    elif request.method == 'PATCH':
        try:
            post_data = await request.post()
            new_urls = post_data['urls']
        except:
            try:
                post_data = await request.json()
                new_urls = post_data['urls']
            except:
                raise web.HTTPBadRequest(text='This patch should have field name urls with value of a new line separated camera URLs')
        if not urls.endswith('\n'):
            urls += '\n'
        if not write_camera_urls(urls + new_urls):
            raise web.HTTPInternalServerError(text='Error writing camera URLs file')
    else:
        raise web.HTTPMethodNotAllowed(method=request.method, allowed_methods=('GET', 'POST', 'PATCH'))
    return web.HTTPOk()


async def camera_controller(request):
    auth.check_auth(request)
    status = 200
    data = None
    path = request.match_info.get('path')
    cam_controller = getattr(request.app['3dp_app'], 'camera_controller', None)
    if not cam_controller:
        status = 425
    elif request.method == 'GET':
        if path == 'current':
            data = cam_controller.current_camera_name
        elif path == 'enable':
            data = cam_controller.enabled
        elif path == 'types':
            data = list(cam_controller.CAMERA_MODULES.keys())
        else:
            raise web.HTTPNotFound()
    elif request.method == 'POST':
        try:
            post_data = await request.json()
        except:
            data = {'error': 'post without or with invalid json body'}
            status = 400
        else:
            if path == 'current':
                if isinstance(post_data, dict):
                    camera_name = post_data['name']
                elif isinstance(post_data, str):
                    camera_name = post_data
                else:
                    raise web.HTTPBadRequest(text='Invalid camera name')
                if camera_name in cam_controller.CAMERA_MODULES.keys():
                    camera_switch_future = asyncio.get_event_loop().run_in_executor(
                            request.app['threads_pool'],
                            cam_controller.switch_camera, camera_name)
                    await asyncio.wait_for(camera_switch_future, CAMERA_SWITCH_TIMEOUT)
                else:
                    data = {'error': 'no such module: ' + str(camera_name)}
                    status = 400
            elif path == 'enable':
                if isinstance(post_data, dict):
                    enable = post_data['enable']
                elif isinstance(post_data, bool):
                    enable = post_data
                else:
                    raise web.HTTPBadRequest()
                if cam_controller.enabled == enable:
                    data = {'error': f'camera is already in got enable set to {enable}'}
                    status = 400
                else:
                    cam_controller.enabled = enable
                    if enable:
                        camera_start_future = asyncio.get_event_loop().run_in_executor(
                                request.app['threads_pool'],
                                cam_controller.start_camera_process)
                        await asyncio.wait_for(camera_start_future, CAMERA_SWITCH_TIMEOUT)
            else:
                raise web.HTTPNotFound()
    return web.json_response(data, status=status)
