import os
import sys

file_dir = os.path.dirname(__file__)
sys.path.append(file_dir)
import pyshark
import webview
import argparse
from queue import Queue, Empty
import requests
from Log import LOG
from Options import options
from base_web import html
from Gui import GUI
import subprocess
import win32gui
import win32process
from threading import Event, Thread
import Window


file_dir = os.path.dirname(__file__)
sys.path.append(file_dir)


from __version__ import __version__

def _get_hwnd_by_pid(pid):
    def callback(hwnd, hwnds):
        if win32gui.IsWindowVisible(hwnd) and win32gui.IsWindowEnabled(hwnd):
            _, found_pid = win32process.GetWindowThreadProcessId(hwnd)
            if found_pid == pid:
                hwnds.append(hwnd)
        return True

    hwnds = []
    win32gui.EnumWindows(callback, hwnds)
    return win32gui.GetWindowText(hwnds[0]) if hwnds else ""

def get_window_info(port):
    if os.name != 'nt':
        return ({
            'port': port,
            'pid': port,
            'name': str(port)
            })
    try:
        pid = str(subprocess.run(f'cmd.exe /c netstat -a -n -o | findstr "{port}"', shell=True, check=True, stdout = subprocess.PIPE).stdout).replace('\\r', '').replace('\\n', '').split(' ')[-1].replace('\'', '')
    except:
        return {
            'port': port,
            'pid': port,
            'name': str(port)
        }
    tmp = {}
    tmp['port'] = port
    tmp['pid'] = pid
    tmp['name'] = _get_hwnd_by_pid(int(pid)).split(' - Dofus')[0]
    return tmp

open_ports = []
packet_buffer = []
open_env = {}

def sniffer_callback(packet, direction, has_raw, sport, dport):
    if direction == "CLI":
        if has_raw:
            if dport not in open_ports:
                window = get_window_info(dport)
                window_name = window['name']
                if "Dofus " in window_name:
                    LOG.debug("Skipping start window containing Dofus")
                    return
                if window_name.strip() == "":
                    LOG.debug("Skipping start window containing Dofus")
                    return
                LOG.info(f"Found window {window['name']}")
                options.available_windows[dport] = window
                if options.current_window is None:
                    options.current_window = window
                    options.set_window()
                    GUI.send_header()
                open_ports.append(dport)
                open_env[window['port']] = None
                for key, window_ref in open_env.items():
                    if window_ref is not None and window_ref.name == window['name']:
                        open_env[window['port']] = Window.Window(window_name, window_ref.env.player)
                        break
                if open_env[window['port']] == None:
                    open_env[window['port']] = Window.Window(window_name)
            window_name = options.available_windows[dport]['name']
            window = options.available_windows[dport]
            if 'Dofus ' in window_name:
                options.available_windows[dport]['name'] = get_window_info(dport)['name']
                window_name = options.available_windows[dport]['name']
                open_env[window['port']] = Window.Window(window_name)
            if options.current_window is None:
                return
            if options.current_window['name'] == window_name or options.multi == True:
                open_env[window['port']].add_packet(packet, direction)
    if direction == "SRV":
        if sport in open_ports and options.available_windows != {} and options.available_windows[sport] != None:
            window_name = options.available_windows[sport]['name']
            window = options.available_windows[sport]
            if options.current_window['name'] == window_name or options.multi == True and open_env[window['port']] is not None:
                open_env[window['port']].add_packet(packet, direction)

e = Event()
packets = Queue()

def put_packet(packet):
    global e
    if e.is_set() == True:
        sys.exit(0)
    packets.put(packet)

def unthreaded_sniffer(e, pcap):
    if options.tshark_path == None or options.tshark_path == "":
        cap = pyshark.FileCapture(pcap)
    else:
        cap = pyshark.FileCapture(pcap, tshark_path=options.tshark_path)
    cap.set_debug()
    for packet in cap:
        src_port = packet[packet.transport_layer].srcport
        dst_port = packet[packet.transport_layer].dstport
        if src_port == '5555':
            direction = "CLI"
        else:
            direction = "SRV"
        has_raw = bool(hasattr(packet, 'tcp') and hasattr(packet.tcp, 'payload'))
        sniffer_callback(packet, direction, has_raw, src_port, dst_port)

