import sys
sys.path.insert(0, r'D:\__sistem__\Masaustu\FikoYT.Yeni.Cag\_yama_tmp')
from yama_lib import *

P = r'D:\__sistem__\Masaustu\FikoYT.Yeni.Cag\fikatia-local\uygulama\fikocad\lisans_uzaktan_soket.cpp'
yedekle(P)
t, crlf = oku(P)

# R1: <memory>
t = degistir(t, '#include <cstdio>\n#include <mutex>\n', '#include <cstdio>\n#include <memory>\n#include <mutex>\n')

# R2: utf16Say (artik gereksiz) -> paket formati notu + gonderim mutex'i + ping araligi
R2 = r'''// -- Socket.IO paket yardimcilari (engine.io v3, WEBSOCKET frame'leri) ------------
// Websocket'te her frame TEK engine.io paketidir. "N:" uzunluk oneki YALNIZ polling
// govdesinde (handshake) vardir. Websocket'e tasinirsa sunucu frame'in ilk rakamini
// engine.io paket tipi sanir (1=close, 2=ping ...) ve olay HIC islenmez.
// EVENT frame'i:   42["olay",{...}]
//   '4' = engine.io MESSAGE, '2' = socket.io EVENT, varsayilan "/" namespace'i yazilmaz.
// Kaynak (sunucu kutuphanesi): socket.io-go-parser/v2 v2.5.0 encoder.go:82 / decoder.go:218.

// ws'e giden TUM frame'ler bu mutex ile serilesir: ping parcacigi ve dinleme dongusu
// ayni handle'a ayni anda WinHttpWebSocketSend cagirmamali.
std::mutex g_gonderMtx;

DWORD wsGonder(HINTERNET ws, const std::string& s) {
  std::lock_guard<std::mutex> lk(g_gonderMtx);
  return WinHttpWebSocketSend(ws, WINHTTP_WEB_SOCKET_UTF8_MESSAGE_BUFFER_TYPE,
                              (PVOID)s.data(), (DWORD)s.size());
}

// EIO=3: kalp atisini ISTEMCI baslatir (engine.io socket.go:183-189, 204-211): istemci "2"
// yollar, sunucu "3" ile cevaplar. pingInterval(25 sn)+pingTimeout(20 sn) = 45 sn icinde
// ping gelmezse sunucu baglantiyi kapatir. Sunucudan once davranmak icin 20 sn.
constexpr DWORD PING_ARALIK_MS = 20000;

'''
t = aralik_degistir(t,
    '// -- Socket.IO v3 paket yardimcilari (namespace YOK: "2event,{args}") -----------',
    'std::string jsonDizgeEkle(std::string& j, const char* a, const std::string& d) {',
    R2)

# R3: baglanti kaydi artik wsGonder ile
t = degistir(t,
'''  const std::string paket = eventUret("FikoYT.sistem.io.cihaz.baglandi", args);
  return WinHttpWebSocketSend(ws, WINHTTP_WEB_SOCKET_UTF8_MESSAGE_BUFFER_TYPE,
                              (PVOID)paket.data(), (DWORD)paket.size()) == NO_ERROR;''',
'''  const std::string paket = eventUret("FikoYT.sistem.io.cihaz.baglandi", args);
  return wsGonder(ws, paket) == NO_ERROR;''')

