import os
from datetime import datetime, timedelta

import pause
import pydub
from pydub.utils import make_chunks

from source.behaviors.roles import base
from source.managers import Manager
from source.packets import AudioPacket
from source.utils.crypto.type import CipherType


class MasterRole(base.BaseRole):
    """
    Role used when the machine is declared as the master.
    It will be the machine responsible for emitting data that the others peers should play together.
    """

    TARGET_SIZE: int = 60 * 1024  # set an upper bound because of the IPv6 maximum packet size.

    def __init__(self, manager: "Manager"):
        super().__init__(manager)

        # generate a random secret key for AES communication
        self.manager.communication.secret_key = os.urandom(32)

        # prepare the audio file that will be streamed
        # TODO(Faraphel): use another audio source
        self.audio = pydub.AudioSegment.from_file("../assets/Queen - Another One Bites the Dust.mp3")
        self.play_time = datetime.now()

        # calculate the number of bytes per milliseconds in the audio
        bytes_per_ms = self.audio.frame_rate * self.audio.sample_width * self.audio.channels / 1000
        # calculate the required chunk duration to reach that size
        self.chunk_duration = timedelta(milliseconds=self.TARGET_SIZE / bytes_per_ms)

        # split the audio into chunks
        self.chunk_count = 0
        self.chunks = make_chunks(self.audio, self.chunk_duration.total_seconds() * 1000)


    def handle(self) -> None:
        # TODO(Faraphel): communicate with chrony to add peers and enable server mode ?
        # TODO(Faraphel): share the secret key generated with the other *allowed* peers ! How to select them ? A file ?
        # TODO(Faraphel): check if another server is emitting sound in the network. Return to undefined if yes

        # get the current chunk
        chunk = self.chunks[self.chunk_count]

        # broadcast it in the network
        audio_packet = AudioPacket(
            # TODO(Faraphel): adjust time depending on the network average latency ?
            datetime.now() + timedelta(seconds=5),  # play it in some seconds to let all the machines get the sample
            chunk.channels,
            chunk.frame_rate,
            chunk.sample_width,
            chunk.raw_data,
        )
        self.manager.communication.broadcast(audio_packet, CipherType.AES_CBC)

        # increment the chunk count
        self.chunk_count += 1

        # wait for the next chunk time
        pause.until(self.play_time + (self.chunk_duration * self.chunk_count))
