"""
自作のヘルパー関数をまとめたモジュール

    Last Modified: 2025/11/23

    Copyright © 2022 toshifumi tsutsui
    Released under the MIT license
    https://wpandora8.net/the_mit_license.html
"""

import os
import re
import sys
from datetime import timedelta
from typing import Literal


def sign(value: int | float) -> int:
    """指定された数値の符号を判定する。

    Args:
        value (int | float): 判定する数値。

    Returns:
        int: 正の数であれば 1、負の数であれば -1。
    """

    return (value > 0) - (value < 0)


def get_default_dir_path() -> str:
    """
    このコードを実行中の OS が Windows の場合はカレントディレクトリのフルパス、
    その他の場合はホームディレクトリのフルパスを返す。

    Returns:
        str: カレントディレクトリまたはホームディレクトリのフルパス。
    """

    if os.path.abspath(".")[0] == "/":
        return os.path.expanduser("~")
    else:
        return os.path.abspath(".")


def get_resource_path(relative_path: str) -> str:
    """
    Pyinstaller でビルドした際に、実行ファイルに埋め込まれたリソースファイルの展開先パスを取得して返す。
    バイナリ化されていなければ、現在のパスを返す。

    Args:
        relative_path (str): バイナリ化されていない状態でのリソースファイルの相対パス。

    Returns:
        str: 実行ファイルに埋め込まれたリソースファイルの展開先パス。
    """

    if hasattr(sys, "_MEIPASS"):
        base_path: str = sys._MEIPASS  # type: ignore
    else:
        base_path: str = os.path.abspath(".")

    return os.path.join(base_path, relative_path)


def get_excel_column_number(alpha: str) -> int:
    """
    Excel のワークシートのアルファベットでの列指定を、整数の列番号（1 から開始）に変換して返す。
    変換できない場合は 0 を返す。

    Args:
        alpha (str): Excel のアルファベットでの列指定。

    Returns:
        int: Excel のワークシートの列番号。
    """

    if not (0 < len(alpha) <= 3):
        return 0

    text: str = alpha.upper()

    if re.search("[^A-Z]", text) is not None:
        return 0

    if len(text) == 1:
        return ord(text) - 64
    elif len(text) == 2:
        return (ord(text[0]) - 64) * 26 + (ord(text[1]) - 64)
    else:
        first: int = (ord(text[0]) - 64) * 676
        second: int = (ord(text[1]) - 64) * 26
        third: int = ord(text[2]) - 64
        temp: int = first + second + third
        return temp if temp <= 16384 else 0


def timespan_to_string(
    span: timedelta | float,
    include_hour=True,
    decimal_places: Literal[0, 1, 2, 3] = 3,
    pattern: Literal[0, 1, 2] = 0,
) -> str:
    """timedelta 型または float 型の時間差データを 00:00:00.000 形式の文字列に変換して返す。

    Args:
        span (timedelta | float): 時間データ（float 型の場合は秒数）。
        include_hour (bool, optional): 「時」を含める場合は True。
        decimal_places (Literal[0, 1, 2, 3], optional): 小数点以下の桁数。
        pattern (Literal[0, 1, 2], optional): 表示形式（0: 00:00:00.000、1: 00ﾟ00'00"000、2: 00h00m00s000）

    Returns:
        str: 時間差をあらわす 00:00:00.000 形式の文字列。
    """

    ms: int = 0
    s: float = 0.0
    m: float = 0.0
    h: float = 0.0

    separators: list[tuple[str, str, str]] = [
        (":", ":", "."),
        ("°", "'", '"'),
        ("h", "m", "s"),
    ]

    h_sep, m_sep, s_sep = separators[pattern]

    if type(span) is timedelta:
        ms = int(span.microseconds * (10**decimal_places) / 1_000_000)
        m, s = divmod(span.total_seconds(), 60)
    elif type(span) is float:
        ms = int((span % 1) * (10**decimal_places))
        m, s = divmod(span, 60)

    if include_hour and (decimal_places > 0):
        h, m = divmod(m, 60)
        to_sec: str = f"{int(h):0>2}{h_sep}{int(m):0>2}{m_sep}{int(s):0>2}"
        return f"{to_sec}{s_sep}{ms:0>{decimal_places}}"
    elif include_hour and (decimal_places == 0) and (pattern == 0):
        h, m = divmod(m, 60)
        return f"{int(h):0>2}{h_sep}{int(m):0>2}{m_sep}{int(s):0>2}"
    elif include_hour and (decimal_places == 0):
        h, m = divmod(m, 60)
        return f"{int(h):0>2}{h_sep}{int(m):0>2}{m_sep}{int(s):0>2}{s_sep}"
    elif (not include_hour) and (decimal_places > 0):
        return f"{int(m):0>2}{m_sep}{int(s):0>2}{s_sep}{ms:0>{decimal_places}}"
    else:
        if pattern == 0:
            return f"{int(m):0>2}{m_sep}{int(s):0>2}"
        else:
            return f"{int(m):0>2}{m_sep}{int(s):0>2}{s_sep}"
