""" Song pool analysis module """
import os
from gig.song import Song
from gig.song_pool import SongPool


class NumericSongStatistic:
    """ Numeric song statistic, containing average, min, max, etc values """
    def __init__(self):
        self.max_song = None
        self.max_value = -999999
        self.min_song = None
        self.min_value = 999999
        self.total_value = 0
        self.song_count = 0

    @property
    def avg_value(self) -> int:
        """ Returns the average value """
        return int(self.total_value / self.song_count)


class NumericSongStatisticGenerator:
    """ Class to determine min / max values for the given stat """
    def __init__(self):
        self.result = NumericSongStatistic()

    def analyse(self, p_song: Song, p_value: int):
        """ Determines min/max values """
        self.result.song_count += 1
        self.result.total_value += p_value

        if p_value > self.result.max_value:
            self.result.max_value = p_value
            self.result.max_song = p_song

        if p_value < self.result.min_value:
            self.result.min_value = p_value
            self.result.min_song = p_song


class SongPropertyCount:
    """ Counter for any song property """

    def __init__(self):
        self.counts = {}

    def add(self, key: str, value: int = 1):
        """ Count++ """
        if key in self.counts:
            self.counts[key] = self.counts[key] + value
        else:
            self.counts[key] = value


class SongPoolAnalysisResult:
    """ Result of a song pool analysis """

    def __init__(self,
                 key_count: SongPropertyCount,
                 duration_stats: NumericSongStatistic,
                 genre_count: SongPropertyCount,
                 energy_stats: NumericSongStatistic,
                 rating_stats: NumericSongStatistic,
                 genre_duration: SongPropertyCount,
                 language_count: SongPropertyCount
                 ):
        self.key_count = key_count
        self.duration_stats = duration_stats
        self.genre_count = genre_count
        self.energy_stats = energy_stats
        self.rating_stats = rating_stats
        self.genre_duration = genre_duration
        self.language_count = language_count


class SongPoolAnalysis:
    """ Analyses the song pool and generates statistics
    Note that this class executes upon object creation,
    immediately.
    """

    def __init__(self, pool: SongPool):
        self.pool = pool
        self.result = None

        key_count = SongPropertyCount()
        genre_count = SongPropertyCount()
        genre_duration = SongPropertyCount()
        language_count = SongPropertyCount()

        duration_generator = NumericSongStatisticGenerator()
        energy_generator = NumericSongStatisticGenerator()
        rating_generator = NumericSongStatisticGenerator()

        for song in pool.leftover_songs:
            duration_generator.analyse(p_song=song, p_value=song.duration)
            energy_generator.analyse(p_song=song, p_value=song.energy)
            rating_generator.analyse(p_song=song, p_value=song.rating)
            key_count.add(song.key)
            genre_count.add(song.genre)
            genre_duration.add(song.genre, value=song.duration)
            language_count.add(song.language)

        self.result = SongPoolAnalysisResult(key_count=key_count,
                                             duration_stats=duration_generator.result,
                                             genre_count=genre_count,
                                             energy_stats=energy_generator.result,
                                             rating_stats=rating_generator.result,
                                             genre_duration=genre_duration,
                                             language_count=language_count)


