import io
import sys
import logging
from datetime import datetime
from collections import deque

from edosl0util.stream import PacketStream, MissingPackets

# date used as a flag value for comparissons
_FLAG_DATE = datetime(1900, 1, 1)


LOG = logging.getLogger(__name__)


def _group_packets(stream):
    """
    Returns a generator that yields all packets between timestamps.
    """
    packet = stream.next()
    while not packet.stamp:
        yield packet
        packet = stream.next()
    # put the next packet with a stamp so it will be next
    stream.push_back(packet)


def _is_valid_group(packets):
    return packets[0].is_first() \
        and packets[-1].is_last() \
        and packets[0].apid == packets[-1].apid


def merge(streams, output=sys.stdout):
    last_apid = None
    last_stamp = datetime(1900, 1, 1)

    # streams are removed as they are exhausted
    while streams:
        for stream in streams:
            LOG.debug(stream)
            try:
                LOG.debug("seeking to %s, %s", last_stamp, last_apid)
                stream.seek(last_stamp, last_apid)

                while True:
                    packets = deque()

                    packet = stream.next()
                    if packet.is_standalone():
                        packets.append(packet)

                    else:
                        # Collect all packets between packets with timestamps.
                        # Missing packets and packets that form an invalid group
                        # will be skipped.
                        group = deque([packet])
                        group.extend(_group_packets(stream))
                        if _is_valid_group(group):
                            packets.extend(group)

                        elif group[0].is_first():
                            last_stamp = group[0].stamp
                            last_apid = group[0].apid
                            break

                        else:
                            # yield to next stream, perhaps they have a valid
                            # group.
                            LOG.debug("invalid group, switching streams:%s", group)
                            break

                    if not packets:
                        continue

                    # first packet always has a stamp because it's either standalone or
                    # part of a valid group
                    last_stamp = packets[0].stamp
                    last_apid = packets[0].apid

                    while packets:
                        output.write(packets.popleft().blob)

            except MissingPackets as err:
                LOG.debug("missing packets, switching streams: %s", err.args)

            except StopIteration:
                streams.remove(stream)
                LOG.debug("end-of-stream %s", stream)
                continue