# R4: eventUret + payloadCoz
R4 = r'''// EVENT frame'i uret:  42["<olay>",<arg>]   (uzunluk oneki YOK, namespace "/" yazilmaz)
// Eski bicim "<N>:2<olay>,<arg>" sunucuda cozulemiyordu (bkz. yukaridaki not).
// 'event' sabit ASCII adlardir (kacis gerekmez); 'args' hazir JSON metnidir.
std::string eventUret(const std::string& event, const std::string& args) {
  std::string p = "42[";
  p += TIRNAK;
  p += event;
  p += TIRNAK;
  if (!args.empty()) {
    p += ',';
    p += args;
  }
  p += ']';
  return p;
}

// Gelen engine.io v3 websocket frame'ini socket.io EVENT olarak ayristir.
// Kabul edilen bicim:  42["olay",{...}]   (42/ns,["olay",..] ve ack kimlikli
// 42<id>["olay",..] bicimleri de tolere edilir). 'json' = olayin ilk argumani (ham JSON).
// false = EVENT degil / bozuk. Cagiran SESSIZ gecmez: ham metni gunluge yazar.
bool payloadCoz(const std::string& frame, std::string& ev, std::string& json) {
  ev.clear();
  json.clear();
  if (frame.size() < 5 || frame[0] != '4' || frame[1] != '2') return false;
  std::size_t i = 2;
  if (frame[i] == '/') {  // 42/ns,[...]
    const std::size_t v = frame.find(',', i);
    if (v == std::string::npos) return false;
    i = v + 1;
  }
  while (i < frame.size() && frame[i] >= '0' && frame[i] <= '9') ++i;  // ack kimligi
  if (i + 1 >= frame.size() || frame[i] != '[' || frame[i + 1] != TIRNAK) return false;
  i += 2;
  while (i < frame.size() && frame[i] != TIRNAK) {
    if (frame[i] == TERS && i + 1 < frame.size()) ++i;
    ev += frame[i];
    ++i;
  }
  if (i >= frame.size() || ev.empty()) return false;
  ++i;  // olay adinin kapanis tirnagi
  if (i < frame.size() && frame[i] == ',') {
    ++i;
    const std::size_t son = frame.find_last_of(']');
    if (son == std::string::npos || son < i) return false;
    json = frame.substr(i, son - i);
  } else if (i >= frame.size() || frame[i] != ']') {
    return false;
  }
  return true;
}

'''
t = aralik_degistir(t, '// EVENT packet: 2<event>,<args>   (namespace yok)', '// -- Handshake (polling)', R4)

# R5: probe oncesi aciklama blogu
R5 = r'''  // -- engine.io v3 upgrade + socket.io oturumu ------------------------------------
  // 1) Polling handshake sid verdi. Sunucu (EIO=3) socket.io "/" namespace'ine KENDISI
  //    baglar: socket.io server.go:519-521 `if conn.Protocol() == 3 { client.connect("/") }`
  //    ve socket.go:515-518 (bos CONNECT paketi). Bu yuzden istemci "40" GONDERMEZ.
  //    Gondermek ZARARLIDIR: client.go:212-219 namespace'i zaten bagli olan bir CONNECT'i
  //    "invalid state" sayip baglantiyi KAPATIR.
  // 2) websocket'e yukseltme icin probe ZORUNLU: C->S "2probe", S->C "3probe", C->S "5".
  //    (Eskiden "0" yollaniyordu; "0" sunucudan istemciye giden open tipidir, sunucu
  //    baglantiyi aninda kapatiyordu - WinHttp 12030, 1 ms sonra.)
  // 3) Olaylar 42["olay",{...}] biciminde, uzunluk oneksiz gider/gelir (eventUret/payloadCoz).
'''
t = aralik_degistir(t, 'Merhaba paketi (cihaz kimli', '  {\n    bool probeTamam = false;', R5)

