# Kyler Olsen
# CS 2450 Final Project
# Apr 2025

from __future__ import annotations
import json
import random
import socket
import select
import network_utilities
from server import Game


class Library:

    __verses: dict
    __games: list[Game]
    __host: str
    __port: int
    __socket: socket.socket

    def __init__(self, host: str = '', port: int = 7788):
        with open("data/scripture-frequencies.json", encoding='utf-8') as file:
            self.__verses = json.load(file)
        self.__games = []

        self.__host = host
        self.__port = port
        self.__socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    def serve_forever(self):
        try:
            with self.__socket as s:
                s.bind((self.__host, self.__port))
                s.listen(1)
                s.setblocking(False)
                while True:
                    ready_to_read, _, _ = select.select([s], [], [], 0)
                    for game in self.__games[:]:
                        if not game.finished: game.update()
                        else: self.__games.remove(game)
                    if ready_to_read:
                        conn, _ = s.accept()
                        conn.setblocking(False)
                        if network_utilities.unpack_varint(conn) == 1:
                            name = network_utilities.unpack_string(conn)
                            self.join_game(name, conn)
        except KeyboardInterrupt:
            print("KeyboardInterrupt\nExiting...")
            return

    def join_game(self, name: str, conn: socket.socket):
        for i, game in enumerate(self.__games):
            if not game.active:
                game_num = i
                break
        else:
            self.__games.append(Game(self))
            game_num = len(self.__games) - 1
        self.__games[game_num].add_player(name, conn)

    def get_verse(self, difficulty: int, game: Game):
        url = self.__select_verse(difficulty)
        text = self.__get_verse_text(url)

        game.new_verse(url, text)

    def __select_verse(self, difficulty: int) -> str:

        difficulty_verses = self.__get_verses_by_difficulty(difficulty)

        return difficulty_verses[random.randint(0,len(difficulty_verses)-1)]

    def __get_verse_text(self, url: str) -> str:
        lang = 'eng'
        volume, book_url, chapter_url, verse_url = url[1:].split('/')

        if volume == 'ot':
            filename = f"data/{lang}.old-testament.json"
        elif volume == 'nt':
            filename = f"data/{lang}.new-testament.json"
        elif volume == 'bofm':
            filename = f"data/{lang}.book-of-mormon.json"
        elif volume == 'dc-testament':
            filename = f"data/{lang}.doctrine-and-covenants.json"
        elif volume == 'pgp':
            filename = f"data/{lang}.pearl-of-great-price.json"

        with open(filename, encoding='utf-8') as file:
            data = json.load(file)
        for book in data['books']:
            if book['lds_slug'] == book_url:
                for chapter in book['chapters']:
                    if chapter['chapter'] == int(chapter_url):
                        for verse in chapter['verses']:
                            if verse['verse'] == int(verse_url):
                                return verse['text']
        raise ValueError(f'ERROR: VERSE NOT FOUND ({url})')

    def __get_verses_by_difficulty(self, difficulty: int) -> list[str]:
        real_difficulty_upper = pow((10-difficulty)/9, 2) * 500
        real_difficulty_lower = pow((9-difficulty)/9.55, 3) * 500

        difficulty_verses = []
        for key, value in self.__verses.items():
            for i, diff in enumerate(value):
                if real_difficulty_lower <= diff <= real_difficulty_upper:
                    difficulty_verses.append(f"{key}/{i+1}")

        return difficulty_verses

