# -*- coding: utf-8 -*-
"""Sync romaji CV, V, and VV oto.ini entries to their hiragana counterparts (same file only)."""

import re

# Romaji to hiragana mapping for matching (including extended kana)
# Mini vowels (di, ti, si, du, tu) -> でぃ てぃ すぃ どぅ とぅ (also try でい てい すい どう とう)
# shi, chi, dji(ぢ), dzu(づ), tsu stay -> し ち ぢ づ つ
VOWEL_H = {'a': 'あ', 'i': 'い', 'u': 'う', 'e': 'え', 'o': 'お'}

ROMAJI_TO_HIRAGANA = {
    # Vowels
    'a': 'あ', 'i': 'い', 'u': 'う', 'e': 'え', 'o': 'お', 'n': 'ん',
    # CV - k
    'ka': 'か', 'ki': 'き', 'ku': 'く', 'ke': 'け', 'ko': 'こ',
    'kya': 'きゃ', 'kyu': 'きゅ', 'kyo': 'きょ', 'kye': 'きぇ',
    # CV - g
    'ga': 'が', 'gi': 'ぎ', 'gu': 'ぐ', 'ge': 'げ', 'go': 'ご',
    'gya': 'ぎゃ', 'gyu': 'ぎゅ', 'gyo': 'ぎょ', 'gye': 'ぎぇ',
    # CV - s (shi/し, si->すい for mini vowel)
    'sa': 'さ', 'si': 'すい', 'shi': 'し', 'su': 'す', 'se': 'せ', 'so': 'そ',
    'sha': 'しゃ', 'shu': 'しゅ', 'sho': 'しょ', 'she': 'しぇ', 'sye': 'しぇ',
    # CV - z
    'za': 'ざ', 'zi': 'じ', 'ji': 'じ', 'zu': 'ず', 'ze': 'ぜ', 'zo': 'ぞ',
    'ja': 'じゃ', 'ju': 'じゅ', 'jo': 'じょ', 'je': 'じぇ', 'jye': 'じぇ',
    # CV - t (chi/ち, tsu/つ; ti->てい tu->とう for mini vowel)
    'ta': 'た', 'ti': 'てい', 'chi': 'ち', 'tu': 'とう', 'tsu': 'つ', 'te': 'て', 'to': 'と',
    'tya': 'ちゃ', 'cha': 'ちゃ', 'tyu': 'ちゅ', 'chu': 'ちゅ', 'tyo': 'ちょ', 'cho': 'ちょ',
    'che': 'ちぇ', 'tye': 'ちぇ',
    # CV - d (dji/ぢ, dzu/づ; di->でい du->どう for mini vowel)
    'da': 'だ', 'di': 'でい', 'dji': 'ぢ', 'du': 'どう', 'dzu': 'づ', 'de': 'で', 'do': 'ど',
    'dya': 'ぢゃ', 'dye': 'ぢぇ', 'dyo': 'ぢょ', 'dyu': 'ぢゅ',
    # CV - n
    'na': 'な', 'ni': 'に', 'nu': 'ぬ', 'ne': 'ね', 'no': 'の',
    'nya': 'にゃ', 'nyu': 'にゅ', 'nyo': 'にょ', 'nye': 'にぇ',
    "n'a": "んあ", "n'i": "んい", "n'u": "んう", "n'e": "んえ", "n'o": "んお",
    'nh a': 'にゃ', 'nh e': 'にぇ', 'nh o': 'にょ', 'nh u': 'にゅ',
    # CV - h
    'ha': 'は', 'hi': 'ひ', 'hu': 'ふ', 'he': 'へ', 'ho': 'ほ',
    'hya': 'ひゃ', 'hyu': 'ひゅ', 'hyo': 'ひょ', 'hye': 'ひぇ',
    # CV - b
    'ba': 'ば', 'bi': 'び', 'bu': 'ぶ', 'be': 'べ', 'bo': 'ぼ',
    'bya': 'びゃ', 'byu': 'びゅ', 'byo': 'びょ', 'bie': 'びぇ', 'bye': 'びぇ',
    # CV - p
    'pa': 'ぱ', 'pi': 'ぴ', 'pu': 'ぷ', 'pe': 'ぺ', 'po': 'ぽ',
    'pya': 'ぴゃ', 'pyu': 'ぴゅ', 'pyo': 'ぴょ', 'pie': 'ぴぇ', 'pye': 'ぴぇ',
    # CV - m
    'ma': 'ま', 'mi': 'み', 'mu': 'む', 'me': 'め', 'mo': 'も',
    'mya': 'みゃ', 'myu': 'みゅ', 'myo': 'みょ', 'mie': 'みぇ', 'mye': 'みぇ',
    # CV - r
    'ra': 'ら', 'ri': 'り', 'ru': 'る', 're': 'れ', 'ro': 'ろ',
    'rya': 'りゃ', 'ryu': 'りゅ', 'ryo': 'りょ', 'rie': 'りぇ', 'rye': 'りぇ',
    # CV - y, w
    'ya': 'や', 'yi': 'い', 'yu': 'ゆ', 'ye': 'いぇ', 'yo': 'よ',
    'wa': 'わ', 'wi': 'うぃ', 'wu': 'う', 'we': 'うぇ', 'wo': 'を',
    # CV - f, v, l, ts
    'fa': 'ふぁ', 'fi': 'ふぃ', 'fu': 'ふ', 'fe': 'ふぇ', 'fo': 'ふぉ',
    'fya': 'ふゃ', 'fyu': 'ふゅ', 'fye': 'ふぃぇ', 'fyo': 'ふょ',
    'va': 'ヴぁ', 'vi': 'ヴぃ', 'vu': 'ヴ', 've': 'ヴぇ', 'vo': 'ヴぉ',
    'la': 'ら', 'li': 'り', 'lu': 'る', 'le': 'れ', 'lo': 'ろ',
    'tsa': 'つぁ', 'tsi': 'つぃ', 'tsu': 'つ', 'tse': 'つぇ', 'tso': 'つぉ',
}