class SongPoolAnalysisHtmlGenerator:
    """ Generates an HTML Report which analyses the song pool """

    _HTML_FILE = "/Users/kerem/Downloads/stats.html"
    analysis: SongPoolAnalysis
    _html: str

    def __init__(self, analysis: SongPoolAnalysis):
        self.analysis = analysis
        self._html = ""

    def generate(self):
        """ Entry method to generate analysis HTML """
        self._generate_html()
        self._download_file()

    def _generate_html(self):
        self._html = ""

        self._put_songs()

        self._put_numeric_song_statistic(stat=self.analysis.result.rating_stats, title="Rating")
        self._put_numeric_song_statistic(stat=self.analysis.result.energy_stats, title="Energy")
        self._put_numeric_song_statistic(stat=self.analysis.result.duration_stats, title="Duration")

        self._put_property_count(stat=self.analysis.result.genre_count, title="Genre")
        self._put_property_count(stat=self.analysis.result.key_count, title="Key")
        self._put_property_count(stat=self.analysis.result.genre_duration, title="Genre Duration")
        self._put_property_count(stat=self.analysis.result.language_count, title="Language")

    def _download_file(self):
        file2 = open(self._HTML_FILE, "w+")
        file2.write(self._html)
        file2.close()
        os.system("open " + self._HTML_FILE)

    def _put_numeric_song_statistic(self, stat: NumericSongStatistic, title: str):
        self._html += "<h1>" + title + "</h1><ul>"
        self._html += "<li>Max: " + stat.max_song.name + " (" + str(stat.max_value) + ")</li>"
        self._html += "<li>Min: " + stat.min_song.name + " (" + str(stat.min_value) + ")</li>"
        self._html += "<li>Avg: " + str(stat.avg_value) + "</li>"
        self._html += "<li>Sum: " + str(stat.total_value) + "</li>"
        self._html += "</ul><hr>"

    def _put_property_count(self, stat: SongPropertyCount, title: str):
        self._html += "<h1>" + title + "</h1><ul>"
        for key in stat.counts:
            self._html += "<li>" + key + " - " + str(stat.counts[key]) + "</li>"
        self._html += "</ul><hr>"

    def _put_songs(self):
        self._html += "<h1>Songs</h1><table cellspacing=3 cellpadding=3 border=0>"
        self._put_song_line(name="Name",
                            formatted_key="Key",
                            duration="Duration",
                            bpm="BPM",
                            genre="Genre",
                            rating="Rating",
                            energy="Energy",
                            language="Language")

        for song in self.analysis.pool.leftover_songs:
            self._put_song_line(name=song.name,
                                formatted_key=song.formatted_key,
                                duration=str(song.duration),
                                bpm=str(song.bpm),
                                genre=song.genre,
                                rating=str(int(song.rating)),
                                energy=str(int(song.energy)),
                                language=song.language)

        self._html += "</table><hr>"

    def _put_song_line(self,
                       name: str,
                       formatted_key: str,
                       duration: str,
                       bpm: str,
                       genre: str,
                       rating: str,
                       energy: str,
                       language: str):
        song_html = "<tr>"
        song_html += "<td align=left valign=top>" + name + "</td>"
        song_html += "<td align=left valign=top>" + formatted_key + "</td>"
        song_html += "<td align=right valign=top>" + duration + "</td>"
        song_html += "<td align=right valign=top>" + bpm + "</td>"
        song_html += "<td align=left valign=top>" + genre + "</td>"
        song_html += "<td align=right valign=top>" + rating + "</td>"
        song_html += "<td align=right valign=top>" + energy + "</td>"
        song_html += "<td align=left valign=top>" + language + "</td>"
        song_html += "</tr>"

        self._html += song_html

""" Song pool analysis module """
import os
from gig.song import Song
from gig.song_pool import SongPool


class NumericSongStatistic:
    """ Numeric song statistic, containing average, min, max, etc values """
    def __init__(self):
        self.max_song = None
        self.max_value = -999999
        self.min_song = None
        self.min_value = 999999
        self.total_value = 0
        self.song_count = 0

    @property
    def avg_value(self) -> int:
        """ Returns the average value """
        return int(self.total_value / self.song_count)


class NumericSongStatisticGenerator:
    """ Class to determine min / max values for the given stat """
    def __init__(self):
        self.result = NumericSongStatistic()

    def analyse(self, p_song: Song, p_value: int):
        """ Determines min/max values """
        self.result.song_count += 1
        self.result.total_value += p_value

        if p_value > self.result.max_value:
            self.result.max_value = p_value
            self.result.max_song = p_song

        if p_value < self.result.min_value:
            self.result.min_value = p_value
            self.result.min_song = p_song


class SongPropertyCount:
    """ Counter for any song property """

    def __init__(self):
        self.counts = {}

    def add(self, key: str, value: int = 1):
        """ Count++ """
        if key in self.counts:
            self.counts[key] = self.counts[key] + value
        else:
            self.counts[key] = value


class SongPoolAnalysisResult:
    """ Result of a song pool analysis """

    def __init__(self,
                 key_count: SongPropertyCount,
                 duration_stats: NumericSongStatistic,
                 genre_count: SongPropertyCount,
                 energy_stats: NumericSongStatistic,
                 rating_stats: NumericSongStatistic,
                 genre_duration: SongPropertyCount,
                 language_count: SongPropertyCount
                 ):
        self.key_count = key_count
        self.duration_stats = duration_stats
        self.genre_count = genre_count
        self.energy_stats = energy_stats
        self.rating_stats = rating_stats
        self.genre_duration = genre_duration
        self.language_count = language_count


