#!/usr/bin/env python3
"""
iScout para Evony - lector de monstruos via memoria (Frida)
Adjunta a Evony en el emulador rooteado, recibe la config id->nombre,
captura objetos del mapa (coords/nivel/tipo) y escribe un CSV limpio.

Uso:
  python3 iscout.py                 # captura continua, Ctrl+C para parar
  Mientras corre: panea el mapa en el emulador (o usa jumps).
Salida:
  scout_bosses.csv   (name, level, type, x, y, id)  ordenado por nivel desc
"""
import sys, os, json, csv, time, signal, frida

HERE    = os.path.dirname(os.path.abspath(__file__))
CSV_OUT = os.path.join(HERE, "scout_bosses.csv")
AGENT   = os.path.join(HERE, "agent.js")

cfg = {}                 # id -> {name, level, type}
objs = {}                # (wx,wy) -> {t,id,lv,wx,wy}

def write_csv():
    rows = []
    for (wx, wy), o in objs.items():
        c = cfg.get(str(o["id"])) or cfg.get(o["id"]) or {}
        name = c.get("name", "")
        rows.append({
            "name": name, "level": o["lv"], "type": o["t"],
            "x": wx, "y": wy, "id": o["id"],
        })
    rows.sort(key=lambda r: (-r["level"], r["name"]))
    with open(CSV_OUT, "w", newline="") as f:
        w = csv.DictWriter(f, fieldnames=["name","level","type","x","y","id"])
        w.writeheader()
        w.writerows(rows)
    return len(rows)

def on_message(msg, data):
    if msg.get("type") == "error":
        print("[agent error]", msg.get("stack") or msg.get("description"), flush=True)
        return
    if msg.get("type") != "send":
        return
    p = msg["payload"]
    if not isinstance(p, dict):
        return
    if p.get("kind") == "config":
        for k, v in p["cfg"].items():
            cfg[str(k)] = v
        print(f"[cfg] {p['n']} configs de monstruo cargadas", flush=True)
    elif p.get("kind") == "batch":
        for it in p["items"]:
            objs[(it["wx"], it["wy"])] = it
        n = write_csv()
        # resumen rapido
        with_name = sum(1 for o in objs.values() if (cfg.get(str(o["id"])) or {}).get("name"))
        print(f"[batch +{p['n']}] total_unique={n}  con_nombre={with_name}  -> {CSV_OUT}", flush=True)

def main():
    dev = frida.get_device("emulator-5554")
    pid = next((pp.pid for pp in dev.enumerate_processes() if pp.name == "Evony"), None)
    if pid is None:
        print("Evony no esta corriendo en el emulador"); sys.exit(1)
    session = dev.attach(pid)
    with open(AGENT) as f:
        script = session.create_script(f.read())
    script.on("message", on_message)
    script.load()
    print("[iscout] activo. Panea el mapa en el emulador. Ctrl+C para parar y volcar CSV.", flush=True)
    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        n = write_csv()
        print(f"\n[iscout] parado. {n} objetos unicos en {CSV_OUT}")

if __name__ == "__main__":
    main()
