#!/usr/bin/env python3
# encoding=utf-8
#
# Copyright © 2024-2025 Sébastien Noel <sebastien@twolife.be>
# SPDX-License-Identifier: GPL-2.0-or-later
import os
import sys
import sdl2
import sdl2.ext
from enum import Enum
from PIL import Image, ImageFile
from sdl2 import sdlmixer
from sdl2.ext import FontTTF

if os.path.isdir('/usr/share/smac'):
    SMACDIR = '/usr/share/smac'
elif os.path.isdir('/usr/share/games/smac'):
    SMACDIR = '/usr/share/games/smac'
else:
    SMACDIR = os.path.dirname(os.path.realpath(__file__))


def data_filename(filename: str) -> str:
    return os.path.join(
                SMACDIR,
                'data',
                filename,
            )


def is_in_rect(rect: sdl2.rect.SDL_Rect, x: int, y: int) -> bool:
    return (x >= rect.x and (x < rect.x + rect.w)) and \
           (y >= rect.y and (y < rect.y + rect.h))


class Button(Enum):
    SMAC = {
        'txt': 'ALPHA CENTAURI',
        'txt_x': 610,
        'txt_y': 448,
        'rect': sdl2.rect.SDL_Rect(575, 435, 295, 55),
    }
    SMACX = {
        'txt': "ALIEN CROSSFIRE",
        'txt_x': 610,
        'txt_y': 523,
        'rect': sdl2.rect.SDL_Rect(575, 510, 295, 55),
    }
    CANCEL = {
        'txt': "CANCEL",
        'txt_x': 610,
        'txt_y': 598,
        'rect': sdl2.rect.SDL_Rect(575, 585, 295, 55),
    }


class FlcFile(Enum):
    FLARE = {'im': 'ax_flare.flc', 'x': 212, 'y': 484}
    SMAC = {'im': 'ax_unity.flc', 'x': 142, 'y': 154}
    SMACX = {'im': 'ax_alien.flc', 'x': 542, 'y': 214}


class WavFile(Enum):
    SMAC = 'fx/ax_unity.wav'
    SMACX = 'fx/ax_alien_shot.wav'


