import aiohttp
import asyncio
import base64
import re

from urllib.parse import quote, unquote

class Xeoma():
    def __init__(self, base_url, login=None, password=None):
        """
            Create a Xeoma Object for interacting with Xeoma web server

            Arguments:
                base_url: the url of the Xeoma web server
                new_version: True if the Xeoma version > 17.5
                login: the Xeoma web server username
                password: the Xeoma web server password
        """

        self._base_url = base_url.rstrip('/')
        self._login = login
        self._password = password

    async def async_test_connection(self):
        try:
            async with aiohttp.ClientSession() as session:
                resp = await session.get(self._base_url, timeout=5)
                assert resp.status == 200
        except asyncio.TimeoutError:
            raise XeomaError('Connection to Xeoma server timed out')
        except AssertionError:
            raise XeomaError('Received bad response from Xeoma server')

    async def async_get_camera_image(self, image_name, username=None, password=None):
        """
            Grab a single image from the Xeoma web server

            Arguments:
                image_name: the name of the image to fetch (i.e. image01)
                username: the username to directly access this image
                password: the password to directly access this image
        """

        try:
            data = await self.async_fetch_image_data(
                image_name, username, password)
            if data is None:
                raise XeomaError('Unable to authenticate with Xeoma web '
                                 'server')
            return data
        except asyncio.TimeoutError:
            raise XeomaError('Connection timeout while fetching camera image.')
        except aiohttp.ClientError as e:
            raise XeomaError('Unable to fetch image: {}'.format(e))

    async def async_fetch_image_data(self, image_name, username, password):
        """
            Fetch image data from the Xeoma web server

            Arguments:
                image_name: the name of the image to fetch (i.e. image01)
                username: the username to directly access this image
                password: the password to directly access this image
        """
        params = {}
        cookies = self.get_session_cookie()
        if username is not None and password is not None:
            params['user'] = self.encode_user(username, password)
        else:
            params['user'] = ''
        async with aiohttp.ClientSession(cookies=cookies) as session:
            resp = await session.get(
                '{}/{}.jpg'.format(self._base_url, image_name),
                params=params
            )
            if resp.headers['Content-Type'] == 'image/jpeg':
                data = await resp.read()
            else:
                data = None
        return data

    async def async_get_image_names(self):
        """
            Parse web server camera view for camera image names
        """

        cookies = self.get_session_cookie()
        try:
            async with aiohttp.ClientSession(cookies=cookies) as session:
                resp = await session.get(
                    self._base_url
                )
                t = await resp.text()
                match = re.findall('(?:\w|\d|")/(.*?).(?:mjpg|jpg)', t)
                if len(match) == 0:
                    raise XeomaError('Unable to find any camera image names')
                image_names = set(match)
                results = []
                for image_name in image_names:
                    match = re.search(
                        image_name + '\.(?:mjpg|jpg).*?user=(.*?)&', t
                    )
                    if match and len(match.group(1)) > 0:
                        d = base64.b64decode(unquote(match.group(1))) \
                            .decode('ASCII')
                        creds = d.split(':')
                        if len(creds) < 2:
                            raise XeomaError('Error parsing image credentials')
                        results.append((image_name, creds[0], creds[1]))
                    else:
                        results.append((image_name, None, None))
                return results
        except asyncio.TimeoutError as e:
            raise XeomaError("Unable to connect to Xeoma web server")

    def encode_user(self, username, password):
        credentials = '{}:{}'.format(username, password)
        b = base64.b64encode(bytes(credentials, encoding='ASCII'))
        return quote(b.decode('ASCII'))

    def get_session_cookie(self):
        """
            Create a session cookie object for use by aiohttp
        """

        if self._login is not None and self._password is not None:
            session_key = self.encode_user(self._login, self._password)
            return {'sessionkey': session_key}
        else:
            return None

class XeomaError(Exception):
    def __init__(self, message):
        self.message = message