class SongPoolAnalysis:
    """ Analyses the song pool and generates statistics
    Note that this class executes upon object creation,
    immediately.
    """

    def __init__(self, pool: SongPool):
        self.pool = pool
        self.result = None

        key_count = SongPropertyCount()
        genre_count = SongPropertyCount()
        genre_duration = SongPropertyCount()
        language_count = SongPropertyCount()

        duration_generator = NumericSongStatisticGenerator()
        energy_generator = NumericSongStatisticGenerator()
        rating_generator = NumericSongStatisticGenerator()

        for song in pool.leftover_songs:
            duration_generator.analyse(p_song=song, p_value=song.duration)
            energy_generator.analyse(p_song=song, p_value=song.energy)
            rating_generator.analyse(p_song=song, p_value=song.rating)
            key_count.add(song.key)
            genre_count.add(song.genre)
            genre_duration.add(song.genre, value=song.duration)
            language_count.add(song.language)

        self.result = SongPoolAnalysisResult(key_count=key_count,
                                             duration_stats=duration_generator.result,
                                             genre_count=genre_count,
                                             energy_stats=energy_generator.result,
                                             rating_stats=rating_generator.result,
                                             genre_duration=genre_duration,
                                             language_count=language_count)


class SongPoolAnalysisHtmlGenerator:
    """ Generates an HTML Report which analyses the song pool """

    _HTML_FILE = "/Users/kerem/Downloads/stats.html"
    analysis: SongPoolAnalysis
    _html: str

    def __init__(self, analysis: SongPoolAnalysis):
        self.analysis = analysis
        self._html = ""

    def generate(self):
        """ Entry method to generate analysis HTML """
        self._generate_html()
        self._download_file()

    def _generate_html(self):
        self._html = ""

        self._put_songs()

        self._put_numeric_song_statistic(stat=self.analysis.result.rating_stats, title="Rating")
        self._put_numeric_song_statistic(stat=self.analysis.result.energy_stats, title="Energy")
        self._put_numeric_song_statistic(stat=self.analysis.result.duration_stats, title="Duration")

        self._put_property_count(stat=self.analysis.result.genre_count, title="Genre")
        self._put_property_count(stat=self.analysis.result.key_count, title="Key")
        self._put_property_count(stat=self.analysis.result.genre_duration, title="Genre Duration")
        self._put_property_count(stat=self.analysis.result.language_count, title="Language")

    def _download_file(self):
        file2 = open(self._HTML_FILE, "w+")
        file2.write(self._html)
        file2.close()
        os.system("open " + self._HTML_FILE)

    def _put_numeric_song_statistic(self, stat: NumericSongStatistic, title: str):
        self._html += "<h1>" + title + "</h1><ul>"
        self._html += "<li>Max: " + stat.max_song.name + " (" + str(stat.max_value) + ")</li>"
        self._html += "<li>Min: " + stat.min_song.name + " (" + str(stat.min_value) + ")</li>"
        self._html += "<li>Avg: " + str(stat.avg_value) + "</li>"
        self._html += "<li>Sum: " + str(stat.total_value) + "</li>"
        self._html += "</ul><hr>"

    def _put_property_count(self, stat: SongPropertyCount, title: str):
        self._html += "<h1>" + title + "</h1><ul>"
        for key in stat.counts:
            self._html += "<li>" + key + " - " + str(stat.counts[key]) + "</li>"
        self._html += "</ul><hr>"

    def _put_songs(self):
        self._html += "<h1>Songs</h1><table cellspacing=3 cellpadding=3 border=0>"
        self._put_song_line(name="Name",
                            formatted_key="Key",
                            duration="Duration",
                            bpm="BPM",
                            genre="Genre",
                            rating="Rating",
                            energy="Energy",
                            language="Language")

        for song in self.analysis.pool.leftover_songs:
            self._put_song_line(name=song.name,
                                formatted_key=song.formatted_key,
                                duration=str(song.duration),
                                bpm=str(song.bpm),
                                genre=song.genre,
                                rating=str(int(song.rating)),
                                energy=str(int(song.energy)),
                                language=song.language)

        self._html += "</table><hr>"

    def _put_song_line(self,
                       name: str,
                       formatted_key: str,
                       duration: str,
                       bpm: str,
                       genre: str,
                       rating: str,
                       energy: str,
                       language: str):
        song_html = "<tr>"
        song_html += "<td align=left valign=top>" + name + "</td>"
        song_html += "<td align=left valign=top>" + formatted_key + "</td>"
        song_html += "<td align=right valign=top>" + duration + "</td>"
        song_html += "<td align=right valign=top>" + bpm + "</td>"
        song_html += "<td align=left valign=top>" + genre + "</td>"
        song_html += "<td align=right valign=top>" + rating + "</td>"
        song_html += "<td align=right valign=top>" + energy + "</td>"
        song_html += "<td align=left valign=top>" + language + "</td>"
        song_html += "</tr>"

        self._html += song_html

Song pool analysis module Numeric song statistic, containing average, min, max, etc values Returns the average value Class to determine min / max values for the given stat Determines min/max values Counter for any song property Count++ Result of a song pool analysis Analyses the song pool and generates statistics     Note that this class executes upon object creation,     immediately. Generates an HTML Report which analyses the song pool Entry method to generate analysis HTML