#!/usr/bin/env python3
import datetime
import sys
import os
import requests
import json

DEBUG=False
defaultstop = 100636

baseurl		= "http://ivu.aseag.de/interfaces/ura/{}"
url_l		= "location"
url_j		= "journey"
url_i		= "instant_V2"
returnlist	= "StopPointName,StopID,StopPointState,StopPointIndicator,Latitude,Longitude,VisitNumber,TripID,VehicleID,LineID,LineName,DirectionID,DestinationName,DestinationText,EstimatedTime,BaseVersion"
usage		= 'Usage: ./main [StopID/StopName] [BusID] ([BusID]…) +[MaxWait]'
infotext	= "Haltestelle:    {}\nHaltestellenID: {}\nLinienfilter:   {}"

totime = 0

if len(sys.argv) == 1 and defaultstop == 0:
	print(usage.format(__file__))
	exit(0)

def debug(text):
	if DEBUG == True:
		print(text)

def unix_epoch_to_datetime(epoch_ms):
	return datetime.datetime.fromtimestamp(epoch_ms * (10**(-3)))

def unix_epoch_to_date(epoch_ms):
	return unix_epoch_to_datetime(epoch_ms).strftime('%Y-%m-%d %H:%M:%S')

def unix_epoch_to_time(epoch_ms):
	return unix_epoch_to_datetime(epoch_ms).strftime('%H:%M:%S')

def unix_epoch_from_now():
	return int(datetime.datetime.now().timestamp() * 1000)

def get_stoppoint(name):
	parameter = {'searchString': name, 'maxResults': 10, 'searchTypes': 'STOPPOINT'}
	request = requests.get(baseurl.format(url_l), params=parameter)
	if request.status_code != 200:
		raise Exception
	if request.headers['content-type'] != 'application/json;charset=UTF-8':
		raise Exception
	data = request.json()
	if data['resultCount'] == 0:
		debug("No result for stop name {}".format(name))
		return {}
	elif data['resultCount'] > 1:
		print("More than one result for stop name {}:".format(name))
		print("ID\tName")
		for elem in data['resultList']:
			print("{}\t{}".format(elem['stopPointId'], elem['stopPointName']))
		exit(0)
		# this might be broken anyway
		#return dict((elem['stopPointId'] = elem['stopPointName']) for elem in data['resultList'])

	else:
		debug("ID\tName")
		debug("{}\t{}".format(data['resultList'][0]['stopPointId'], data['resultList'][0]['stopPointName']))
		return {data['resultList'][0]['stopPointId']: data['resultList'][0]['stopPointName']}

def parsejson(data, encoding):
	output = []
	for line in data.splitlines():
		linelist = json.loads(line)
		if (linelist[0] == 1):
			output.append((linelist[15],linelist[8],linelist[12]))
	output.sort(key=lambda tup: tup[0])
	return output

# See: http://stackoverflow.com/questions/480214/how-do-you-remove-duplicates-from-a-list-in-python-whilst-preserving-order
def deduplication(data):
	jsondata = data
	seen = set()
	seen_add = seen.add
	jsondata = [x for x in jsondata if not (x in seen or seen_add(x))]
	return jsondata

def tsfilter(ts, totime): # Todo: schöner machen
	if totime == 0:
		return (unix_epoch_to_datetime(ts) >= datetime.datetime.now() - datetime.timedelta(minutes = 5))
	else:
		return (unix_epoch_to_datetime(ts) >= datetime.datetime.now() - datetime.timedelta(minutes = 5)) and (unix_epoch_to_datetime(ts) <= datetime.datetime.now() + datetime.timedelta(minutes = totime))

def get_stopdata(stop_point_id, lines):
	parameter = {'ReturnList': returnlist, 'StopID': stop_point_id}
	if lines:
		parameter['LineID'] = ",".join(lines)
	request = requests.get(baseurl.format(url_i), params = parameter)
	if request.status_code != 200:
		raise Exception
	if request.headers['content-type'] != 'application/json;charset=UTF-8':
		raise Exception
	data = deduplication(parsejson(request.text, request.encoding))
	return data

