import csv 
from Record import *
from datetime import datetime
import json

class Point():
    def __init__(self,x,y,name=None):
        self.x  = x
        self.y = y 
        if name != None:
            self.name = name
class Trip():
    def __init__(self,start=None,end=None,start_time=None,end_time=None):
        self.start = start
        self.end = end
        self.start_time = start_time
        self.end_time = end_time
        self.all_locations = None #used for intermediate points 
        self.all_times = None #used for intermediate times
        self.in_vehicle_time = None
        self.trip_time = None
        self.waiting_time = None
    def setLocation(self,start,end):
        self.start = start
        self.end = end
    def setTime(self,start_time,end_time):
        self.start_time = start_time
        self.end_time = end_time
    def computeTripDistance(self):
        pass
    def computeTripTime(self):
        # print "start:" + str(self.start_time) + " end: " + str(self.end_time)
        self.trip_time = self.end_time - self.start_time
    def setInvehicleTime(self,in_vehicle_time):
        self.in_vehicle_time = in_vehicle_time
    def computeWaitingTime(self):
        self.waiting_time = self.trip_time - self.in_vehicle_time
    def timeToMin(self,t_hour=None,t_min=None,t_sec=None):
        total_min = 0
        if t_hour is not None:
          total_min = total_min + t_hour * 60
        if t_min is not None:
            total_min = total_min + t_min
        if t_sec is not None:
            total_min = float(total_min) + float(t_sec)/float(60)
        return total_min
    def timeSlot(self,t_hour = None, t_min=None,t_sec = None):
        divide_min = self.timeToMin(t_hour,t_min,t_sec)
        start_min = self.timeToMin(self.start_time.hour,self.start_time.minute,self.start_time.second)
        slot = int(start_min/divide_min)
        self.timeslot = slot
        return slot
    def waitingTime(self,in_vehicle_time):
        # print in_vehicle_time
        self.waiting_time = self.trip_time - in_vehicle_time
        self.waiting_time_to_min = (self.waiting_time.seconds + self.waiting_time.days * 24 * 3600)/float(60)
        return self.waiting_time_to_min
    def originDestination(self):
        return (self.start.station_id,self.end.station_id)


class Transportation():
    def __init__(self):
        self.dir_path = None
        self.file_path = None
    def setFileDirectoryPath(self,dir_path):
        self.dir_path = dir_path
    def setFilePath(self,file_path):
        self.file_path = file_path
    def setTransRegionFilePath(self,file_path):
        self.trans_region_file_path = file_path

    def writeToLocal(self,data,file_path):
        file_handler = open(file_path,'wb')

    def minimumByKey(self,a,b):
        if a > b:
            return b
        else:
            return a

    def isInTranRegion(self,sc):
        simple_region_file_path = "hdfs://namenode:9000/zf72/data/shenzhen_tran_simple_gps.json"
        region_file = sc.textFile(simple_region_file_path)
        file_content = region_file.collect()
        file_content = json.loads(file_content[0])
        (polygons,polygon_ids) = buildPolygon(file_content)
        file_content['out_edge']
        print(file_content['out_edge'])
    
    def TimeSlotDensity(self,sc):
        dir_path = "/zf72/human_mobility/Tranporation"
        new_name = "tranportation"
        client = Config().get_client('dev')
        file_dates = client.list(dir_path)
        for file_date in file_dates:
            print(file_date)
            files = sc.textFile(dir_path+"/"+file_date+"/*").cache()
            lines = files.map(extract_user_slot_location_telecom).map(lambda (k,v):((v,k),1)).reduceByKey(lambda a,b: a+b)\
                .map(lambda ((k,userid),v):(k,1)).reduceByKey(lambda a,b: a+b).map(lambda (k,v): (tuple(k.split(',')),v))\
                .map(lambda (k,v): ((int(k[0]), float(k[1]), float(k[2])), v))\
                .sortByKey(True,keyfunc=lambda k: int(k[0]))
            lines.saveAsTextFile(dir_path+'_'+new_name+'/'+file_date)

class LocalAnalysis():
    def test(self):
        print "test"

