"""
Historical 2D Studio - Tactical Battle Map Renderer (2026 Engine)
Optimized for Low-Spec / Multi-Core Hardware with NVENC Hardware Encoding.
Generates 100% copyright-free procedural battle maps with War Room aesthetic.
"""

import json
import math
import os
import subprocess
import sys
import numpy as np
import cv2
from PIL import Image, ImageDraw, ImageFont

def create_parchment_background(width, height):
    """
    Tạo texture bản đồ cổ phong cách Chiaroscuro hoàn toàn bằng thuật toán,
    100% không vi phạm bản quyền hình ảnh.
    """
    # Màu nền vintage sẫm màu
    base = np.full((height, width, 3), [24, 28, 34], dtype=np.uint8)  # BGR Dark slate parchment
    
    # Tạo vân giấy sần sùi ngẫu nhiên (noise layer)
    noise = np.random.normal(0, 8, (height, width, 3)).astype(np.int16)
    bg = np.clip(base.astype(np.int16) + noise, 0, 255).astype(np.uint8)
    
    # Thêm lưới tọa độ hàng hải cổ điển (Cartographic Grid Lines)
    grid_color = (35, 42, 50)
    for x in range(0, width, 160):
        cv2.line(bg, (x, 0), (x, height), grid_color, 1, cv2.LINE_AA)
    for y in range(0, height, 160):
        cv2.line(bg, (0, y), (width, y), grid_color, 1, cv2.LINE_AA)
        
    # Tạo dòng sông Aufidus (River Ofanto) uốn lượn chiến thuật
    river_pts = []
    for y in range(0, height, 20):
        x = int(320 + 70 * math.sin(y * 0.005) + 30 * math.sin(y * 0.015))
        river_pts.append((x, y))
    river_pts = np.array(river_pts, np.int32).reshape((-1, 1, 2))
    
    # Sông: Bờ sông mờ và lòng sông xanh rêu trầm
    cv2.polylines(bg, [river_pts], False, (45, 55, 60), 22, cv2.LINE_AA)
    cv2.polylines(bg, [river_pts], False, (70, 85, 75), 14, cv2.LINE_AA)
    cv2.polylines(bg, [river_pts], False, (95, 115, 100), 4, cv2.LINE_AA)
    
    # Hiệu ứng Vignette tối góc (Tập trung ánh sáng vào trung tâm sa bàn)
    x = np.linspace(-1, 1, width)
    y = np.linspace(-1, 1, height)
    X, Y = np.meshgrid(x, y)
    radius = np.sqrt(X**2 + Y**2)
    vignette = np.clip(1.0 - (radius * 0.55)**2, 0.25, 1.0)
    for c in range(3):
        bg[:, :, c] = np.clip(bg[:, :, c] * vignette, 0, 255).astype(np.uint8)
        
    return bg

def draw_tactical_unit(img, x, y, w, h, name, side, unit_type, color_rgb, strength=""):
    """
    Vẽ khối quân bài gỗ/sơn mài (Tactical Wooden Counter) chuẩn sa bàn chiến thuật.
    """
    x1, y1 = int(x - w / 2), int(y - h / 2)
    x2, y2 = int(x + w / 2), int(y + h / 2)
    bgr_color = (int(color_rgb[2]), int(color_rgb[1]), int(color_rgb[0]))
    
    # 1. Đổ bóng khối (Drop Shadow) tạo cảm giác thẻ bài 3D đặt trên bản đồ
    shadow_offset = 6
    shadow_overlay = img.copy()
    cv2.rectangle(shadow_overlay, (x1 + shadow_offset, y1 + shadow_offset), 
                  (x2 + shadow_offset, y2 + shadow_offset), (0, 0, 0), -1)
    cv2.addWeighted(shadow_overlay, 0.45, img, 0.55, 0, img)
    
    # 2. Thân khối thẻ bài sơn mài
    cv2.rectangle(img, (x1, y1), (x2, y2), (20, 20, 20), -1)  # Nền tối
    cv2.rectangle(img, (x1 + 2, y1 + 2), (x2 - 2, y2 - 2), bgr_color, -1)  # Lớp sơn chính
    
    # 3. Viền nổi hoàng gia (Border highlights)
    cv2.rectangle(img, (x1, y1), (x2, y2), (255, 255, 255), 1, cv2.LINE_AA)
    
    # 4. Ký hiệu binh chủng (Tactical Symbol NATO/Classic)
    if unit_type == "cavalry":
        # Kỵ binh: Đường chéo chiến thuật
        cv2.line(img, (x1 + 8, y2 - 8), (x1 + 32, y1 + 8), (255, 255, 255), 2, cv2.LINE_AA)
    elif unit_type == "infantry":
        # Bộ binh: Dấu chéo chữ X cổ điển
        cv2.line(img, (x1 + 8, y1 + 8), (x1 + 32, y2 - 8), (255, 255, 255), 2, cv2.LINE_AA)
        cv2.line(img, (x1 + 8, y2 - 8), (x1 + 32, y1 + 8), (255, 255, 255), 2, cv2.LINE_AA)
        
    # 5. Tên đơn vị và quân số
    label = f"{name}"
    font = cv2.FONT_HERSHEY_SIMPLEX
    cv2.putText(img, label, (x1 + 38, y1 + int(h * 0.55)), font, 0.42, (255, 255, 255), 1, cv2.LINE_AA)
    if strength:
        cv2.putText(img, f"[{strength}]", (x1 + 38, y2 - 5), font, 0.35, (200, 220, 240), 1, cv2.LINE_AA)

