#!/usr/bin/env python3
"""
vocab_256.py
------------
256-word vocabulary for Helix glyph-to-language projection.

Structure: 64 base indices × 4 semantic lanes (pos_mod4)
Integrates with MorphSAT position-based adjacency.

Base64 alphabet maps to 64 base indices.
Position mod 4 selects semantic lane:
  0: ENTITY (nouns/objects)
  1: ACTION (verbs)
  2: QUALITY (adjectives/modifiers)
  3: RELATION (prepositions/conjunctions)
"""

from typing import List, Tuple

# Semantic lanes (pos_mod4 = 0, 1, 2, 3)
LANE_ENTITY = 0    # nouns, objects, concepts
LANE_ACTION = 1    # verbs, processes
LANE_QUALITY = 2   # adjectives, modifiers
LANE_RELATION = 3  # prepositions, conjunctions, connectors

# 64 words per lane = 256 total vocabulary
# Organized by base64 alphabet order (A-Za-z0-9+/)

VOCAB_ENTITY = [
    # A-Z (0-25): concrete → abstract
    "world", "space", "time", "light", "force", "wave", "field", "form",
    "mind", "soul", "heart", "body", "cell", "atom", "core", "shell",
    "seed", "root", "stem", "leaf", "flow", "path", "gate", "bridge",
    "star", "void",
    # a-z (26-51): human/abstract
    "thought", "dream", "memory", "vision", "truth", "order", "chaos", "balance",
    "energy", "matter", "signal", "pattern", "cycle", "phase", "state", "mode",
    "layer", "depth", "height", "width", "center", "edge", "origin", "end",
    "point", "line",
    # 0-9 (52-61): meta/system
    "zero", "one", "pair", "triple", "quad", "penta", "hexa", "septa",
    "octa", "nona",
    # +/ (62-63): special
    "plus", "ratio",
]

VOCAB_ACTION = [
    # A-Z (0-25): fundamental actions
    "be", "become", "exist", "emerge", "arise", "form", "flow", "move",
    "grow", "change", "shift", "turn", "spin", "fold", "bind", "link",
    "create", "destroy", "build", "break", "open", "close", "rise", "fall",
    "expand", "contract",
    # a-z (26-51): cognitive/complex
    "think", "feel", "sense", "know", "learn", "teach", "see", "hear",
    "speak", "write", "read", "parse", "compute", "encode", "decode", "hash",
    "search", "find", "seek", "reach", "hold", "release", "take", "give",
    "merge", "split",
    # 0-9 (52-61): system ops
    "init", "load", "save", "send", "receive", "process", "filter", "sort",
    "map", "reduce",
    # +/ (62-63): special
    "add", "divide",
]

VOCAB_QUALITY = [
    # A-Z (0-25): fundamental qualities
    "new", "old", "true", "false", "real", "virtual", "deep", "shallow",
    "high", "low", "fast", "slow", "hot", "cold", "bright", "dark",
    "dense", "sparse", "stable", "volatile", "pure", "mixed", "whole", "partial",
    "clear", "hidden",
    # a-z (26-51): abstract qualities
    "strong", "weak", "sharp", "soft", "smooth", "rough", "open", "closed",
    "inner", "outer", "upper", "lower", "first", "last", "next", "prior",
    "active", "passive", "latent", "manifest", "local", "global", "finite", "infinite",
    "symmetric", "asymmetric",
    # 0-9 (52-61): quantitative
    "minimal", "maximal", "optimal", "suboptimal", "exact", "approximate", "discrete", "continuous",
    "linear", "nonlinear",
    # +/ (62-63): special
    "positive", "inverse",
]

VOCAB_RELATION = [
    # A-Z (0-25): spatial/logical
    "in", "on", "at", "to", "from", "with", "through", "across",
    "above", "below", "before", "after", "within", "beyond", "near", "far",
    "toward", "away", "along", "against", "between", "among", "around", "beside",
    "over", "under",
    # a-z (26-51): logical connectors
    "and", "or", "but", "if", "then", "else", "when", "while",
    "as", "like", "than", "because", "therefore", "however", "although", "unless",
    "until", "since", "during", "despite", "except", "including", "regarding", "concerning",
    "per", "via",
    # 0-9 (52-61): structural
    "equals", "contains", "precedes", "follows", "implies", "requires", "enables", "prevents",
    "maps", "links",
    # +/ (62-63): special
    "plus", "over",
]

# Combined vocabulary: 4 lanes × 64 words = 256 words
VOCAB_256 = VOCAB_ENTITY + VOCAB_ACTION + VOCAB_QUALITY + VOCAB_RELATION


def vocab_lookup(base_idx: int, pos_mod4: int) -> str:
    """
    Look up word from base index and position.

    Args:
        base_idx: Base64 alphabet index (0-63)
        pos_mod4: Position mod 4 (0=entity, 1=action, 2=quality, 3=relation)

    Returns:
        Word from vocabulary
    """
    base_idx = base_idx % 64  # clamp to 64
    pos_mod4 = pos_mod4 % 4   # clamp to 4

    lanes = [VOCAB_ENTITY, VOCAB_ACTION, VOCAB_QUALITY, VOCAB_RELATION]
    return lanes[pos_mod4][base_idx]


def vocab_lookup_256(idx: int) -> str:
    """
    Direct lookup into 256-word vocabulary.

    Args:
        idx: Index 0-255

    Returns:
        Word from vocabulary
    """
    return VOCAB_256[idx % 256]


def attention_to_words(
    basis_indices: List[int],
    use_position: bool = True
) -> List[str]:
    """
    Convert basis indices to words using position-aware vocabulary.

    Args:
        basis_indices: List of basis indices from attention
        use_position: If True, use position mod 4 for lane selection

    Returns:
        List of words
    """
    words = []
    for pos, idx in enumerate(basis_indices):
        if use_position:
            word = vocab_lookup(idx, pos % 4)
        else:
            word = vocab_lookup_256(idx)
        words.append(word)
    return words


def get_vocab_info() -> dict:
    """Return vocabulary metadata."""
    return {
        "vocab_size": 256,
        "num_lanes": 4,
        "words_per_lane": 64,
        "lanes": ["entity", "action", "quality", "relation"],
        "base64_aligned": True,
        "morphsat_compatible": True,
    }


# Base64 alphabet for reference
B64_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"


def b64_char_to_idx(char: str) -> int:
    """Convert base64 character to index (0-63)."""
    idx = B64_ALPHABET.find(char)
    return idx if idx >= 0 else 0


def idx_to_b64_char(idx: int) -> str:
    """Convert index (0-63) to base64 character."""
    return B64_ALPHABET[idx % 64]


if __name__ == "__main__":
    # Test vocabulary
    print("VOCAB_256 Info:")
    info = get_vocab_info()
    for k, v in info.items():
        print(f"  {k}: {v}")

    print("\nSample lookups:")
    for base_idx in [0, 16, 32, 48, 63]:
        for pos in range(4):
            word = vocab_lookup(base_idx, pos)
            lane = ["entity", "action", "quality", "relation"][pos]
            print(f"  idx={base_idx:2d}, pos={pos} ({lane:8s}): {word}")

    print("\nPosition-aware sequence (idx=0,1,2,3,4,5,6,7):")
    indices = [0, 1, 2, 3, 4, 5, 6, 7]
    words = attention_to_words(indices, use_position=True)
    print(f"  {' '.join(words)}")

    print("\nDirect 256 lookup (idx=0,64,128,192):")
    for idx in [0, 64, 128, 192]:
        word = vocab_lookup_256(idx)
        print(f"  idx={idx:3d}: {word}")
