import os
import sys
import subprocess
import atexit
from threading import Timer
import string
import random
import signal
import socket
import codecs
import json
import datetime
import time

whoami = subprocess.getoutput(["whoami"])
with open(f'/home/{whoami}/ze_runner/config.json') as jsonfile:
        data_ports = json.load(jsonfile)
looptestPath = f"/home/{whoami}/.steam/debian-installation/steamapps/common/Counter-Strike Source/cstrike/cfg/looptest.cfg"
chatmsg = ""

def writeCfgInput(Input_user):
    with open(looptestPath, 'w') as f:
        f.write(Input_user)
        #print(Input_user)
    time.sleep(0.1)
    open(looptestPath, 'w').close()

def exit_handler():
    print('reached exithandler')
    writeCfgInput('')
    #securing the looptest.cfg wont be stuck accidently with commands

def joinTeam():
    str = "jointeam 2; joinclass 3; zspawn;"
    writeCfgInput(str)
    print('jointeam func: ')

def bot_process_movement(input_line):
    dist_target = input_line[input_line.index("dist_target:") + len("dist_target:"):input_line.index("targethuman:")]
    targethuman = input_line[input_line.index("targethuman:") + len("targethuman:"):input_line.index("enemy_distance:")]
    enemy_distance = input_line[input_line.index("enemy_distance:") + len("enemy_distance:"):input_line.index("targeteam:")]
    targeteam = input_line[input_line.index("targeteam:") + len("targeteam:"):input_line.index("target_enemy:")]
    target_enemy = input_line[input_line.index("target_enemy:") + len("target_enemy:"):]
    dist_target = float(dist_target)
    enemy_distance = float(enemy_distance)
    targeteam = int(targeteam)
    min_distance_target_human = 1.0
    strInput = "-attack; wait 2; -use; wait 5; +attack; wait 5; cl_minmodels 1; wait 2; +use; +forward; wait 2; "
    if dist_target > min_distance_target_human:
        #print('dist_target: ', dist_target)
        strInput += "use weapon_elite; wait 3; "
    elif targeteam == 3:
        strInput += "use weapon_p90; wait 3; "
    elif targeteam == 2:
        strInput += "use weapon_knife; wait 5; "
    print('date: ', datetime.datetime.now().time(), ' target_enemy: ', target_enemy, ' enemy distance: ', enemy_distance, ' target human: ', targethuman, 
           ' dist_target: ', dist_target) 
    strInput = strinput_append(strInput, 2)
    #print('strInput final:', strInput)
    writeCfgInput(strInput)

def strinput_append(strInput, nth):
    for _ in range(10 * nth):
        boolean_val = random.choice([True, False])
        if boolean_val:
            strInput += "+moveleft; wait 15; -moveleft; "
        else:
            strInput += "+moveright; wait 15; -moveright; "
    return strInput

def kill_user_owned_pid(pid):
    print('pid: ', pid, ' killed')
    pid = int(pid.strip())
    os.kill(pid, signal.SIGKILL)
    time.sleep(10)

def return_user_owned_pid(pidof):
    owner_pid = subprocess.getoutput([f"{pidof}"])
    if owner_pid:
        for pid in owner_pid.split(" "):
            username = subprocess.getoutput([f"""ps -o user= -p {pid}"""])
            if username == whoami:
                return pid
    return None

def kill_owned_process(pidof):
    pid = return_user_owned_pid(pidof)
    while pid:
        kill_user_owned_pid(pid)
        pid = return_user_owned_pid(pidof)

def restart_sdl_and_steam():
    #ending screen
    subprocess.getoutput(["screen -XS XTERM quit"])
    kill_owned_process("pidof hl2_linux")
    kill_owned_process("pidof xterm")
    
    x2go_session_list = subprocess.getoutput(["x2golistsessions"])
    print('x2go_session_list: ', x2go_session_list)
    if not x2go_session_list:
        print('no session available. establish the session manually again')
        raise Exception('')

    x2go_session_display = x2go_session_list.split('|unloze.com')[0].rsplit('|', 1)[1]
    x2go_session_pid = x2go_session_list.split('|')[0]
    x2go_session_id = x2go_session_list.split('|')[1].split('|')[0]
    x2go_session_command = 'TERMINAL xterm' 

    subprocess.getoutput([f"screen -d -m -S XTERM x2goruncommand {x2go_session_display} {x2go_session_pid} {x2go_session_id} {x2go_session_command}"])
    print('reached .bashrc executing steam and variables')
    time.sleep(60)
    #we sleep here to wait for .bashrc to launch steam. It takes some minutes

def launch_css_process():
    print('preparing to launch game....')
    #deleting maps
    subprocess.getoutput([f"rm '/home/{whoami}/.steam/debian-installation/steamapps/common/Counter-Strike Source/cstrike/download/maps/'*"])
    
    #launching game
    subprocess.getoutput([f"/home/{whoami}/.steam/debian-installation/steam.sh -applaunch 240 -textmode -novid -nosound -noipx -nojoy -noshaderapi -port {data_ports['steam_port']}"])
    print('finished starting game')

