import itertools
import time

import pyparsing as pp

from . import http, http2, websockets, writer, exceptions

from .exceptions import RenderError, FileAccessDenied, ParseException
from .base import Settings

__all__ = [
    "RenderError", "FileAccessDenied", "ParseException",
    "Settings",
]


def expand(msg):
    times = getattr(msg, "times", None)
    if times:
        for j_ in range(int(times.value)):
            yield msg.strike_token("times")
    else:
        yield msg


def parse_pathod(s, use_http2=False):
    """
        May raise ParseException
    """
    try:
        s.encode("ascii")
    except UnicodeError:
        raise exceptions.ParseException("Spec must be valid ASCII.", 0, 0)
    try:
        if use_http2:
            expressions = [
                # http2.Frame.expr(),
                http2.Response.expr(),
            ]
        else:
            expressions = [
                websockets.WebsocketFrame.expr(),
                http.Response.expr(),
            ]
        reqs = pp.Or(expressions).parseString(s, parseAll=True)
    except pp.ParseException as v:
        raise exceptions.ParseException(v.msg, v.line, v.col)
    return itertools.chain(*[expand(i) for i in reqs])


def parse_pathoc(s, use_http2=False):
    try:
        s.encode("ascii")
    except UnicodeError:
        raise exceptions.ParseException("Spec must be valid ASCII.", 0, 0)
    try:
        if use_http2:
            expressions = [
                # http2.Frame.expr(),
                http2.Request.expr(),
            ]
        else:
            expressions = [
                websockets.WebsocketClientFrame.expr(),
                http.Request.expr(),
            ]
        reqs = pp.OneOrMore(pp.Or(expressions)).parseString(s, parseAll=True)
    except pp.ParseException as v:
        raise exceptions.ParseException(v.msg, v.line, v.col)
    return itertools.chain(*[expand(i) for i in reqs])


def parse_websocket_frame(s):
    """
        May raise ParseException
    """
    try:
        reqs = pp.OneOrMore(
            websockets.WebsocketFrame.expr()
        ).parseString(
            s,
            parseAll=True
        )
    except pp.ParseException as v:
        raise exceptions.ParseException(v.msg, v.line, v.col)
    return itertools.chain(*[expand(i) for i in reqs])


def serve(msg, fp, settings):
    """
        fp: The file pointer to write to.

        request_host: If this a request, this is the connecting host. If
        None, we assume it's a response. Used to decide what standard
        modifications to make if raw is not set.

        Calling this function may modify the object.
    """
    msg = msg.resolve(settings)
    started = time.time()

    vals = msg.values(settings)
    vals.reverse()

    actions = sorted(msg.actions[:])
    actions.reverse()
    actions = [i.intermediate(settings) for i in actions]

    disconnect = writer.write_values(fp, vals, actions[:])
    duration = time.time() - started
    ret = dict(
        disconnect=disconnect,
        started=started,
        duration=duration,
    )
    ret.update(msg.log(settings))
    return ret
