import argparse
import os
import subprocess
from typing import Optional, Tuple

import cv2
import numpy as np


def run_adb(adb_path: str, args: list, device: Optional[str] = None) -> subprocess.CompletedProcess:
    cmd = [adb_path]
    if device:
        cmd += ['-s', device]
    cmd += args
    return subprocess.run(cmd, capture_output=True)


def list_devices(adb_path: str) -> list:
    result = run_adb(adb_path, ['devices'])
    lines = (result.stdout or b'').decode('utf-8', errors='ignore').splitlines()
    devices = []
    for line in lines[1:]:
        if not line.strip():
            continue
        parts = line.split()[:2]
        if len(parts) != 2:
            continue
        serial, status = parts
        if status == 'device':
            devices.append(serial)
    return devices


def screencap(adb_path: str, device: str) -> Optional[np.ndarray]:
    result = run_adb(adb_path, ['exec-out', 'screencap', '-p'], device=device)
    if result.returncode != 0 or not result.stdout:
        return None
    data = result.stdout
    img = cv2.imdecode(np.frombuffer(data, dtype=np.uint8), cv2.IMREAD_COLOR)
    return img


def parse_base(value: str) -> Optional[Tuple[int, int]]:
    if not value:
        return None
    parts = value.lower().split('x')
    if len(parts) != 2:
        return None
    try:
        return int(parts[0]), int(parts[1])
    except ValueError:
        return None


def write_region(config_path: str, section: str, key: str, region: Tuple[int, int, int, int]) -> None:
    if not os.path.exists(config_path):
        print('Config not found:', config_path)
        return
    target_section = f'[{section}]'
    updated_lines = []
    with open(config_path, 'r', encoding='utf-8') as f:
        lines = f.readlines()

    in_section = False
    inserted = False
    replacement = f'{key}={region[0]},{region[1]},{region[2]},{region[3]}\n'

    for line in lines:
        stripped = line.strip()
        if stripped.startswith('[') and stripped.endswith(']'):
            if in_section and not inserted:
                updated_lines.append(replacement)
                inserted = True
            in_section = (stripped.lower() == target_section)

        if in_section and stripped.startswith(f'{key}='):
            updated_lines.append(replacement)
            inserted = True
            continue

        updated_lines.append(line)

    if in_section and not inserted:
        updated_lines.append(replacement)

    with open(config_path, 'w', encoding='utf-8') as f:
        f.writelines(updated_lines)
    print(f'Updated {key} in:', config_path)


def parse_fit(value: str) -> Optional[Tuple[int, int]]:
    if not value:
        return None
    parts = value.lower().split('x')
    if len(parts) != 2:
        return None
    try:
        return int(parts[0]), int(parts[1])
    except ValueError:
        return None


def main() -> None:
    parser = argparse.ArgumentParser(description='Capture LDPlayer screen templates')
    parser.add_argument('--adb', default=os.path.abspath(os.path.join(os.path.dirname(__file__), '..\\adb.exe')))
    parser.add_argument('--device', default='')
    parser.add_argument('--out', default='')
    parser.add_argument('--roi', action='store_true')
    parser.add_argument('--print-coords', action='store_true')
    parser.add_argument('--base', default='')
    parser.add_argument('--write-room-region', action='store_true')
    parser.add_argument('--write-empty-region', action='store_true')
    parser.add_argument('--config', default=os.path.abspath(os.path.join(os.path.dirname(__file__), 'training.txt')))
    parser.add_argument('--fit', default='1000x800')
    args = parser.parse_args()

    adb_path = args.adb
    devices = list_devices(adb_path)
    if not devices:
        print('No devices found. Run adb devices first.')
        return

    device = args.device or devices[0]
    if device not in devices:
        print('Device not found. Available:')
        for d in devices:
            print(' -', d)
        return

    img = screencap(adb_path, device)
    if img is None:
        print('Failed to capture screen')
        return

    if args.roi:
        display_img = img
        display_scale = 1.0
        fit = parse_fit(args.fit)
        if fit:
            max_w, max_h = fit
            h, w = img.shape[:2]
            scale = min(max_w / w, max_h / h, 1.0)
            if scale < 1.0:
                display_scale = scale
                display_img = cv2.resize(img, (int(w * scale), int(h * scale)), interpolation=cv2.INTER_AREA)

        r = cv2.selectROI('Select ROI', display_img, fromCenter=False, showCrosshair=True)
        cv2.destroyAllWindows()
        dx, dy, dw, dh = [int(v) for v in r]
        if dw == 0 or dh == 0:
            print('ROI canceled')
            return

        x = int(dx / display_scale)
        y = int(dy / display_scale)
        w = int(dw / display_scale)
        h = int(dh / display_scale)
        x = max(0, min(x, img.shape[1] - 1))
        y = max(0, min(y, img.shape[0] - 1))
        w = max(1, min(w, img.shape[1] - x))
        h = max(1, min(h, img.shape[0] - y))

        crop = img[y:y + h, x:x + w]
        if args.out:
            cv2.imwrite(args.out, crop)
            print('Saved:', args.out)
        else:
            print('No output file provided, ROI not saved')

        base_region = None
        if args.print_coords or args.write_room_region:
            base = parse_base(args.base)
            if base:
                base_w, base_h = base
                scale_x = base_w / img.shape[1]
                scale_y = base_h / img.shape[0]
                bx = int(x * scale_x)
                by = int(y * scale_y)
                bw = int(w * scale_x)
                bh = int(h * scale_y)
                base_region = (bx, by, bw, bh)
                if args.print_coords:
                    print(f'Base coords: {bx},{by},{bw},{bh}')
            else:
                if args.print_coords:
                    print(f'Coords: {x},{y},{w},{h}')

        if args.write_room_region:
            if base_region is None:
                print('Use --base WIDTHxHEIGHT when writing room_code_region')
                return
            write_region(args.config, 'state.room_created', 'room_code_region', base_region)
        if args.write_empty_region:
            if base_region is None:
                print('Use --base WIDTHxHEIGHT when writing empty_slot_region')
                return
            write_region(args.config, 'state.play_ready', 'empty_slot_region', base_region)
        return

    if args.out:
        cv2.imwrite(args.out, img)
        print('Saved:', args.out)
        return

    print('Captured image. Use --out to save or --roi to crop.')


if __name__ == '__main__':
    main()