class Subway(Transportation):
    def __init__(self):
        self.file_path = None
        self.dir_path = None
        self.record_list = None
        self.all_trips = None
        self.time_matrix = None
        self.in_vehicle_time = None
    def parseTime(self,timeStr):
        return datetime.strptime(timeStr,'%Y-%m-%d %H:%M:%S')
    def subwayOneRow(self,row):
        attrs = row.split(",")
        if attrs[4] == '22':
            in_station = False
        elif attrs[4] == '21':
            in_station = True
        else:
            return None
        recordTime = self.parseTime(attrs[8])
        record = SubwayRecord(user_id=attrs[1],time=recordTime,in_station=in_station,station_id=attrs[13],station_name=attrs[13],route_name=attrs[12],train_id=attrs[14])
        return record
    def generateUniqueSubwayMap(self):
        if self.file_path is None:
            print "ERROR: set file path first"
            return None
        with open(self.file_path) as csvFile:
            for one_row in csvFile:
                record = self.subwayOneRow(one_row)
                if record is not None:
                    print record.in_station
    def inSubwayTime(self):
        if self.file_path is None:
            print "ERROR: set file path first"
            return None
        with open(self.file_path) as csvFile:
            for one_row in csvFile:
                pass

    def buildRecordList(self):
        self.record_list = {}
        if self.file_path is None:
            print "ERROR: set file path first"
            return None
        with open(self.file_path) as csvFile:
            for one_row in csvFile:
                record = self.subwayOneRow(one_row)
                if record is not None:
                    if record.user_id not in self.record_list.keys():
                        self.record_list[record.user_id] = [record]
                    else:
                        self.record_list[record.user_id].append(record)
        return self.record_list

    def tripTime(self,start_record,end_record):
        pass

    def buildTripList(self):
        if self.record_list is None:
            print "INFO: build station list first, call buildRecordList"
            return None
        self.all_trips = []
        for one_user in self.record_list.keys():
            start = None
            end = None
            one_user_records = self.record_list[one_user]
            sorted_records = sorted(one_user_records, key=lambda record: record.time)
            for record in sorted_records:
                if record.in_station:
                    start = record
                else:
                    end = record
                    if start is not None:
                        temp_trip = Trip(start=start,end=end)
                        temp_trip.start_time = start.time
                        temp_trip.end_time = end.time
                        temp_trip.computeTripTime()
                        self.all_trips.append(temp_trip)
                        start =None
    def buildTripTimeMatrix(self):
        if self.all_trips is None:
            print "INFO: build trip list first, call buildTripList"
            return None
        self.time_matrix = {}
        for one_trip in self.all_trips:
            start_end = one_trip.start.station_id+","+one_trip.end.station_id
            timeslot = one_trip.timeSlot(t_min=60)
            if start_end not in self.time_matrix.keys():
                self.time_matrix[start_end] = [one_trip] 
            else:
                self.time_matrix[start_end].append(one_trip)
    def inVehicleTimeEstiamte(self):
        if self.time_matrix is None:
            print "INFO: build time matrix first, call buildTripTimeMatrix"
            return None
        self.in_vehicle_time = {}
        for one_key in self.time_matrix.keys():
            min_trip_time =  -1
            trip_list = self.time_matrix[one_key]
            for one_trip in trip_list:
                if min_trip_time == -1:
                    min_trip_time = one_trip.trip_time
                elif min_trip_time > one_trip.trip_time:
                    min_trip_time = one_trip.trip_time
            self.in_vehicle_time[one_key] = min_trip_time
            
    def waitingTime(self):
        pass 
    def inSubwayTime(self):
        pass
    def waitingTimeOneDay(self):
        self.buildRecordList()
        self.buildTripList()
        self.buildTripTimeMatrix()
        self.inVehicleTimeEstiamte()
        self.one_day_waiting_time = {} 
        self.one_day_average_waiting_time = {}
        for one_key in self.time_matrix.keys():
            trip_list = self.time_matrix[one_key]
            for one_trip in trip_list:
                one_trip.waitingTime(self.in_vehicle_time[one_key])
                if one_trip.timeslot not in self.one_day_waiting_time.keys():
                    self.one_day_waiting_time[one_trip.timeslot] = [one_trip.waiting_time_to_min]
                else:
                    self.one_day_waiting_time[one_trip.timeslot].append(one_trip.waiting_time_to_min)
        for one_key in self.one_day_waiting_time.keys():
            trip_list = self.one_day_waiting_time[one_key]
            self.one_day_average_waiting_time[str(one_key)] = float(sum(trip_list))/float(len(trip_list))
        with open('../data/average_waiting_time.json','w') as fh:
            json.dump(self.one_day_average_waiting_time,fh)
#*********************************for spark use
    def sortedRecordsToTrip(self,input):
        start = None
        end = None
        (k,sorted_records) = input
        all_trips = []
        for record in sorted_records:
                if record.in_station:
                    start = record
                else:
                    end = record
                    if start is not None and start is not None:
                        temp_trip = Trip(start=start,end=end)
                        temp_trip.start_time = start.time
                        temp_trip.end_time = end.time
                        temp_trip.computeTripTime()
                        if start.station_id != end.station_id:
                            all_trips.append(temp_trip)
                        start =None
                        end = None
        return (k,all_trips)

    def mapToStationTimeMapping(self,user_trip_list):
        (user_id,trip_list) = user_trip_list
        od_time = []
        for one_trip in trip_list:
            od_time.append((one_trip.originDestination(),one_trip.trip_time.total_seconds()))
        return od_time
    def mapToODTrip(self,user_trip_list):
        (user_id,trip_list) = user_trip_list
        od_time = []
        for one_trip in trip_list:
            od_time.append((one_trip.originDestination(),one_trip))
        return od_time
    def mapToTripWaitingTime(self,user_trip_in_vehicle_time):
        (od,(one_trip,in_vehicle_time)) = user_trip_in_vehicle_time
        return (one_trip.timeSlot(t_min=60), one_trip.start.time,one_trip.trip_time.total_seconds() - in_vehicle_time)

