import socket, base64, os, struct, time, urllib.request, re

PORT = 9911


def el_sikisma():
    url = 'http://127.0.0.1:%d/socket.io/?EIO=3&transport=polling' % PORT
    req = urllib.request.Request(url, headers={'Cihaz_kimlik': 'TESTCIHAZ'})
    body = urllib.request.urlopen(req, timeout=5).read().decode()
    return re.search(r'"sid":"([^"]+)"', body).group(1), body


class WS:
    def __init__(self, path):
        self.s = socket.create_connection(('127.0.0.1', PORT), timeout=5)
        key = base64.b64encode(os.urandom(16)).decode()
        self.s.sendall(('GET %s HTTP/1.1\r\nHost: 127.0.0.1:%d\r\nUpgrade: websocket\r\n'
                        'Connection: Upgrade\r\nSec-WebSocket-Key: %s\r\nSec-WebSocket-Version: 13\r\n\r\n'
                        % (path, PORT, key)).encode())
        r = b''
        while b'\r\n\r\n' not in r:
            r += self.s.recv(1)
        assert b' 101 ' in r.split(b'\r\n')[0], r

    def gonder(self, text):
        d = text.encode()
        m = os.urandom(4)
        n = len(d)
        h = bytes([0x81]) + (bytes([0x80 | n]) if n < 126 else bytes([0x80 | 126]) + struct.pack('>H', n))
        self.s.sendall(h + m + bytes(b ^ m[i % 4] for i, b in enumerate(d)))

    def _tam(self, n):
        b = b''
        while len(b) < n:
            c = self.s.recv(n - len(b))
            if not c:
                raise EOFError('baglanti kapandi')
            b += c
        return b

    def al(self, timeout=3):
        """(opcode, metin) | ('ZAMAN ASIMI',None) | ('KAPANDI',None)"""
        self.s.settimeout(timeout)
        try:
            h = self._tam(2)
            op = h[0] & 0x0F
            n = h[1] & 0x7F
            if n == 126:
                n = struct.unpack('>H', self._tam(2))[0]
            elif n == 127:
                n = struct.unpack('>Q', self._tam(8))[0]
            p = self._tam(n)
            if op == 8:
                return ('KAPANDI(close frame)', p[2:].decode(errors='replace') if len(p) > 2 else '')
            return (op, p.decode(errors='replace'))
        except socket.timeout:
            return ('ZAMAN ASIMI', None)
        except (EOFError, ConnectionError):
            return ('KAPANDI(tcp)', None)


def baglan_ve_yuksel():
    sid, body = el_sikisma()
    ws = WS('/socket.io/?EIO=3&transport=websocket&sid=' + sid)
    ws.gonder('2probe')
    print('  probe cevabi :', ws.al())
    ws.gonder('5')
    return ws, sid


def kisa(x):
    return x if len(str(x)) < 200 else str(x)[:200] + '...'


print('=== T1: YENI bicim (42["olay",{...}], uzunluk oneksiz) + ping thread davranisi ===')
ws, sid = baglan_ve_yuksel()
print('  (sunucu ilk frame\'ler):', ws.al(1.0), ws.al(0.5))
ws.gonder('42["FikoYT.sistem.io.cihaz.baglandi",{"cihaz_kimlik":"TESTCIHAZ","cihaz":{"tipi":"fikocad"}}]')
t0 = time.time()
komut = None
while time.time() - t0 < 3:
    op, m = ws.al(1.0)
    if m and m.startswith('42['):
        komut = m
        break
print('  GELEN komut frame:', kisa(komut))
ws.gonder('42["FikoYT.sistem.io.cihaz.komut-cevap",{"basarili":true,"mesaj":"tamam","komutId":"id-1"}]')
# 12 sn boyunca her 2 sn "2" ping at: sunucu "3" donmeli ve baglanti ACIK kalmali (5 sn timeout'u asar)
acik = True
for i in range(6):
    ws.gonder('2')
    op, m = ws.al(2.0)
    time.sleep(2)
    if op in ('KAPANDI(close frame)', 'KAPANDI(tcp)'):
        acik = False
        print('  ping', i, 'KAPANDI', op)
        break
print('  12 sn ping dongusu sonunda baglanti acik mi:', acik, '(son cevap:', m, ')')

print('=== T2: ESKI bicim ("<N>:2<olay>,<arg>") ===')
ws2, _ = baglan_ve_yuksel()
ws2.al(1.0); ws2.al(0.5)
eski = '2FikoYT.sistem.io.cihaz.baglandi,{"cihaz_kimlik":"ESKI","cihaz":{"tipi":"fikocad"}}'
ws2.gonder('%d:%s' % (len(eski), eski))
print('  eski bicim sonrasi 2.5 sn:', ws2.al(2.5))

print('=== T3: NAMESPACE CONNECT "40" gonderirsek ===')
ws3, _ = baglan_ve_yuksel()
ws3.al(1.0); ws3.al(0.5)
ws3.gonder('40')
print('  "40" sonrasi:', ws3.al(2.5), ws3.al(2.5))

print('=== T4: HIC ping atilmazsa (pingInterval 3s + pingTimeout 2s = 5s) ===')
ws4, _ = baglan_ve_yuksel()
ws4.al(1.0); ws4.al(0.5)
t0 = time.time()
sonuc = ('ZAMAN ASIMI', None)
while time.time() - t0 < 9:
    sonuc = ws4.al(1.0)
    if str(sonuc[0]).startswith('KAPANDI'):
        break
print('  sessiz kanal sonucu: %s (%.1f sn sonra)' % (sonuc[0], time.time() - t0))
