from pydub import AudioSegment
from pydub.playback import play

class Granule:

    def __init__(self,
                 audio:AudioSegment,
                 iterations:int=0, 
                 start:int=0,
                 end:int=0,
                 fade_start:int=0,
                 fade_end:int=0,
                 ):
        
        self.audio:AudioSegment = audio
        self.iterations:int = iterations
        self.final_clip:AudioSegment = AudioSegment.empty()
        self.start:int = start
        self.end:int = end
        self.fade_start:int = fade_start
        self.fade_end:int = fade_end
        self.clip = None

        if self.start > 0 and self.end > 0:
            self.split(self.start, self.end)

    def split(self, start:int, end:int):
        self.clip = self.audio[start:end]

    def render(self):
        final_clip = AudioSegment.empty()

        for _ in range(self.iterations):
            current_clip = self.clip
            
            if self.fade_start > 0:
                current_clip = current_clip.fade_in(self.fade_start)
            
            if self.fade_end > 0:
                current_clip = current_clip.fade_out(self.fade_end)
            
            final_clip += current_clip
        
        return final_clip

    def generate(self):
        for _ in range(self.iterations):
            current_clip = self.clip
            
            if self.fade_start > 0:
                current_clip = current_clip.fade_in(self.fade_start)
            
            if self.fade_end > 0:
                current_clip = current_clip.fade_out(self.fade_end)
            
            yield current_clip

class Render:

    def __init__(self, granules:list[Granule]=[]):
        self.granules = granules
        self.final_clip = AudioSegment.empty()

    def append(self, granule:Granule):
        self.granules.append(granule)

    def pop(self, index:int):
        self.granules.pop(index)

    def insert(self, index:int, granule:Granule):
        self.granules.pop(index, granule)

    def render(self):
        self.final_clip = AudioSegment.empty()
        
        for granule in self.granules:
            for clip in granule.generate():
                self.final_clip += clip
    
    def play(self):
        play(self.final_clip)

    def export(self, file_path: str, format: str = "mp3", parameters: dict = None):

        if len(self.final_clip) == 0:
            raise BaseException("Нет аудио для экспорта. Сначала выполните render().")
        
        try:
            export_params = {"format": format}
            
            if parameters:
                export_params.update(parameters)
            
            self.final_clip.export(file_path, **export_params)
            return True
            
        except Exception as e:
            raise BaseException(f"Ошибка при экспорте файла: {e}")
    
    def export_mp3(self, file_path: str, bitrate: str = "192k"):
        return self.export(file_path, format="mp3", parameters={"bitrate": bitrate})
    
    def export_wav(self, file_path: str):
        return self.export(file_path, format="wav")
    
    def clear(self):
        self.granules.clear()
        self.final_clip = AudioSegment.empty()