# R6: gelen socket.io paketlerini isleyen yardimci (oturumAc'tan once)
R6 = r'''// engine.io MESSAGE ('4') frame'indeki socket.io paketini isle. false = oturumu kapat.
bool socketioIsle(HINTERNET ws, const std::string& frame) {
  const char tip = frame.size() > 1 ? frame[1] : '?';
  switch (tip) {
    case '0':  // CONNECT (EIO=3'te bos paket: "40")
      gunluk::bilgi("uzak: socket.io CONNECT alindi: {}", frame.substr(0, 80));
      return true;
    case '1':  // DISCONNECT
      gunluk::uyari("uzak: sunucu socket.io DISCONNECT gonderdi: {}", frame.substr(0, 80));
      return false;
    case '4':  // CONNECT_ERROR
      gunluk::uyari("uzak: socket.io hata paketi: {}", frame.substr(0, 160));
      return true;
    case '2':  // EVENT
      break;
    default:
      gunluk::bilgi("uzak: islenmeyen socket.io paketi: {}", frame.substr(0, 80));
      return true;
  }

  // Ham metin HER ZAMAN izlenir: paket sessizce dusmesin (komut imzasi ve mesaj dahil).
  gunluk::bilgi("uzak: gelen olay ({} bayt): {}", frame.size(), frame.substr(0, 400));
  std::string ev, json;
  if (!payloadCoz(frame, ev, json)) {
    gunluk::uyari("uzak: olay paketi COZULEMEDI (dusuruldu): {}", frame.substr(0, 200));
    return true;
  }
  if (ev != "FikoYT.sistem.io.cihaz.komut") {
    gunluk::bilgi("uzak: ilgilenilmeyen olay: {}", ev);
    return true;
  }
  // YEREL KOPYAYA AL: kapanis sirasinda g_cekirdek nullptr yapilabilir.
  Cekirdek* cek = g_cekirdek.load();
  if (!cek) {
    gunluk::uyari("uzak: komut geldi ama cekirdek hazir degil - cevaplanmadi");
    return true;
  }
  // Imza dogrulamasi, beyaz liste ve saglik kontrolu uzakKomutMesajiIsle -> uzakKomutUygula
  // icindedir; imzasiz / bozuk imzali komut orada REDDEDILIR (burada gevsetme YOK).
  std::string cevap;
  const bool ok = uzakKomutMesajiIsle(*cek, json, cevap);
  {
    std::lock_guard lk(g_mtx);
    ++g_kanal.komutSayisi;
  }
  const DWORD src = wsGonder(ws, eventUret("FikoYT.sistem.io.cihaz.komut-cevap", cevap));
  if (src != NO_ERROR)
    gunluk::uyari("uzak: komut cevabi gonderilemedi (WinHttp {})", (unsigned)src);
  gunluk::bilgi("uzak komut cevabi: {}", ok ? "OK" : "RED");
  return true;
}

'''
t = degistir(t,
    '// Bir websocket oturumu: handshake -> upgrade -> dinle -> cevap ver\nDWORD oturumAc',
    R6 + '// Bir websocket oturumu: handshake -> upgrade -> dinle -> cevap ver\nDWORD oturumAc')