def get_routedata(start, stop):
	parameter = {'startStopId': start, 'endStopId': stop, 'departureTime': unix_epoch_from_now(), 'maxNumResults': 4}
	if totime:
		parameter['maxNumResults'] = totime
	request = requests.get(baseurl.format(url_j), params = parameter)
	if request.headers['content-type'] != 'application/json;charset=UTF-8':
		raise Exception
	data = request.json()
	return data

def parseargv(): #parses argv, returns StopID and LineID
	query_stop = []
	query_ids  = []
	argc = len(sys.argv)
	if argc >  1:
		for elem in sys.argv:
			if (elem == __file__):
				continue
			if ('help' in elem or elem == '-h'):
				print(usage)
				exit(0)
			if (len(elem) == 6 and elem.isdigit()):
				query_stop.append(int(elem))
			elif (1 <= len(elem) <= 3 and elem.isdigit()):
				query_ids.append(elem)
			elif elem.lower() == 'debug':
				global DEBUG
				DEBUG=True
			elif elem[0] == '+':
				global totime
				try:
					totime = int(elem[1:])
				except:
					pass
			elif elem.isprintable():
				query_stop.append(get_stoppoint(elem).keys())
			else:
				print("Unknown parameter {!r}".format(elem))
	query_ids.sort(key=int)
	return (query_stop, query_ids)

def output(data):
	(haltid, ids) = data
	if len(haltid) == 1:
		debug("Haltestellenabfrage")
		output = get_stopdata(haltid[0], ids)
		for line in output:
			if tsfilter(line[0], totime):
				print("{} {:>3} {}".format(unix_epoch_to_time(line[0]), line[1], line[2]))
	elif len(haltid) == 2:
		debug("Routenabfrage")
		output = get_routedata(haltid[0], haltid[1])
		for elem in output['resultList']:
			try:
				print("========")# Von {} nach {}".format(elem['startLocation']['stopPointName'], elem['endLocation']['stopPointName']))
				for connection in elem['elementList']:
					if connection["type"] == "LineChange":
						print("Bus wechselt Linie")
					elif connection['modalType'] == "bus":
						print("{} ab {:<30}{:>3} {}".format(
							unix_epoch_to_time(
								connection['start']['aimedArrivalInUnixEpochMillis'] or
								connection['start']['estimatedArrivalInUnixEpochMillis'] or
								connection['start']['scheduledArrivalInUnixEpochMillis']
							), 
							connection['start']['location']['stopPointName'], 
							connection['lineName'], connection['destinationName'])
						)
						print("{} an {}".format(
							unix_epoch_to_time(
								connection['end']['aimedArrivalInUnixEpochMillis']	or 
								connection['end']['estimatedArrivalInUnixEpochMillis'] or
								connection['end']['scheduledArrivalInUnixEpochMillis']
							),
							connection['end']['location']['stopPointName'])
						)
					elif connection['modalType'] == "walk":
						print("{} ab {:<30} laufen".format(
							unix_epoch_to_time(
								connection['start']['aimedArrivalInUnixEpochMillis'] or
								connection['start']['estimatedArrivalInUnixEpochMillis'] or
								connection['start']['scheduledArrivalInUnixEpochMillis']
							),
							connection['start']['location']['stopPointName'])
						)
						print("{} an {}".format(
							unix_epoch_to_time(
								connection['end']['aimedArrivalInUnixEpochMillis'] or 
								connection['end']['estimatedArrivalInUnixEpochMillis'] or
								connection['end']['scheduledArrivalInUnixEpochMillis']
							), 
							connection['end']['location']['stopPointName'])
						)
					print()
			except KeyError as e:
				print(e)
				print(connection)
	else:
		print("Fehler: Weniger als 1 oder mehr als 2 HaltestellenIDs")
		exit(1)
	linien = " ".join(ids)

data = parseargv()

debug('=== Start')
output(data)