def get_bezier_point(p0, p1, p2, t):
    """Tính tọa độ điểm trên đường cong Bézier bậc 2."""
    inv = 1.0 - t
    x = inv * inv * p0[0] + 2 * inv * t * p1[0] + t * t * p2[0]
    y = inv * inv * p0[1] + 2 * inv * t * p1[1] + t * t * p2[1]
    return int(x), int(y)

def draw_tactical_arrow(img, p_from, p_ctrl, p_to, progress, color_rgb, label=""):
    """
    Vẽ mũi tên tiến công uốn lượn Bézier mượt mà với đầu mũi tên động.
    """
    if progress <= 0.05:
        return
        
    bgr_color = (int(color_rgb[2]), int(color_rgb[1]), int(color_rgb[0]))
    pts = []
    steps = max(5, int(40 * progress))
    for i in range(steps + 1):
        t = (i / 40.0)
        if t > progress:
            break
        pts.append(get_bezier_point(p_from, p_ctrl, p_to, t))
        
    if len(pts) >= 2:
        # Đường phát sáng phía ngoài (Outer Glow)
        glow_pts = np.array(pts, np.int32).reshape((-1, 1, 2))
        cv2.polylines(img, [glow_pts], False, (0, 0, 0), 10, cv2.LINE_AA)
        cv2.polylines(img, [glow_pts], False, bgr_color, 4, cv2.LINE_AA)
        
        # Vẽ đầu mũi tên tại vị trí hiện tại của mũi nhọn
        head_pt = pts[-1]
        prev_pt = pts[-2]
        angle = math.atan2(head_pt[1] - prev_pt[1], head_pt[0] - prev_pt[0])
        arrow_len = 22
        arrow_angle = math.pi / 6.0
        
        left_wing = (
            int(head_pt[0] - arrow_len * math.cos(angle - arrow_angle)),
            int(head_pt[1] - arrow_len * math.sin(angle - arrow_angle))
        )
        right_wing = (
            int(head_pt[0] - arrow_len * math.cos(angle + arrow_angle)),
            int(head_pt[1] - arrow_len * math.sin(angle + arrow_angle))
        )
        arrow_poly = np.array([head_pt, left_wing, right_wing], np.int32)
        cv2.fillPoly(img, [arrow_poly], bgr_color, cv2.LINE_AA)
        cv2.polylines(img, [arrow_poly], True, (255, 255, 255), 1, cv2.LINE_AA)
        
        # Nhãn chiến thuật
        if label and progress > 0.6:
            mid_pt = pts[len(pts) // 2]
            cv2.putText(img, label, (mid_pt[0] + 10, mid_pt[1] - 10), 
                        cv2.FONT_HERSHEY_SIMPLEX, 0.45, (220, 240, 255), 1, cv2.LINE_AA)

def draw_hud(img, battle_title, phase_name, current_time, total_time, rome_loss, carthage_loss):
    """
    Vẽ thanh tiêu đề sa bàn và bộ đếm thương vong thời gian thực (Casualty HUD).
    """
    h, w = img.shape[:2]
    
    # 1. Top Bar: Tiêu đề phong cách phòng tác chiến (War Room Banner)
    cv2.rectangle(img, (0, 0), (w, 80), (12, 14, 18), -1)
    cv2.line(img, (0, 80), (w, 80), (180, 150, 60), 2, cv2.LINE_AA)
    
    # Huy hiệu / Tiêu đề
    cv2.putText(img, battle_title.upper(), (40, 42), 
                cv2.FONT_HERSHEY_TRIPLEX, 0.85, (230, 205, 120), 1, cv2.LINE_AA)
    cv2.putText(img, f"STRATEGY DISPATCH: {phase_name.upper()}", (40, 68), 
                cv2.FONT_HERSHEY_SIMPLEX, 0.45, (160, 175, 190), 1, cv2.LINE_AA)
                
    # 2. Bottom HUD: Bộ đếm thương vong cơ học (Casualty Counters)
    cv2.rectangle(img, (0, h - 70), (w, h), (12, 14, 18), -1)
    cv2.line(img, (0, h - 70), (w, h - 70), (50, 60, 75), 1, cv2.LINE_AA)
    
    # Cột Rome (Đỏ)
    cv2.rectangle(img, (40, h - 55), (60, h - 25), (40, 40, 180), -1)
    cv2.putText(img, "ROMAN REPUBLIC LOSSES:", (75, h - 45), 
                cv2.FONT_HERSHEY_SIMPLEX, 0.42, (180, 180, 180), 1, cv2.LINE_AA)
    cv2.putText(img, f"{int(rome_loss):,} CASUALTIES", (75, h - 25), 
                cv2.FONT_HERSHEY_DUPLEX, 0.65, (80, 100, 240), 1, cv2.LINE_AA)
                
    # Cột Carthage (Vàng Kim)
    cv2.rectangle(img, (w - 420, h - 55), (w - 400, h - 25), (55, 175, 212), -1)
    cv2.putText(img, "CARTHAGINIAN ALLIANCE LOSSES:", (w - 385, h - 45), 
                cv2.FONT_HERSHEY_SIMPLEX, 0.42, (180, 180, 180), 1, cv2.LINE_AA)
    cv2.putText(img, f"{int(carthage_loss):,} CASUALTIES", (w - 385, h - 25), 
                cv2.FONT_HERSHEY_DUPLEX, 0.65, (60, 205, 230), 1, cv2.LINE_AA)
                
    # Thanh tiến trình thời gian trận đánh (Timeline Progress Bar)
    progress_ratio = min(1.0, current_time / max(0.1, total_time))
    bar_w = 400
    bar_x = (w - bar_w) // 2
    cv2.rectangle(img, (bar_x, h - 45), (bar_x + bar_w, h - 35), (35, 42, 50), -1)
    cv2.rectangle(img, (bar_x, h - 45), (bar_x + int(bar_w * progress_ratio), h - 35), (180, 150, 60), -1)
    cv2.putText(img, "BATTLE RECONSTRUCTION CHRONOLOGY", (bar_x + 40, h - 20), 
                cv2.FONT_HERSHEY_SIMPLEX, 0.38, (140, 150, 160), 1, cv2.LINE_AA)

def render_battle_video(json_path, output_mp4, preview_png=None):
    """
    Render video sa bàn bằng đa luồng CPU và mã hóa phần cứng NVENC.
    """
    with open(json_path, "r", encoding="utf-8") as f:
        data = json.load(f)
        
    width, height = data.get("resolution", [1920, 1080])
    fps = data.get("fps", 30)
    duration = data.get("duration_seconds", 6.0)
    total_frames = int(fps * duration)
    
    print(f"[*] Generating Tactical War Room Background ({width}x{height})...")
    base_bg = create_parchment_background(width, height)
    
    # Kiểm tra bộ mã hóa FFmpeg: Ưu tiên hevc_nvenc hoặc h264_nvenc
    encoder = "hevc_nvenc"
    test_proc = subprocess.run(["ffmpeg", "-hide_banner", "-encoders"], 
                               stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
    if "hevc_nvenc" not in test_proc.stdout and "h264_nvenc" not in test_proc.stdout:
        print("[!] NVENC not found in standard build, falling back to libx264...")
        encoder = "libx264"
    elif "hevc_nvenc" not in test_proc.stdout:
        encoder = "h264_nvenc"
        
    print(f"[*] Encoder selected: {encoder} (Hardware accelerated)")
    
    ffmpeg_cmd = [
        "ffmpeg", "-y",
        "-f", "rawvideo",
        "-vcodec", "rawvideo",
        "-s", f"{width}x{height}",
        "-pix_fmt", "bgr24",
        "-r", str(fps),
        "-i", "-",
        "-c:v", encoder,
    ]
    
    if "nvenc" in encoder:
        ffmpeg_cmd.extend(["-preset", "p4", "-cq", "19", "-bf", "0"])
    else:
        ffmpeg_cmd.extend(["-preset", "fast", "-crf", "18"])
        
    ffmpeg_cmd.extend(["-pix_fmt", "yuv420p", output_mp4])
    
    proc = subprocess.Popen(ffmpeg_cmd, stdin=subprocess.PIPE, stderr=subprocess.PIPE)
    
    print(f"[*] Rendering {total_frames} frames ({duration}s @ {fps}fps)...")
    
    saved_preview = False
    
    for frame_idx in range(total_frames):
        current_time = frame_idx / float(fps)
        t_norm = current_time / float(duration)
        
        # Clone nền sa bàn
        frame = base_bg.copy()
        
        # Xác định phase hiện tại
        current_phase = "Tactical Engagement"
        for p in data.get("phases", []):
            if p["timestamp_start"] <= current_time <= p["timestamp_end"]:
                current_phase = p["name"]
                break
                
        # 1. Vẽ các mũi tên chiến thuật
        for arrow in data.get("arrows", []):
            appear_t = arrow.get("appear_time", 0.0)
            if current_time >= appear_t:
                arrow_progress = min(1.0, (current_time - appear_t) / 1.8)
                draw_tactical_arrow(frame, arrow["from"], arrow["control"], arrow["to"], 
                                    arrow_progress, arrow["color"], arrow.get("label", ""))
                
        # 2. Vẽ các khối quân bài di chuyển
        for unit in data.get("units", []):
            start = unit["start_pos"]
            end = unit["end_pos"]
            # Smooth Hermite Interpolation (S-curve easing)
            smooth_t = 3 * (t_norm ** 2) - 2 * (t_norm ** 3)
            ux = start[0] + (end[0] - start[0]) * smooth_t
            uy = start[1] + (end[1] - start[1]) * smooth_t
            draw_tactical_unit(frame, ux, uy, unit["size"][0], unit["size"][1], 
                               unit["name"], unit["side"], unit["type"], unit["color"], 
                               unit.get("strength", ""))
                               
        # 3. Tính toán bộ đếm thương vong
        rome_loss = data.get("hud", {}).get("rome_max_loss", 50000) * (t_norm ** 1.8)
        carthage_loss = data.get("hud", {}).get("carthage_max_loss", 5000) * (t_norm ** 1.1)
        
        # 4. Vẽ HUD
        draw_hud(frame, data.get("battle_name", "HISTORICAL BATTLE"), 
                 current_phase, current_time, duration, rome_loss, carthage_loss)
                 
        # Lưu ảnh snapshot ở thời điểm cao trào (giây thứ 5.0)
        if preview_png and not saved_preview and current_time >= 5.0:
            cv2.imwrite(preview_png, frame)
            saved_preview = True
            
        # Ghi trực tiếp vào stdin của FFmpeg qua pipe
        proc.stdin.write(frame.tobytes())
        
        if frame_idx % 30 == 0:
            sys.stdout.write(f"\r  -> Progress: {frame_idx}/{total_frames} frames ({(frame_idx/total_frames)*100:.1f}%)")
            sys.stdout.flush()
            
    sys.stdout.write("\n")
    proc.stdin.close()
    proc.wait()
    
    print(f"[OK] Render completed successfully: {output_mp4}")
    if preview_png and os.path.exists(preview_png):
        print(f"[OK] High-res Preview snapshot saved: {preview_png}")

if __name__ == "__main__":
    json_path = os.path.join(os.path.dirname(__file__), "..", "..", "data", "sample_battle.json")
    out_mp4 = os.path.join(os.path.dirname(__file__), "..", "..", "output", "cannae_tactical_demo.mp4")
    out_png = os.path.join(os.path.dirname(__file__), "..", "..", "output", "cannae_preview.png")
    render_battle_video(json_path, out_mp4, out_png)