# VV patterns: "vowel vowel" like "a a", "a i" (romaji); file uses "a あ" (romaji + hiragana)
VOWELS = {'a', 'e', 'i', 'o', 'u'}

def is_romaji_only(s):
    """Check if string contains only ASCII romaji (no hiragana/katakana)."""
    return all(ord(c) < 256 or c in " '-" for c in s.strip())

def is_vc_entry(alias):
    """VC = vowel + consonant, e.g. 'a b', 'e ch', 'i k' - these have form 'V C' or 'V Cy'."""
    parts = alias.strip().split()
    if len(parts) != 2:
        return False
    v, c = parts[0], parts[1]
    if v not in VOWELS and v != 'n':
        return False
    # Second part is consonant (not a vowel)
    if c in VOWELS or len(c) == 0:
        return False
    return True

def is_cv_entry(alias):
    """CV = single syllable like ka, ba, sa (consonant+vowel)."""
    a = alias.strip().lower()
    if ' ' in a or '-' in a or len(a) < 2:
        return False
    return a in ROMAJI_TO_HIRAGANA or (a[0] not in VOWELS and a[0] != 'n')

def is_v_entry(alias):
    """V = single vowel: a, i, u, e, o, n."""
    a = alias.strip().lower()
    return a in ('a', 'i', 'u', 'e', 'o', 'n') and len(a) == 1 or a == 'n'

def is_vv_entry(alias):
    """VV = romaji form: 'a a', 'a i', 'n i', '- a', etc. (vowel/vowel, n/vowel, -/vowel, vowel/n)."""
    parts = alias.strip().split()
    if len(parts) != 2:
        return False
    p0, p1 = parts[0].lower(), parts[1].lower()
    # First: vowel, n, or -
    if p0 not in VOWELS and p0 not in ('n', '-'):
        return False
    # Second: vowel or n
    return p1 in VOWELS or p1 == 'n'

def is_nh_entry(alias):
    """nh x = nya/nye/nyo/nyu (e.g. 'nh a' -> にゃ)."""
    parts = alias.strip().split()
    return len(parts) == 2 and parts[0].lower() == 'nh' and parts[1].lower() in VOWELS

# Alternate hiragana for mini vowels (file may use でぃ or でい etc.)
MINI_VOWEL_ALT = {'di': ['でい', 'でぃ'], 'ti': ['てい', 'てぃ'], 'si': ['すい', 'すぃ'], 'du': ['どう', 'どぅ'], 'tu': ['とう', 'とぅ']}