def bot_connect(server, connected_to_other):
    #use whatever ip you want here to connect with
    str1 = ""
    if "ze" in server:
        str1 = f"connect {data_ports['server_ip_port_ze']}"
        connected_to_other = False
    elif "gg" in server and not connected_to_other:
        str1 = f"connect {data_ports['server_ip_port_gg']}"
    elif not connected_to_other:
        str1 = f"connect {data_ports['server_ip_port_zr']}"
    writeCfgInput(str1)
    print('not yet connected')

def pairwise(it):
    it = iter(it)
    while True:
        try:
            yield next(it), next(it)
        except StopIteration:
            # no more elements in the iterator
            return

if __name__ == '__main__':
    atexit.register(exit_handler)
    ovh_ip = "135.125.188.157"
    local_port = data_ports['udp_port']
    external_port_messages = data_ports['chat_external_port']
    buffer_size = 4096 #potentially not large enough?
    connection_issue_counter = 0;
    pid = return_user_owned_pid("pidof hl2_linux")
    if not pid:
        restart_sdl_and_steam()
    else:
        kill_owned_process("pidof hl2_linux")
    connected_to_other = False
    launch_css_process()
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    sock.bind(("", local_port))
    sock.settimeout(5.0)
    messager_name = ""
    print('reached deadlock')
    try:
        while True:
            try:
                data, addr = sock.recvfrom(buffer_size)
            except socket.timeout:
                continue
            data = codecs.decode(data, "utf-8", "ignore")
            ip = addr[0]
            port = addr[1]
            #print('port: ', port, " ip: ", ip)
            #print('data: ', data)
            if not data:
                continue
            if ip == ovh_ip and port == external_port_messages:
                if messager_name in data:
                    messager_name = ""
                response_msg = f"""say {messager_name} {data}"""
                print("remote UDP packet response_msg: ", response_msg)  
                writeCfgInput(response_msg)
            if ip != ovh_ip:
                continue
            #print("data: ", data)
            if data == "autismo connected":
                print('Bot connected!')
                connection_issue_counter = 0
                joinTeam()
            elif data == "rtv":
                response_msg = "say rtv"
                writeCfgInput(response_msg)
            elif data == "bot kicked server full":
                print('bot kicked server full: ', datetime.datetime.now().time())
            elif "connected to" in data:
               connected_to_other = True
            elif "connect to" in data:
                if connection_issue_counter == 20:
                    kill_owned_process("pidof hl2_linux")
                    launch_css_process()
                    connected_to_other = False
                elif connection_issue_counter == 50:
                    restart_sdl_and_steam()
                    launch_css_process()
                    connected_to_other = False
                    connection_issue_counter = -10
                if not connected_to_other or "ze" in data:
                    connection_issue_counter += 1
                    print('connection_issue_counter: ', connection_issue_counter)
                    bot_connect(data, connected_to_other)
            elif "clientmessage:" in data:
                messager_name = data.split("clientmessage:", 1)[1].split(" 72DqZ84")[0]
                databyte_send_message = messager_name + data.split("72DqZ84")[1]
                sock.sendto(databyte_send_message.encode(), (ovh_ip, external_port_messages))
                print('databyte_send_message: ', databyte_send_message)
            elif data.startswith("dist_target:"):
                bot_process_movement(data)
            elif data.startswith("surfing:"):
                bot_process_surf(data)
            elif data.startswith("hull info:"):
                hull_info = data[data.index("hull info:") + len("hull info:"):]
                strInput = ""
                if hull_info == "jump":
                    strInput += "+jump; wait 5; -jump; +duck; wait 50; -duck; wait 5; "
                elif hull_info == "crouch":
                    strInput += "+duck; wait 50; -duck; wait 5; "
                writeCfgInput(strInput)
    finally:
        sock.close()

#/home/gameservers/.steam/debian-installation/steamapps/common/Counter-Strike Source/cstrike/cfg/autoexec.cfg:
#alias loop "exec looptest.cfg; wait 5; loop;"; wait 5; loop;
#create looptest.cfg in /cfg/ folder
#X2GO_NXAGENT_DEFAULT_OPTIONS="-options nx/nx,sleep=0"  /etc/x2go/x2goagent.options   
#keeps session as R instead of S which is needed for interaction with graphical components such as steam and CS:S

#cd /home/gameservers/ze_runner_files
#python3 ingamefollowct.py
#screen -d -m -S ze_runner python3 ingamefollowct.py

#before steam login: export SDL_VIDEO_X11_VISUALID=0x074
#to find correct SDL_VIDEO_X11_VISUALID use glxinfo in X2GO

"""
.bashrc requires:
if [[ $DISPLAY ]]; then
        echo 'SDL_VIDEO_X11_VISUALID: ' $SDL_VIDEO_X11_VISUALID
        export SDL_VIDEO_X11_VISUALID=0x205
        echo 'SDL_VIDEO_X11_VISUALID: ' $SDL_VIDEO_X11_VISUALID
        steam
fi
"""