# R7: dinleme dongusu
R7 = r'''  // Dinle dongusu
  // Kalp atisi: EIO=3'te ISTEMCI baslatir (bkz. PING_ARALIK_MS). Eskiden "2" yalniz bir
  // frame GELDIKTEN sonra, dongu basinda kontrol ediliyordu; Receive bloklayici oldugundan
  // sessiz kanalda ping hic gitmiyor, sunucu 45 sn sonra baglantiyi kapatiyordu.
  // Ping parcacigi yalniz ws DEGERINI ve paylasilan durum nesnesini (shared_ptr kopyasi)
  // kullanir, yerel degiskene referans tutmaz; ws kapanmadan ONCE join edilir.
  struct PingDurum {
    std::mutex mtx;
    std::condition_variable cv;
    bool dur = false;
  };
  const std::shared_ptr<PingDurum> pingDurum = std::make_shared<PingDurum>();
  std::thread pingIs([ws, pingDurum] {
    try {
      std::unique_lock<std::mutex> lk(pingDurum->mtx);
      while (!pingDurum->dur) {
        if (pingDurum->cv.wait_for(lk, std::chrono::milliseconds(PING_ARALIK_MS),
                                   [&] { return pingDurum->dur; }))
          break;  // durdurma istendi
        lk.unlock();
        const DWORD prc = wsGonder(ws, "2");  // engine.io ping
        lk.lock();
        if (prc != NO_ERROR) break;
      }
    } catch (...) {
    }
  });

  std::string birikim;
  char tampon[4096];
  bool bitir = false;
  while (!bitir && !g_kanal.durduruldu.load()) {
    DWORD okunan = 0;
    WINHTTP_WEB_SOCKET_BUFFER_TYPE tur = WINHTTP_WEB_SOCKET_CLOSE_BUFFER_TYPE;
    const DWORD rc = WinHttpWebSocketReceive(ws, tampon, sizeof(tampon), &okunan, &tur);
    if (rc != NO_ERROR) {
      std::lock_guard lk(g_mtx);
      g_kanal.hata = "okuma hatasi";
      g_kanal.durum = UzakKanalDurum::Kopuk;
      gunluk::uyari("uzak: okuma hatasi GLE {} (WinHttp {}) - 5 sn sonra tekrar",
                    GetLastError(), (unsigned)rc);
      break;
    }
    if (tur == WINHTTP_WEB_SOCKET_CLOSE_BUFFER_TYPE) {
      // Sunucu neden kapatti? Close frame durum kodunu sor (teshis icin).
      // Imza: (ws, USHORT* kod, PVOID sebepBuf, DWORD sebepLen, DWORD* tuketilen)
      USHORT kapKod = 0;
      char sebepBuf[128] = {0};
      DWORD tuketilen = 0;
      const DWORD cs = WinHttpWebSocketQueryCloseStatus(ws, &kapKod, sebepBuf,
                                                        (DWORD)sizeof(sebepBuf) - 1, &tuketilen);
      if (cs == NO_ERROR) {
        gunluk::uyari("uzak: sunucu kapatti - kod={} sebep='{}'", (unsigned)kapKod, sebepBuf);
      } else {
        gunluk::uyari("uzak: sunucu kapatti (close sorgusu hata {}, kod={})",
                      (unsigned)cs, (unsigned)kapKod);
      }
      std::lock_guard lk(g_mtx);
      g_kanal.hata = "sunucu baglantiyi kapatti";
      g_kanal.durum = UzakKanalDurum::Kopuk;
      break;
    }
    birikim.append(tampon, okunan);
    if (birikim.size() > (1u << 20)) {  // 1 MB ust sinir: bellek korumasi
      gunluk::uyari("uzak: ws mesaji cok buyuk ({} bayt) - baglanti kapatiliyor", birikim.size());
      std::lock_guard lk(g_mtx);
      g_kanal.hata = "ws mesaji cok buyuk";
      g_kanal.durum = UzakKanalDurum::Kopuk;
      break;
    }
    if (tur != WINHTTP_WEB_SOCKET_UTF8_MESSAGE_BUFFER_TYPE) continue;  // parca: devam

    // Tam bir frame geldi. Ayristirma basarisiz olsa da birikim HER ZAMAN bosaltilir.
    // (Eskiden yalniz basarida bosaltiliyordu: tek bir cozulemeyen frame - or. "3" pong
    // ya da "42[...]" - sonrakilerin basina yapisip HER SEYI sessizce zehirliyordu.)
    const std::string frame = std::move(birikim);
    birikim.clear();
    if (frame.empty()) continue;

    switch (frame[0]) {
      case '1': {  // engine.io close
        gunluk::uyari("uzak: sunucu engine.io CLOSE gonderdi");
        std::lock_guard lk(g_mtx);
        g_kanal.hata = "sunucu oturumu kapatti";
        g_kanal.durum = UzakKanalDurum::Kopuk;
        bitir = true;
        break;
      }
      case '2':  // sunucu ping (yalniz EIO=4'te gelir) -> pong
        wsGonder(ws, "3");
        break;
      case '4':  // engine.io MESSAGE -> socket.io paketi
        if (!socketioIsle(ws, frame)) {
          std::lock_guard lk(g_mtx);
          g_kanal.hata = "sunucu socket.io oturumunu kapatti";
          g_kanal.durum = UzakKanalDurum::Kopuk;
          bitir = true;
        }
        break;
      default:  // '0' open, '3' pong, '5' upgrade, '6' noop: islem gerekmez
        break;
    }
  }

  // Ping parcacigini durdur ve BEKLE (ws kapanmadan once).
  {
    std::lock_guard<std::mutex> lk(pingDurum->mtx);
    pingDurum->dur = true;
  }
  pingDurum->cv.notify_all();
  if (pingIs.joinable()) pingIs.join();

'''
t = aralik_degistir(t, '// Dinle dongusu',
    '  WinHttpWebSocketClose(ws, WINHTTP_WEB_SOCKET_SUCCESS_CLOSE_STATUS, nullptr, 0);\n  WinHttpCloseHandle(ws);\n  if (baglantiWs)',
    R7)

yaz(P, t, crlf)
print('soket.cpp TAMAM crlf=%s' % crlf)