def get_hiragana_for_romaji(alias):
    """Get hiragana form for romaji alias. VV uses 'romaji + hiragana' format (e.g. 'a あ')."""
    a = alias.strip().lower()
    if a in ROMAJI_TO_HIRAGANA:
        return ROMAJI_TO_HIRAGANA[a]
    if is_vv_entry(alias):
        v1, v2 = alias.strip().lower().split()
        h = {'a': 'あ', 'i': 'い', 'u': 'う', 'e': 'え', 'o': 'お', 'n': 'ん'}
        return v1 + ' ' + h.get(v2, v2)
    return None

def get_hiragana_candidates(alias):
    """Return list of possible hiragana forms (for mini vowels with alternates)."""
    base = get_hiragana_for_romaji(alias)
    if not base:
        return []
    alts = MINI_VOWEL_ALT.get(alias.strip().lower(), [])
    return list(dict.fromkeys([base] + alts))  # Try base first, then alternates

def parse_line(line):
    """Parse oto line: filename.wav=alias,offset,consonant,cutoff,preutterance,overlap"""
    m = re.match(r'^([^=]+\.wav)=([^,]+),(.+)$', line.strip())
    if not m:
        return None
    fname, alias, values = m.group(1), m.group(2), m.group(3)
    return (fname, alias, values)

def main():
    import sys
    base_dir = r"D:\OpenUtau\Singers\Irone Raiko 2.0"
    # Usage: python sync_oto.py [power|neutral|soft] [start_line]
    bank = sys.argv[1] if len(sys.argv) > 1 else "power"
    start_line = int(sys.argv[2]) if len(sys.argv) > 2 else 505

    if bank.lower() == "neutral":
        path = base_dir + r"\A3-Neutral\oto.ini"
        suffix = "_N"
    elif bank.lower() == "soft":
        path = base_dir + r"\A3-Soft\oto.ini"
        suffix = "_S"
    else:
        path = base_dir + r"\A3-Power\oto.ini"
        suffix = "_P"

    print(f"Bank: {bank}, path: {path}, start_line: {start_line}, suffix: '{suffix}'")

    with open(path, 'r', encoding='shift_jis', errors='replace') as f:
        lines = f.readlines()

    # Build hiragana lookup by file from ALL lines
    hiragana_by_file = {}
    for i, line in enumerate(lines):
        if not line.strip():
            continue
        p = parse_line(line)
        if not p:
            continue
        fname, alias, values = p
        if fname not in hiragana_by_file:
            hiragana_by_file[fname] = {}
        hiragana_by_file[fname][alias] = values

    # Find romaji CV/V/VV entries from line 505 down and sync from hiragana (same file only)
    changes = []
    for i, line in enumerate(lines):
        if i + 1 < start_line:  # Skip lines above 505
            continue
        if not line.strip():
            continue
        p = parse_line(line)
        if not p:
            continue
        fname, alias, values = p

        # Strip bank suffix for lookup (e.g. di_N -> di)
        alias_base = alias[:-len(suffix)] if suffix and alias.endswith(suffix) else alias

        if not is_romaji_only(alias_base):
            continue
        if is_vc_entry(alias_base):
            continue

        candidates = []
        if is_v_entry(alias_base):
            candidates = get_hiragana_candidates(alias_base)
        elif is_nh_entry(alias_base):
            candidates = get_hiragana_candidates(alias_base)
        elif is_cv_entry(alias_base):
            candidates = get_hiragana_candidates(alias_base)
        elif is_vv_entry(alias_base):
            h = get_hiragana_for_romaji(alias_base)
            candidates = [h] if h else []

        if not candidates:
            continue

        file_hirag = hiragana_by_file.get(fname, {})
        target_hiragana = None
        for cand in candidates:
            key = cand + suffix
            if key in file_hirag:
                target_hiragana = key
                break
        if not target_hiragana:
            continue

        new_values = file_hirag[target_hiragana]
        new_line = f"{fname}={alias},{new_values}\n"
        if new_line != line:
            changes.append((i + 1, line.strip(), new_line.strip()))
            lines[i] = new_line

    if not changes:
        print("No changes needed.")
        return

    with open(path, 'w', encoding='shift_jis', newline='') as f:
        f.writelines(lines)

    print(f"Synced {len(changes)} entries:")
    for ln, old, new in changes[:20]:
        print(f"  L{ln}: {old[:60]}... -> {new[:60]}...")
    if len(changes) > 20:
        print(f"  ... and {len(changes)-20} more")

if __name__ == '__main__':
    main()
