#!/usr/bin/env python3
# -*- coding: utf-8 -*-

# Fenrir TTY screen reader
# By Chrys, Storm Dragon, and contributors.

import datetime
import fcntl
import os
import tempfile
import time

from fenrirscreenreader.core.i18n import _


ANNOUNCEMENT_LOCK_TIMEOUT_SEC = 5.0


class command:
    def __init__(self):
        pass

    def initialize(self, environment):
        self.env = environment
        self.last_time = datetime.datetime.now()
        self.lastDateString = ""
        self.lastTimeString = ""

    def shutdown(self):
        pass

    def get_description(self):
        return "No Description found"

    def _get_announcement_lock_path(self):
        return os.path.join(
            tempfile.gettempdir(),
            "fenrirscreenreader-time-announcement.lock",
        )

    def _read_announcement_lock_slot(self, lock_file):
        lock_file.seek(0)
        lock_content = lock_file.readline().strip().split()
        if len(lock_content) < 2:
            return ""
        return lock_content[1]

    def _claim_announcement_lock(self, announcement_slot):
        now = time.time()
        lock_path = self._get_announcement_lock_path()
        try:
            lock_fd = os.open(lock_path, os.O_CREAT | os.O_RDWR, 0o666)
        except OSError:
            return False
        try:
            os.chmod(lock_path, 0o666)
        except OSError:
            pass

        with os.fdopen(lock_fd, "r+", encoding="utf-8") as lock_file:
            try:
                fcntl.flock(lock_file.fileno(), fcntl.LOCK_EX)
                lock_slot = self._read_announcement_lock_slot(lock_file)
                lock_stat = os.fstat(lock_file.fileno())

                if lock_slot == announcement_slot:
                    return False

                lock_file.seek(0)
                lock_file.truncate()
                lock_file.write(f"{os.getpid()} {announcement_slot} {now}\n")
                lock_file.flush()
                os.fsync(lock_file.fileno())
                return True
            finally:
                fcntl.flock(lock_file.fileno(), fcntl.LOCK_UN)

    def run(self):
        if not self.env["runtime"]["SettingsManager"].get_setting_as_bool(
            "time", "enabled"
        ):
            return

        on_minutes = self.env["runtime"]["SettingsManager"].get_setting(
            "time", "on_minutes"
        )
        delay_sec = self.env["runtime"]["SettingsManager"].get_setting_as_int(
            "time", "delay_sec"
        )

        # no need
        if on_minutes == "" and delay_sec <= 0:
            return

        on_minutes = on_minutes.split(",")
        now = datetime.datetime.now()

        # ignore on_minutes if there is a delay_sec
        if delay_sec > 0:
            if int((now - self.last_time).total_seconds()) < delay_sec:
                return
            announcement_slot = f"delay:{int(now.timestamp()) // delay_sec}"
        else:
            # should announce?
            if not str(now.minute).zfill(2) in on_minutes:
                return
            # already announced?
            if now.hour == self.last_time.hour:
                if now.minute == self.last_time.minute:
                    return
            announcement_slot = f"minute:{datetime.datetime.strftime(now, '%Y%m%d%H%M')}"

        date_format = self.env["runtime"]["SettingsManager"].get_setting(
            "general", "date_format"
        )
        date_string = datetime.datetime.strftime(now, date_format)

        present_date = (
            self.env["runtime"]["SettingsManager"].get_setting_as_bool(
                "time", "present_date"
            )
            and self.lastDateString != date_string
        )
        present_time = self.env["runtime"][
            "SettingsManager"
        ].get_setting_as_bool("time", "present_time")

        # no changed value to announce
        if not (present_date or present_time):
            return

        if not self._claim_announcement_lock(announcement_slot):
            self.last_time = now
            return

        time_format = self.env["runtime"]["SettingsManager"].get_setting(
            "general", "time_format"
        )
        time_string = datetime.datetime.strftime(now, time_format)

        if self.env["runtime"]["SettingsManager"].get_setting_as_bool(
            "time", "interrupt"
        ):
            self.env["runtime"]["OutputManager"].interrupt_output()
        if self.env["runtime"]["SettingsManager"].get_setting_as_bool(
            "time", "announce"
        ):
            self.env["runtime"]["OutputManager"].play_sound_icon("announce")

        if present_time:
            # present the time
            self.env["runtime"]["OutputManager"].present_text(
                _("It's {0}").format(
                    time_string.replace(":00", " O'clock ").lstrip("0")
                ),
                sound_icon="",
                interrupt=False,
            )
            # Check if it's 12:00 AM
            if now.hour == 0 and now.minute == 0:
                # present the date
                self.env["runtime"]["OutputManager"].present_text(
                    date_string, sound_icon="", interrupt=False
                )

        self.last_time = datetime.datetime.now()
        self.lastTimeString = time_string

    def set_callback(self, callback):
        pass
