try:
	import mu_class
except:
	print("Mu class is not in this file directory. It's needed for writing results. Exiting.")
	exit()

import logging
import numpy as np
import pandas
import argparse
import pickle
import math
import datetime
from os import system

logging.basicConfig(filename='log.txt', level=logging.DEBUG)
columns = ["Bedrooms", "netTaxableValue", "HouseNo"]
	
def main():
	global data
	try:
		data = pandas.read_csv("data.csv")[columns]
	except:
		print("data.csv could not be read, check your path:")
		os.system('ls')
		exit()
		
	parser = argparse.ArgumentParser(description="Real estate AI")
	parser.add_argument("range_start", type=int, help="Mu count range to start at")
	parser.add_argument("range_end", type=int, help="Mu count range you want to end at (not inclusive)")
	parser.add_argument("-o", "--override", help="Override the limit of range_end being capped at 999", action='store_true')
	parser.add_argument("-d", "--dont_skip", help="In the event a convergence makes a mu have no points, don't skip and reiterate with new random mu values", action='store_true')
	parser.add_argument("-q", "--quantile",  type=int, help="Will cut off N %ile above and below", default=5, choices=range(0,41))
	args = parser.parse_args()

	upper = (100 - args.quantile ) / 100
	lower = args.quantile / 100
	data = data[data.Bedrooms < data.Bedrooms.quantile(upper)]
	data = data[data.Bedrooms > data.Bedrooms.quantile(lower)]
	data = data[data.netTaxableValue < data.netTaxableValue.quantile(upper)]
	data = data[data.netTaxableValue > data.netTaxableValue.quantile(lower)]
	data = data[data.HouseNo < data.HouseNo.quantile(upper)]
	data = data[data.HouseNo > data.HouseNo.quantile(lower)]
	
	if args.range_start < 2 or args.range_end <= args.range_start or args.range_end > 1000:
		if not args.override:
			print("Invalid arguments")
			exit()

	delta = range(args.range_start, args.range_end)
	print("Will iterate through",len(delta),"value(s)")
	lost_a_mu = False
	just_randomized = False
	for i in delta:
		print("Starting iteration", i)
		mu = init_mus(i)
		counter = 0
		while True:
			mu_array = np.array(mu)
			mu_points = new_points(mu_array)
			new_mu = converge(mu_points)
			if len(new_mu) != len(mu_array):
				if args.dont_skip:
					x = init_mus(len(mu_array) - len(new_mu))
					for i in x:
						mu_array += i
					print("Shaking around to capture another mu...")
					just_randomized = True
				else:
					logging.debug("Lost a mu. Continuing with next iteration.")
					lost_a_mu = True
					break
			if not just_randomized:
				if equivalent(mu_array, new_mu):
					lost_a_mu = False
					break
			mu = new_mu
		if not lost_a_mu:
			write_results(mu, mu_points,i)
			logging.info("Converged:" + str(mu))

def new_points(mu_array):
	data_array = np.array(data)
	n = len(mu_array)
	mu_points = [[]] * n
	
	if n < 2:
		write_stack_trace("Your array is not big enough")
	
	for i in data_array:
		minimum = distance(i,mu_array[0])
		min_index = 0
		for j in range(1,n):
			d = distance(mu_array[j], i)
			if d < minimum:
				min_index = j
				minimum = d
		mu_points[min_index] = mu_points[min_index] + [i]
	return mu_points
		
def init_mus(mu_count):
	triples = pandas.DataFrame(np.random.rand(mu_count,3))
	maximum = []
	for i in columns:
		maximum += [max(data[i])]
	for i in range(3):
		triples.iloc[:, i] *= maximum[i]
	return triples

def converge(mu_points):
	mus = []
	for i in mu_points:
		n = len(i)
		frame = np.array(i)
		if len(frame) != 0:
			mus += [[sum(frame[:,0]) / n,sum(frame[:,1]) / n,sum(frame[:,2]) / n]]			
	return mus

def equivalent(mu, new_mu):
	n = len(mu)
	m = len(mu[0])

	for i in range(n):
		for j in range(m):
			if mu[i][j] != new_mu[i][j]: return False
	return True

def write_results(mu, mu_points, range_numbering):
	try:
		d = datetime.date.today()
		filename = str(d.year) + str(d.month) + str(d.day) + "_mu_" + str(range_numbering) + ".mu"
		pickle.dump(mu_class.Mu_data_collection(mu, mu_points), open("mu/" + filename, 'bw'))
	except Exception as e:
		logging.critical("Writing results failed")
		write_stack_trace(e)

def write_stack_trace(exception, message=None):
	f = open("stack_trace.txt", 'a' if file_exists("stack_trace.txt") else 'w')
	f.write("============================================================================\n")
	f.write(str(datetime.datetime.now()) + "\n\n")
	if message != None:
		f.write(message + "\n")
	f.write(str(exception))
	f.close()

def distance(p0, p1):
	try:
		return math.sqrt((p0[0] - p1[0])**2 + (p0[1] - p1[1])**2 + (p0[2] - p1[2])**2)
	except Exception as e:
		write_stack_trace(e, "Error with getting the distance")

main()
