"""
This generates a 30 minute silent wav, and is capable of
responding to Range requests.
"""
import time
import re
import struct

from wptserve.utils import isomorphic_decode

def create_wav_header(sample_rate, bit_depth, channels, duration):
    bytes_per_sample = int(bit_depth / 8)
    block_align = bytes_per_sample * channels
    byte_rate = sample_rate * block_align
    sub_chunk_2_size = duration * byte_rate

    data = b''
    # ChunkID
    data += b'RIFF'
    # ChunkSize
    data += struct.pack('<L', 36 + sub_chunk_2_size)
    # Format
    data += b'WAVE'
    # Subchunk1ID
    data += b'fmt '
    # Subchunk1Size
    data += struct.pack('<L', 16)
    # AudioFormat
    data += struct.pack('<H', 1)
    # NumChannels
    data += struct.pack('<H', channels)
    # SampleRate
    data += struct.pack('<L', sample_rate)
    # ByteRate
    data += struct.pack('<L', byte_rate)
    # BlockAlign
    data += struct.pack('<H', block_align)
    # BitsPerSample
    data += struct.pack('<H', bit_depth)
    # Subchunk2ID
    data += b'data'
    # Subchunk2Size
    data += struct.pack('<L', sub_chunk_2_size)

    return data


def main(request, response):
    if request.method == u"OPTIONS":
        response.status = (404, b"Not Found")
        response.headers.set(b"Content-Type", b"text/plain")
        return b"Preflight not accepted"

    response.headers.set(b"Content-Type", b"audio/wav")
    response.headers.set(b"Accept-Ranges", b"bytes")
    response.headers.set(b"Cache-Control", b"no-cache")
    response.headers.set(b"Access-Control-Allow-Origin", request.headers.get(b'Origin', b''))

    range_header = request.headers.get(b'Range', b'')
    range_header_match = range_header and re.search(r'^bytes=(\d*)-(\d*)$', isomorphic_decode(range_header))
    range_received_key = request.GET.first(b'range-received-key', b'')
    accept_encoding_key = request.GET.first(b'accept-encoding-key', b'')

    if range_received_key and range_header:
        # Remove any current value
        request.server.stash.take(range_received_key, b'/fetch/range/')
        # This is later collected using stash-take.py
        request.server.stash.put(range_received_key, u'range-header-received', b'/fetch/range/')

    if accept_encoding_key:
        # Remove any current value
        request.server.stash.take(
            accept_encoding_key,
            b'/fetch/range/'
        )
        # This is later collected using stash-take.py
        request.server.stash.put(
            accept_encoding_key,
            isomorphic_decode(request.headers.get(b'Accept-Encoding', b'')),
            b'/fetch/range/'
        )

    # Audio details
    sample_rate = 8000
    bit_depth = 8
    channels = 1
    duration = 60 * 5

    total_length = int((sample_rate * bit_depth * channels * duration) / 8)
    bytes_remaining_to_send = total_length
    initial_write = b''

    if range_header_match:
        response.status = 206
        start, end = range_header_match.groups()

        start = int(start)
        end = int(end) if end else 0

        if end:
            bytes_remaining_to_send = (end + 1) - start
        else:
            bytes_remaining_to_send = total_length - start

        wav_header = create_wav_header(sample_rate, bit_depth, channels, duration)

        if start < len(wav_header):
            initial_write = wav_header[start:]

            if bytes_remaining_to_send < len(initial_write):
                initial_write = initial_write[0:bytes_remaining_to_send]

        content_range = b"bytes %d-%d/%d" % (start, end or total_length - 1, total_length)

        response.headers.set(b"Content-Range", content_range)
    else:
        initial_write = create_wav_header(sample_rate, bit_depth, channels, duration)

    response.headers.set(b"Content-Length", bytes_remaining_to_send)

    response.write_status_headers()
    response.writer.write(initial_write)

    bytes_remaining_to_send -= len(initial_write)

    while bytes_remaining_to_send > 0:
        to_send = b'\x00' * min(bytes_remaining_to_send, sample_rate)
        bytes_remaining_to_send -= len(to_send)

        if not response.writer.write(to_send):
            break

        # Throttle the stream
        time.sleep(0.5)
