import random
import simpy

"""
This is the main run file. 
It creates an enviorment for simpy and then 
attaches that to each elevator and rider. 
"""

MAX_EVEVATOR_SIZE = 5

class Elevator():

    def __init__(self, name, env):
        self.max    = MAX_EVEVATOR_SIZE
        self.riders = []
        self.name   = name
        self.curr_floor = 0
        self.stops  = []
        self.going_up   = True
        self.still  = True
        self.env    = env
        self.speed  = [0, 75]

    def addRider(self,rider):
        self.riders.append(rider)

    def getRiders(self):
        return self.riders

    def run(self):
        while True:
            self.still = True
            if self.stops:
                curr_dest=None
                if self.going_up:
                    curr_dest = max(self.stops)
                else:
                    curr_dest = min(self.stops)

                if curr_dest > self.curr_floor:
                    self.curr_floor+= 1
                    self.still  = False
                    self.going_up   = True
                elif curr_dest < self.curr_floor:
                    self.curr_floor-= 1
                    self.still  = False
                    self.going_up   = False

                for rider in self.riders:
                    rider.curr_floor=self.curr_floor
                yield self.env.timeout(1)
            else:
                yield self.env.timeout(1)

    def add_stop(self, stop):
        self.stops.append(stop)

    def remove_stop(self, stop):
        self.stops.remove(stop)

    def add_rider(self,rider):
        self.riders.append(rider)


class Rider():

    def __init__(self, name, env):
        self.wait   = random.randint(1,5)
        self.name   = name
        self.chosen_elevator    = None
        self.env    = env
        self.curr_floor = 0
        self.desired_floor  = random.randint(1,7)
        self.request_elevator   = False
        self.waiting    = True
        self.rect   = None
        self.img    = None

    def set_rect(self,rect):
        self.rect = rect

    def set_img(self,img):
        self.img = img

    def run(self):
        while True:
            if not self.waiting:
                if self.curr_floor != self.desired_floor or self.request_elevator:
                    if self.request_elevator:
                        yield self.env.timeout(1)
                    elif self.curr_floor != self.desired_floor and not self.request_elevator and not self.chosen_elevator:
                        yield self.env.timeout(1)
                    yield self.env.timeout(1)
                elif self.curr_floor == self.desired_floor:
                    yield self.env.timeout(self.wait)
                    self.desired_floor = random.randint(1,10)
            else:
                yield self.env.timeout(1)
                self.waiting = False


class Building():
    def __init__(self, elevators, riders, env):
        self.elevators  = elevators
        self.riders     = riders
        self.env        = env

    def run(self):
        while True:
            for ele in self.elevators:
               for rider in self.riders:
                  if rider.chosen_elevator == ele and ele.curr_floor == rider.curr_floor and rider.request_elevator:
                      rider.request_elevator = False
                      ele.add_rider(rider)
                      ele.remove_stop(rider.curr_floor)
                      ele.add_stop(rider.desired_floor)

               print(ele.name, "at", ele.curr_floor, "with stops", ele.stops, "and has riders",)
               for rider in ele.riders:  print(rider.name, end="-")
               print()
            for rider in self.riders:
                if not rider.request_elevator and not rider.chosen_elevator and rider.curr_floor != rider.desired_floor:
                    rider.request_elevator = True
                    best = self.elevators[0]
                    best_score = -10000
                    for elevator in self.elevators:
                        ele_score = 0
                        if ((elevator.going_up and elevator.curr_floor <= rider.curr_floor and elevator.curr_floor < rider.desired_floor) or
                        (not elevator.going_up and elevator.curr_floor >= rider.curr_floor and elevator.curr_floor > rider.desired_floor)):
                            ele_score+=200
                        if len(elevator.stops):
                            ele_score-=(len(elevator.stops)*4)
                        ele_score-=abs(elevator.curr_floor-rider.desired_floor)
                        if ele_score > best_score:
                            best = elevator
                            best_score = ele_score

                    best.add_stop(rider.curr_floor)
                    rider.chosen_elevator = best

                elif rider.chosen_elevator and rider.curr_floor == rider.desired_floor:
                    rider.chosen_elevator.riders.remove(rider)
                    rider.chosen_elevator.stops.remove(rider.curr_floor)
                    rider.chosen_elevator = None

                print("Time:",self.env.now,">",rider.name, "at", rider.curr_floor, rider.request_elevator, "wants to go to", rider.desired_floor)
                # elevator is choosen and picks up the next rider
            yield self.env.timeout(1)


RANDOM_SEED	 = 421
NUM_OF_ELEVATORS = 3 #How many elevators
NUM_OF_RIDERS	 = 50 #How many Riders

#env = simpy.RealtimeEnvironment(initial_time=0, factor=0.05, strict=False)
env = simpy.Environment()

elevators	= []
riders		= []
random.seed(RANDOM_SEED)

# Creating Elevators
for ele in range(NUM_OF_ELEVATORS):
   new_ele = Elevator("E-" + str(ele), env)
   elevators.append(new_ele)

# Creating Riders
for rid in range(NUM_OF_RIDERS):
   rider = Rider(chr(66+rid), env)
   riders.append(rider)

modelix = Building(elevators, riders, env) #

for rider in riders:
   env.process(rider.run())
for elevator in elevators:
   env.process(elevator.run())
env.process(modelix.run())

#Length of runtime
env.run(until= 500)
