import socket, struct, time, threading
from datetime import datetime

SERVER_IP   = "222.108.131.130"
SERVER_PORT = 15010

USER_ID   = "ilshin"
USER_PASS = "ilshin1234"

HEADERKEY        = 0x12345678
AIR_QUALITY_TYPE = 0x38000000
MSGTYPE_BIND     = AIR_QUALITY_TYPE | 0
MSGTYPE_ALIVE    = AIR_QUALITY_TYPE | 4
MSGTYPE_ADD      = AIR_QUALITY_TYPE | 178

# ---- 응답 헤더 파싱 ----
def parse_header(resp: bytes):
    if len(resp) < 28: return None
    try:
        hk, mt, bl, r1, r2, r3, res = struct.unpack("<IIIIIII", resp[:28])
        return {"headerKey": hk, "msgType": mt, "bodyLen": bl, "result": res}
    except Exception: return None

# ---- C 문자열 ----
def fixed_cstr(s: str, size: int) -> bytes:
    b = s.encode("utf-8")
    b = b[:size-1] + b"\x00"
    return b.ljust(size, b"\x00")

# ---- BIND ----
def build_bind_packet(user_id: str, user_pass: str) -> bytes:
    home_version = struct.pack("<I", 0)
    nKind        = struct.pack("<I", 0)
    nVersion     = (0).to_bytes(16, "little")
    szId         = fixed_cstr(user_id, 40)
    szPass       = fixed_cstr(user_pass, 40)
    body = home_version + nKind + nVersion + szId + szPass
    header = struct.pack("<IIIIIII", HEADERKEY, MSGTYPE_BIND, len(body), 0,0,0,0)
    return header + body

# ---- ALIVE ----
def build_alive_packet() -> bytes:
    home_version = struct.pack("<I", 0)
    nKind        = struct.pack("<I", 0)
    nVersion     = (0).to_bytes(16, "little")
    body = home_version + nKind + nVersion
    header = struct.pack("<IIIIIII", HEADERKEY, MSGTYPE_ALIVE, len(body), 0,0,0,0)
    return header + body

# ---- ADD (PM10/PM2.5) ----
def build_air_quality_add(pm25, pm10, *, n_area=1) -> bytes:
    now = datetime.now().strftime("%Y-%m-%d %H:%M:%S").encode("utf-8").ljust(20, b"\x00")
    body  = struct.pack("<I", n_area)  # nArea
    body += now
    body += b"\x00"*20 + b"\x00"*20 + b"\x00"*20
    body += struct.pack("<ffff", 0.0,0.0,0.0,0.0)
    body += struct.pack(
        "<IIIIIIIIIIII",
        int(pm10), 0, int(pm25), 0,
        0,0,0,0, 0,0,0,0
    )
    header = struct.pack("<IIIIIII", HEADERKEY, MSGTYPE_ADD, len(body), 0,0,0,0)
    return header + body

# ---- KeepAlive 쓰레드 ----
def keepalive_loop(sock: socket.socket, stop_event: threading.Event):
    while not stop_event.is_set():
        try:
            sock.sendall(build_alive_packet())
            resp = sock.recv(64)
            print("💓 ALIVE sent, ack:", parse_header(resp))
        except Exception as e:
            print("⚠️ ALIVE 실패:", e)
            break
        stop_event.wait(30)  # 30초마다
    print("💤 KeepAlive thread 종료")

# ---- 가짜 ADD 쓰레드 ----
def add_fake_loop(sock: socket.socket, stop_event: threading.Event):
    cnt = 0
    while not stop_event.is_set():
        try:
            pm25 = 15 + (cnt % 5)   # 15~19
            pm10 = 25 + (cnt % 5)   # 25~29
            pkt = build_air_quality_add(pm25, pm10)
            sock.sendall(pkt)
            ack = sock.recv(128)
            print(f"🛰️ Fake ADD sent (PM2.5={pm25}, PM10={pm10}) ack:", parse_header(ack))
            cnt += 1
        except Exception as e:
            print("⚠️ Fake ADD 실패:", e)
            break
        stop_event.wait(10)  # 10초마다
    print("💤 Fake ADD thread 종료")

# ---- 서버 연결 + BIND ----
def try_connect_and_bind():
    try:
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        sock.settimeout(5)
        sock.connect((SERVER_IP, SERVER_PORT))
        sock.sendall(build_bind_packet(USER_ID, USER_PASS))
        resp = sock.recv(1024)
        hdr = parse_header(resp)
        if hdr and hdr.get("result", 1) == 0:
            print("✅ BIND 성공")
            return sock
        else:
            print("❌ BIND 실패:", hdr)
            sock.close()
            return None
    except Exception as e:
        print("⚠️ 연결 실패:", e)
        return None

# ---- 메인 ----
def main():
    sock = try_connect_and_bind()
    if not sock: return

    # 쓰레드 시작
    stop_event = threading.Event()
    th_alive = threading.Thread(target=keepalive_loop, args=(sock, stop_event), daemon=True)
    th_add   = threading.Thread(target=add_fake_loop, args=(sock, stop_event), daemon=True)
    th_alive.start()
    th_add.start()

    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        stop_event.set()
        sock.close()
        print("🛑 종료")

if __name__ == "__main__":
    main()