#************************************************

class Bus(Transportation):

    def __init__(self):
        self.file_path = None
        self.dir_path = None
        self.all_trips = None
        self.time_matrix = None
        self.in_vehicle_time = None

    def parseTime(self, timeStr):
        return datetime.strptime(timeStr, '%H:%M:%S')

    # Parse row to obtain trip
    def busOneRow(self, row):
        attrs = row.split(",")
        startTime = self.parseTime(attrs[4])
        startLocation = Point(x = float(attrs[5]), y = float(attrs[6]))
        endTime = self.parseTime(attrs[7])
        endLocation = Point(x = float(attrs[8]), y = float(attrs[9]))
        trip = Trip(startLocation, endLocation, startTime, endTime)
        trip.computeTripTime()
        return trip

    # Parse file to obtain all trips
    def buildTripList(self):
        if self.file_path is None:
            print "ERROR: set file path first"
            return None
        self.all_trips = []
        num_trips = 0
        with open(self.file_path) as csvFile:
            for one_row in csvFile:
                trip = self.busOneRow(one_row)
                self.all_trips.append(trip)
                num_trips += 1
                if num_trips % 10000 == 0:
                    print num_trips
        return self.all_trips

    # Converts point to string, with rounding if necessary
    def pointToString(self, p):
        return '(%.3f, %.3f)' % (p.x, p.y)

    # For each start/end pair, return all trips from start to end
    # Ignore start/end pairs with only one trip
    def buildTripTimeMatrix(self):
        if self.all_trips is None:
            print "INFO: build trip list first, call buildTripList"
            return None
        self.time_matrix = {}
        num_trips = 0
        for one_trip in self.all_trips:
            start_end = self.pointToString(one_trip.start) + "," + self.pointToString(one_trip.end)
            timeslot = one_trip.timeSlot(t_min = 60)
            if start_end not in self.time_matrix.keys():
                self.time_matrix[start_end] = [one_trip] 
            else:
                self.time_matrix[start_end].append(one_trip)
            num_trips += 1
            if num_trips % 10000 == 0:
                print num_trips

    # For each start/end pair, estimate in-vehicle time
    def inVehicleTimeEstimate(self):
        if self.time_matrix is None:
            print "INFO: build time matrix first, call buildTripTimeMatrix"
            return None
        self.in_vehicle_time = {}
        for one_key in self.time_matrix.keys():
            min_trip_time =  -1
            trip_list = self.time_matrix[one_key]
            if len(trip_list) == 1:
                continue
            for one_trip in trip_list:
                if min_trip_time == -1:
                    min_trip_time = one_trip.trip_time
                elif min_trip_time > one_trip.trip_time:
                    min_trip_time = one_trip.trip_time
            self.in_vehicle_time[one_key] = min_trip_time

    # For each timeslot, compute average delay time
    def waitingTimeOneDay(self):
        print 'Building trip list'
        self.buildTripList()
        print 'Building trip time matrix'
        self.buildTripTimeMatrix()
        print 'Estimating in-vehicle time'
        self.inVehicleTimeEstimate()
        print 'Computing delay time'
        self.one_day_waiting_time = {}
        self.one_day_average_waiting_time = {}
        for one_key in self.time_matrix.keys():
            trip_list = self.time_matrix[one_key]
            if len(trip_list) == 1:
                continue
            for one_trip in trip_list:
                one_trip.waitingTime(self.in_vehicle_time[one_key])
                if one_trip.timeslot not in self.one_day_waiting_time.keys():
                    self.one_day_waiting_time[one_trip.timeslot] = [one_trip.waiting_time_to_min]
                else:
                    self.one_day_waiting_time[one_trip.timeslot].append(one_trip.waiting_time_to_min)
        for one_key in self.one_day_waiting_time.keys():
            trip_list = self.one_day_waiting_time[one_key]
            self.one_day_average_waiting_time[str(one_key)] = float(sum(trip_list))/float(len(trip_list))
        with open('../data/average_waiting_time.json','w') as fh:
            json.dump(self.one_day_average_waiting_time,fh)
        for t in range(23):
            if str(t) in self.one_day_average_waiting_time.keys():
                delay = self.one_day_average_waiting_time[str(t)]
            else:
                delay = None
            print t, delay

if __name__ == "__main__":
    bus = Bus()
    bus.file_path = '../medium'
    bus.waitingTimeOneDay()

#************************************************

class PrivateVehicle(Transportation):
    def __init__(self):
        pass