class SMACPack():
    def __init__(self) -> None:
        sdl2.SDL_Init(sdl2.SDL_INIT_VIDEO | sdl2.SDL_INIT_AUDIO)
        sdl2.sdlmixer.Mix_OpenAudio(22050, sdl2.sdlmixer.MIX_DEFAULT_FORMAT,
                                    1, 1024)
        centered = sdl2.SDL_WINDOWPOS_CENTERED

        self.window = sdl2.ext.Window(
                          'SMAC Planetary Pack',
                          size=(1024, 768),
                          position=(centered, centered),
                          flags=sdl2.SDL_SWSURFACE
                      )

        self.font = FontTTF(data_filename('fonts/arialb.ttf'),
                            '15px', (255, 255, 255, 255))
        self.font.add_style('green', '15px', (84, 172, 148, 255))
        self.font.add_style('green_highlight', '15px', (176, 232, 188, 255))

        self.sounds = {}
        for data in WavFile:
            self.sounds[data.name] = sdl2.sdlmixer.Mix_LoadWAV(
                bytes(data_filename(data.value), 'utf-8')
            )

        self.anims = {}
        ImageFile.LOAD_TRUNCATED_IMAGES = True
        for data in FlcFile:
            self.anims[data.name] = {
                'im': Image.open(data_filename(data.value['im'])),
                'count': 0,
            }

        self.copy_background_surface = None

    def init_background(self) -> sdl2.surface.SDL_Surface:
        bg = sdl2.ext.load_img(data_filename('ax_background.pcx'))
        border = sdl2.ext.load_img(data_filename('axstart_border.pcx'))

        # Create a green version of 'bg'
        bg_green = sdl2.SDL_DuplicateSurface(bg)
        tmp = sdl2.SDL_DuplicateSurface(bg_green)
        sdl2.ext.fill(tmp, sdl2.ext.Color(0, 95, 20))
        sdl2.SDL_SetSurfaceAlphaMod(tmp, 100)
        sdl2.SDL_BlitSurface(tmp, None, bg_green, None)
        sdl2.SDL_FreeSurface(tmp)

        # Create a big black surface 'tmp' and center 'bg_green' on it
        tmp = sdl2.SDL_DuplicateSurface(border)
        sdl2.ext.fill(tmp, 0)
        rect = sdl2.rect.SDL_Rect(400, 300, 800, 600)
        sdl2.SDL_BlitSurface(bg_green, None, tmp, rect)

        # Create a big black surface 'final_bg' and center 'bg' on it
        final_bg = sdl2.SDL_DuplicateSurface(border)
        sdl2.ext.fill(final_bg, 0)
        rect = sdl2.rect.SDL_Rect(400, 300, 800, 600)
        sdl2.SDL_BlitSurface(bg, None, final_bg, rect)

        # Apply 'border' with AlphaColorKey to 'tmp' (black -> alpha)
        key = sdl2.pixels.SDL_MapRGB(border.format, 0, 0, 0)
        sdl2.SDL_SetColorKey(border, True, key)
        sdl2.SDL_BlitSurface(border, None, tmp, None)

        # Apply 'tmp' with AlphaColorKey to 'final_bg' (pink -> alpha)
        key = sdl2.pixels.SDL_MapRGB(border.format, 100, 16, 156)
        sdl2.SDL_SetColorKey(tmp, True, key)
        sdl2.SDL_BlitSurface(tmp, None, final_bg, None)

        sdl2.SDL_FreeSurface(bg)
        sdl2.SDL_FreeSurface(bg_green)
        sdl2.SDL_FreeSurface(border)
        sdl2.SDL_FreeSurface(tmp)

        return final_bg

    def show_splash_screen(self) -> None:
        screen = self.window.get_surface()
        splash = sdl2.ext.load_img(data_filename('loki_logo1024.pcx'))
        sdl2.SDL_BlitSurface(splash, None, screen, None)
        self.window.refresh()
        sdl2.SDL_Delay(3000)

    def show_main_screen(self) -> None:
        screen = self.window.get_surface()

        background_image = self.init_background()
        sdl2.SDL_BlitSurface(
            background_image,
            sdl2.rect.SDL_Rect(288, 216, 1024, 768),
            screen,
            None
        )
        sdl2.SDL_FreeSurface(background_image)

        txt_rendered = self.font.render_text('CHOOSE YOUR WORLD')
        sdl2.SDL_BlitSurface(
            txt_rendered,
            None,
            screen,
            sdl2.rect.SDL_Rect(493, 101, 0, 0)
        )
        sdl2.SDL_FreeSurface(txt_rendered)

        self.copy_background_surface = sdl2.SDL_DuplicateSurface(screen)

        for b in Button:
            txt_rendered = self.font.render_text(b.value['txt'], 'green')
            txt_rect = sdl2.rect.SDL_Rect(
                b.value['txt_x'],
                b.value['txt_y'],
                txt_rendered.w,
                txt_rendered.h
            )
            sdl2.SDL_BlitSurface(txt_rendered, None, screen, txt_rect)
            sdl2.SDL_FreeSurface(txt_rendered)

        self.window.refresh()

    def setButtonStyle(self, highlight: Button, style: str) -> None:
        screen = self.window.get_surface()
        rect = highlight.value['rect']

        # Cleanup previous text
        sdl2.SDL_BlitSurface(self.copy_background_surface, rect, screen, rect)

        # Render new text
        txt_rendered = self.font.render_text(highlight.value['txt'], style)
        txt_rect = sdl2.rect.SDL_Rect(
            highlight.value['txt_x'],
            highlight.value['txt_y'],
            txt_rendered.w,
            txt_rendered.h
        )
        sdl2.SDL_BlitSurface(txt_rendered, None, screen, txt_rect)
        sdl2.SDL_FreeSurface(txt_rendered)

        sdl2.SDL_UpdateWindowSurfaceRects(self.window.window, rect, 1)

    def refresh_animations(
            self,
            animations: list,
    ) -> None:
        screen = self.window.get_surface()
        for anim in animations:
            im = self.anims[anim]['im']
            self.anims[anim]['count'] += 1
            if self.anims[anim]['count'] >= im.n_frames:
                self.anims[anim]['count'] = 1

            im.seek(self.anims[anim]['count'])
            surface = sdl2.ext.image.pillow_to_surface(im)
            x = FlcFile[anim].value['x']
            y = FlcFile[anim].value['y']
            rect = sdl2.rect.SDL_Rect(x, y, im.width, im.height)
            sdl2.SDL_BlitSurface(surface, None, screen, rect)
            sdl2.SDL_UpdateWindowSurfaceRects(self.window.window, rect, 1)
            sdl2.SDL_FreeSurface(surface)

    def launch(self, app: str) -> None:
        try:
            os.execvp(app, [app])
        except OSError as e:
            print("%s: %s" % (app, e))

    def run(self) -> int:
        rect_button_smac = Button.SMAC.value['rect']
        rect_button_smacx = Button.SMACX.value['rect']
        rect_button_quit = Button.CANCEL.value['rect']
        rect_anim_smac = sdl2.rect.SDL_Rect(142, 154, 320, 256)
        rect_anim_smacx = sdl2.rect.SDL_Rect(542, 214, 274, 205)

        self.window.show()
        self.show_splash_screen()
        self.show_main_screen()

        running = True
        animations = ['FLARE']
        highlight = None
        while running:
            events = sdl2.ext.get_events()
            for event in events:
                if event.type == sdl2.SDL_QUIT:
                    running = False
                    break
                elif event.type == sdl2.SDL_MOUSEBUTTONDOWN:
                    x = event.motion.x
                    y = event.motion.y
                    if is_in_rect(rect_button_smac, x, y):
                        self.launch('smac')
                    elif is_in_rect(rect_button_smacx, x, y):
                        self.launch('smacx')
                    elif is_in_rect(rect_button_quit, x, y):
                        running = False
                elif event.type == sdl2.SDL_MOUSEMOTION:
                    x = event.motion.x
                    y = event.motion.y
                    if is_in_rect(rect_button_smac, x, y) or \
                       is_in_rect(rect_anim_smac, x, y):
                        if highlight != Button.SMAC:
                            highlight = Button.SMAC
                            self.setButtonStyle(highlight, 'green_highlight')
                            sdlmixer.Mix_PlayChannel(-1,
                                                     self.sounds['SMAC'], -1)
                            animations.append('SMAC')
                    elif is_in_rect(rect_button_smacx, x, y) or is_in_rect(
                                    rect_anim_smacx, x, y):
                        if highlight != Button.SMACX:
                            highlight = Button.SMACX
                            self.setButtonStyle(highlight, 'green_highlight')
                            sdlmixer.Mix_PlayChannel(-1,
                                                     self.sounds['SMACX'], -1)
                            animations.append('SMACX')
                    elif is_in_rect(rect_button_quit, x, y):
                        if highlight != Button.CANCEL:
                            highlight = Button.CANCEL
                            self.setButtonStyle(highlight, 'green_highlight')
                    else:
                        if highlight is not None:
                            if highlight == Button.CANCEL:
                                self.setButtonStyle(highlight, 'green')
                            elif highlight == Button.SMAC:
                                self.setButtonStyle(highlight, 'green')
                                animations.remove('SMAC')
                            elif highlight == Button.SMACX:
                                self.setButtonStyle(highlight, 'green')
                                animations.remove('SMACX')

                            highlight = None
                            sdlmixer.Mix_HaltChannel(-1)

            self.refresh_animations(animations)
            sdl2.SDL_Delay(70)

        sdlmixer.Mix_CloseAudio()
        sdl2.ext.quit()
        return 0


if __name__ == "__main__":
    os.environ['SDL_VIDEO_WAYLAND_WMCLASS'] = 'smacpack'
    os.environ['SDL_VIDEO_X11_WMCLASS'] = 'smacpack'
    smackpack = SMACPack()
    sys.exit(smackpack.run())
