import math
from time import gmtime, strftime
import os
import shutil
import random
import string
from os import listdir
import StringIO
import gzip
from os.path import exists,isfile, join
import socket
import sys
from utils.rsacrypt import RSACrypt
from utils.colors import bcolors
from utils.colors import update_progress
from client_config import client_conf
from Crypto.Hash import MD5

conf = client_conf()

class ServerConnection:
    
    def __init__(self, server_address = conf.c['server_address'], server_port = int(conf.c['server_port'])):
        self.serversocket = socket.socket(
	socket.AF_INET, socket.SOCK_STREAM)
	self.server_address = server_address
	self.server_port = server_port
	
    def connect(self, do_login = True):
	try:
	    self.serversocket.connect((self.server_address, self.server_port))
        except socket.error, (value,message): 
    	    if self.serversocket: 
    		self.serversocket.close() 
    	    print bcolors.FAIL + "Could not connect to server (%s:%s): " %(self.server_address, self.server_port) + message  + bcolors.ENDC
	    self.connected = False
	    return False
	try:
	    kf = open(conf.c['server_public_rsa'])
	    self.server_public_key = kf.read()
	    kf.close()
	    kf = open(conf.c['client_public_rsa'])
	    self.my_public_key = kf.read()
	    kf.close()
	    kf = open(conf.c['client_private_rsa'])
	    self.my_private_key = kf.read()
	    kf.close()
	except:
	    print bcolors.WARNING + "Error loading key files."  + bcolors.ENDC

	self.connected = True
	# Don't wait more than 15 seconds for the server.
	self.serversocket.settimeout(15)
	print bcolors.OKBLUE + strftime("%Y-%m-%d %H:%M:%S") + ": Server connection successful." + bcolors.ENDC
	if do_login:
	    self.logged_in = self.login()
	else:
	    self.logged_in = False
	self.connected = True
	return True

    def disconnect(self):
	if not self.connected:
	    return True
	if self.serversocket:
	    print bcolors.WARNING + strftime("%Y-%m-%d %H:%M:%S") + ": Closing connection to the server." + bcolors.ENDC
	    try:
		#no need to authenticate when closing...
		self.send_dyn("unauthorized")
		self.send_fixed("x")
	    except:
		pass
	    self.serversocket.close()

    def send_fixed(self, data):
	if not self.connected:
	    print bcolors.FAIL + "Server not connected!" + bcolors.ENDC
	    raise Exception("Not connected.")
	    return False

	try:
	    sent = self.serversocket.send(data)
	except socket.error, (value,message): 
	    if self.serversocket: 
    		self.serversocket.close() 
    	    raise Exception("Could not send data to server (%s:%s): " %(self.server_address, self.server_port) + message)
	    return False
	    
	#print "Sent %d bytes to the server." %(sent)
	return True

    def send_dyn(self, data):
	if not self.connected:
	    print bcolors.FAIL + "Server not connected!" + bcolors.ENDC
	    return False
	self.send_fixed(str(len(data)).zfill(10))
	self.send_fixed(data)
    
    def receive_fixed(self, message_len):
	if not self.connected:
	    print bcolors.FAIL + "Server not connected!" + bcolors.ENDC
	    raise Exception("Not connected.")
	    return False
	chunks = []
        bytes_recd = 0
        while bytes_recd < message_len:
            chunk = self.serversocket.recv(min(message_len - bytes_recd, 2048))
            if chunk == '':
                raise Exception("Socket connection broken (%s:%s): " %(self.server_address, self.server_port))
            chunks.append(chunk)
            bytes_recd = bytes_recd + len(chunk)
	#print ''.join(chunks)
        return ''.join(chunks)
    
    def receive_dyn(self):
	msg_size = self.receive_fixed(10)
	msg = self.receive_fixed(int(msg_size))
	return msg

    def receive_crypt(self, decryption_key, show_progress=True):
	crypt = RSACrypt()

	crypt.import_public_key(decryption_key)

	chunk_count = int(self.receive_dyn())
	received_digest = self.receive_dyn()

	org = chunk_count
	chunk_size = 256
	decrypted_results = ""
	
	if show_progress:
	    print bcolors.OKBLUE + "Progress: "
	while chunk_count > 0:
	    encrypted_chunk = self.receive_dyn()
	    decrypted_results = decrypted_results + crypt.public_key_decrypt(encrypted_chunk)
	    chunk_count = chunk_count - 1
	    if show_progress:
		update_progress( int(100 * float(org - chunk_count) / float(org)) )
	if show_progress:
	    print bcolors.ENDC
    
	calculated_digest = MD5.new(decrypted_results).digest()
	if calculated_digest == received_digest:
	    return decrypted_results
	else:
	    print bcolors.FAIL + "Data integrity check failed." + bcolors.ENDC
	    return False


    def send_crypt(self, data, encryption_key):
	crypt = RSACrypt()
	crypt.import_public_key(encryption_key)