import json
import random
import subprocess
import sys
import time
from datetime import datetime

import emoji
import pygame
import pytchat
from playsound import playsound
from pygame.locals import *

from aques import Aques
from common_const import *
from common_module import CommonModule
from mode import Mode
from talk import Talk
from youtube import *


class Main:
    @classmethod
    def main (cls, argv: list, argc: int) -> None:
        mode = Mode.NIZIKA
        match (argc > 1) and argv[1]:
            case '-g':
                mode = Mode.GOATOH

            case '-w':
                mode = Mode.DOUBLE

        nizika_mode: bool = mode == Mode.NIZIKA
        goatoh_mode: bool = mode == Mode.GOATOH
        double_mode: bool = mode == Mode.DOUBLE

        print (mode)

        # ウィンドゥの初期化
        pygame.init ()
        screen: pygame.Surface = pygame.display.set_mode (
                (CWindow.WIDTH, CWindow.HEIGHT))

        # 吹き出し
        balloon = pygame.transform.scale (pygame.image.load ('talking.png'),
                                          (CWindow.WIDTH, 384))
        if goatoh_mode:
            balloon = pygame.transform.flip (balloon, False, True)

        # 音声再生器の初期化
        pygame.mixer.init (frequency = 44100)

        # ニジカの “ぬ゛ぅ゛ぅ゛ぅ゛ん゛”
        noon = pygame.mixer.Sound ('noon.wav')

        # ゴートうの “ﾑﾑﾑﾑ”
        mumumumu = pygame.mixer.Sound ('mumumumu.wav')

        # ゴートうの “ｸｻﾀﾍﾞﾃﾙ!!”
        kusa = pygame.mixer.Sound ('kusa.wav')

        # YouTube Chat オブジェクト
        live_chat = pytchat.create (video_id = YOUTUBE_ID)

        # デバッグ・メシジのフォント
        system_font = pygame.font.SysFont ('notosanscjkjp', 24, bold = True)

        # 視聴者コメントのフォント
        user_font = pygame.font.SysFont ('notosanscjkjp', 32, italic = True)

        # ニジカのフォント
        nizika_font = pygame.font.SysFont ('07nikumarufont', 50)

        # Youtube Chat から取得したコメントたち
        chat_items: list = []

        # 会話の履歴（3 件分保持）
        histories: list = []

        while (True):
            screen.fill ((0, 255, 0))

            # 左上に時刻表示
            for i in range (4):
                screen.blit (
                        system_font.render (str (datetime.now ()), True, (0, 0, 0)),
                        (i % 2, i // 2 * 2))

            if live_chat.is_alive ():
                # Chat オブジェクトが有効

                # Chat 取得
                chat_items: list = live_chat.get ().items

                if chat_items:
                    # 溜まってゐる Chat からランダムに 1 つ抽出
                    chat_item = random.choice (chat_items)

                    # 投稿者情報を辞書化
                    chat_item.author = chat_item.author.__dict__

                    # 絵文字を復元
                    chat_item.message = emoji.emojize (chat_item.message)

                    message: str = chat_item.message

                    if nizika_mode:
                        goatoh_talking = False
                    if goatoh_mode:
                        goatoh_talking = True
                    if double_mode:
                        goatoh_talking: bool = random.random () < .5

                    while True:
                        # ChatGPT API を呼出し，返答を取得
                        answer: str = Talk.main (message, chat_item.author['name'], histories, goatoh_talking).replace ('\n', ' ')

                        # 履歴に追加
                        histories = (histories
                                     + [{'role': 'user',      'content': message},
                                    {'role': 'assistant', 'content': answer}])[(-12):]

                        # ログ書込み
                        with open ('log.txt', 'a') as f:
                            f.write (f'{datetime.now ()}\t{json.dumps (chat_item.__dict__)}\t{answer}\n')

                        # 吹出し描画（ニジカは上，ゴートうは下）
                        if nizika_mode:
                            screen.blit (balloon, (0, 0))
                        if goatoh_mode:
                            screen.blit (balloon, (0, 384))
                        if double_mode:
                            screen.blit (pygame.transform.flip (
                                             balloon,
                                             not goatoh_talking,
                                             False),
                                         (0, 0))

                        # 視聴者コメント描画
                        screen.blit (
                                user_font.render (
                                    '> ' + (message
                                            if (CommonModule.len_by_full (message)
                                                <= 21)
                                            else (CommonModule.mid_by_full (
                                                      message, 0, 19.5)
                                                  + '...')),
                                    True,
                                    (0, 0, 0)),
                                (120, 70 + 384) if goatoh_mode else (120 + (64 if (double_mode and not goatoh_talking) else 0), 70))

                        # ニジカの返答描画
                        screen.blit (
                                nizika_font.render (
                                    (answer
                                     if CommonModule.len_by_full (answer) <= 16
                                     else CommonModule.mid_by_full (answer, 0, 16)),
                                    True,
                                    (192, 0, 0)),
                                (100, 150 + 384) if goatoh_mode else (100 + (64 if (double_mode and not goatoh_talking) else 0), 150))
                        if CommonModule.len_by_full (answer) > 16:
                            screen.blit (
                                    nizika_font.render (
                                        (CommonModule.mid_by_full (answer, 16, 16)
                                         if CommonModule.len_by_full (answer) <= 32
                                         else (CommonModule.mid_by_full (
                                                   answer, 16, 14.5)
                                               + '...')),
                                         True,
                                         (192, 0, 0)),
                                    (100, 200 + 384) if goatoh_mode else (100 + (64 if (double_mode and not goatoh_talking) else 0), 200))

                        pygame.display.update ()

                        # 鳴く．
                        if goatoh_talking:
                            if random.random () < .1:
                                kusa.play ()
                            else:
                                mumumumu.play ()
                        else:
                            noon.play ()

                        time.sleep (1.5)

                        # 返答の読上げを WAV ディタとして生成，取得
                        try:
                            wav: bytearray | None = Aques.main (answer, goatoh_talking)
                        except:
                            wav: None = None

                        # 読上げを再生
                        if wav is not None:
                            with open ('./nizika_talking.wav', 'wb') as f:
                                f.write (wav)

                            playsound ('./nizika_talking.wav')

                        time.sleep (1)

                        if not double_mode or random.random () < .5:
                            break

                        screen.fill ((0, 255, 0))

                        chat_item.author = {'name':     'ゴートうひとり' if goatoh_talking else '伊地知ニジカ',
                                            'id':       '',
                                            'imageUrl': './favicon-goatoh.ico' if goatoh_talking else './favicon.ico'}
                        chat_item.message = histories.pop (-1)['content']

                        message = chat_item.message

                        goatoh_talking = not goatoh_talking
            else:
                # Chat オブジェクトが無効

                # 再生成
                live_chat = pytchat.create (video_id = YOUTUBE_ID)

            pygame.display.update ()

            for event in pygame.event.get ():
                if event.type == QUIT:
                    pygame.quit ()
                    sys.exit ()


if __name__ == '__main__':
    # Main.main ((len (sys.argv) > 1) and (sys.argv[1] == '-g'))
    Main.main (sys.argv, len (sys.argv))