def sniffer(e, pcap=None):
    if pcap is not None:
        if options.tshark_path == None or options.tshark_path == "":
            cap = pyshark.FileCapture(pcap)
        else:
            cap = pyshark.FileCapture(pcap, tshark_path=options.tshark_path)
        for packet in cap:
            packets.put(packet)
            #src_port = packet[packet.transport_layer].srcport
            #dst_port = packet[packet.transport_layer].dstport
            #if src_port == '5555':
                #direction = "CLI"
            #else:
                #direction = "SRV"
            #has_raw = bool(hasattr(packet, 'tcp') and hasattr(packet.tcp, 'payload'))
            #sniffer_callback(packet, direction, has_raw, src_port, dst_port)
    else:
        if options.tshark_path == None or options.tshark_path == "":
            cap = pyshark.LiveCapture(interface=options.connection, bpf_filter='tcp port 5555')#, output_file=f'{os.path.expandvars(r"%LOCALAPPDATA%")}/ChrisPS/debug_log.pcap')
        else:
            cap = pyshark.LiveCapture(interface=options.connection, bpf_filter='tcp port 5555', tshark_path=options.tshark_path)#, output_file=f'{os.path.expandvars(r"%LOCALAPPDATA%")}/ChrisPS/debug_log.pcap')
        cap.set_debug()
        v = False
        cap.apply_on_packets(put_packet)

def dispatch(e, pcap):
    GUI.initial_values()
    sniffer(e, pcap=pcap)

def version_checker():
    try:
        r = requests.get('https://chrisps.captain-nwa.ch/version')
        if float(r.json()) > float(__version__):
            LOG.error(f"Your version is outdated ({str(__version__)}). Visit https://chrisps.captain-nwa.ch/ChrisPS to update it ({str(r.json())}).")
    except:
        pass

def read_packets(p):
    while True:
        try:
            packet = p.get(block=False)
            src_port = packet[packet.transport_layer].srcport
            dst_port = packet[packet.transport_layer].dstport
            if src_port == '5555':
                direction = "CLI"
            else:
                direction = "SRV"
            has_raw = bool(hasattr(packet.tcp, 'payload'))
            sniffer_callback(packet, direction, has_raw, src_port, dst_port)
        except Empty:
            continue


def main():
    global e
    parser = argparse.ArgumentParser(description='Launch options')
    parser.add_argument('-p', help='Use pcap instead of live', type=str, default=None)
    parser.add_argument("-v", "--verbosity", action="count", help="increase output verbosity", default=0)
    parser.add_argument("-n", action="store_true", help="disables the shitty looking ui", default=False)
    parser.add_argument("-o", action="store_true", help="old version parsing", default=False)
    args = parser.parse_args()
    #args.verbosity = 1
    LOG.set_verbosity(args.verbosity)
    LOG.clear(1)
    LOG.welcome(__version__)
    version_checker()
    if options.connection == "":
        LOG.error(
            """
        Précisez le type de connexion internet utilisée:
         1- Ethernet
         2- Wi-Fi
         """)
        a = input("1/2/Autre: ")
        if a.strip() == "1":
            options.connection = "Ethernet"
        elif a.strip() == "2":
            options.connection = "Wi-Fi"
        else:
            options.connection = a
        options.update_connection_type()
    if options.tshark_path == None:
        LOG.error(
            """
            Enter Wireshark installation folder.
            Leave blank for default installation.
            """
        )
        a = input ("folder: ")
        options.tshark_path = a
        options.update_tshark_path()
    #if args.p != "":
        #unthreaded_sniffer(e, args.p)
        #return

    if args.n == True:
        s_thread = Thread(target=sniffer, args=(e, args.p))
        s_thread.setDaemon(True)
        s_thread.start()
        read_packets(packets)
    else:
        process = Thread(target=sniffer, args=(e, args.p))
        process.setDaemon(True)
        process.start()
        preader = Thread(target=read_packets, args=(packets, ))
        preader.setDaemon(True)
        preader.start()
        window = webview.create_window('ChrisPS', html=html, frameless=False, width=418, height=358, resizable=True, js_api=GUI, on_top=True)
        GUI.set_window(window)
        webview.start(debug=True, gui='edgechromium', http_server=True)
        LOG.error("closing")
        sys.exit(0)

if __name__ == "__main__":
    main()
