# 10 am rec code

# Travel ✈
# Modeling tourists in a city

# Problem Setup
# You're the mayor of MITropolis and you want to know what the tourism scene is like in your city. 
# You want to model your City (modeled as a grid), with Landmarks (like the Dome, the Alchemist, etc.) 
# as well as different types of people, like locals and tourists. How can you do this? 
# Well good thing you're taking 6.1000 and learned about Classes!

import matplotlib.pyplot as plt
import matplotlib.animation as animation
import random

# Part 1 - Landmarks
# Let's make a class to represent different landmarks
# TODO - Implement Landmark Class (include get_location function)
class Landmark:
    def __init__(self, name, loc):
        ... # TODO

    def get_name(self):
        ... # TODO

    def get_location(self):
        ... # TODO

    def set_name(self, name):
        ... # TODO

    def set_location(self, loc):
        ... # TODO

# Part 2 - City
# Let's make a class to represent a city
class City:
    # City has a set of landmarks and people
    # Defined on a 2D rectangular grid of size width x height
    def __init__(self, width, height, landmarks=None, people=None):
        # initialize landmarks, people, and dimensions (width and height)
        ... # TODO

    def add_landmark(self, landmark):
        # add a landmark to the city, make sure to check if it's a Landmark type
        ... # TODO

    def add_person(self, person):
        # add a person to the city, ensure we are adding a Person or its subclass instance
        ... # TODO

    def get_random_location(self):
        # return random location as a tuple (x, y)
        ... # TODO

    def get_landmarks(self):
        #  return landmarks
        ... # TODO

    def get_people(self):
        # return people
        ... # TODO

    def get_distance(self, pos1, pos2):
        # returns manhattan distance between pos1 and pos2
        x1, y1 = pos1
        x2, y2 = pos2

        dx = abs(x2 - x1)
        dy = abs(y2 - y1)

        return dx + dy
  
# Part 3 - Person
# Now we will implement classes for different kinds of people
# people can only move up, down, left, right (no diagonals)
# we're given base Person class
class Person:
    def __init__(self, env: City):
        self.env = env
        self.loc = env.get_random_location()
        self.directions = [(0,1), (0,-1), (1,0), (-1,0)] # Right, Left, Down, Up

    def set_random_location(self):
        self.loc = self.env.get_random_location()

    def get_locaction(self):
        return self.loc

    def get_x(self):
        return self.loc[0]

    def get_y(self):
        return self.loc[1]

# Implement a local
# They should inherit from the Person class
class LocalPerson(Person):
    def __init__(self, env):
        ... # TODO

    # moves randomly
    def move(self):
        ... # TODO

        # we need to get our starting location

        # pick a random direction to move in

        # update our new location
        self.loc = (new_x, new_y)

        return self.loc # Return the new location


# Implement a tourist
# They should inherit from the Person class
class TouristPerson(Person):
    def __init__(self, env):
        ... # TODO
        # tourists should keep track of landmarks they've visited!

    #moves towards closets unvistied landmark, if no unvisited landmarks move randomly
    def move(self):
        # Find unvisited landmarks
        ... # TODO


        # If all landmarks visited, move randomly
        if not unvisited_landmarks:
            ... # TODO

            self.loc = (new_x, new_y)
            return self.loc

        # Find closest unvisited landmark
        ... # TODO


        # Move towards the closest unvisited landmark
        ... # TODO

        self.loc = (new_x, new_y) # Update person's location

        # Check if the tourist has reached the landmark
        if self.loc == closest_landmark.get_location():
            self.visited_landmarks.add(closest_landmark)

        return self.loc # Return the new location
    
# Part 4 - Visualize!
# Let's see what our city looks like!
def visualize_city(city, steps=50, interval=300):
    """
    Animate people (dots) moving in a city with landmarks (stars).
    - city: your City object
    - steps: number of frames to run
    - interval: milliseconds per frame
    """

    fig, ax = plt.subplots(figsize=(6, 6))
    ax.set_xlim(-0.5, city.width + 0.5)
    ax.set_ylim(-0.5, city.height + 0.5)
    ax.grid(True, linestyle='--', alpha=0.5)

    # Landmarks
    landmark_scatter = ax.scatter(
        [lm.get_location()[0] for lm in city.get_landmarks()],
        [lm.get_location()[1] for lm in city.get_landmarks()],
        marker='*', s=200, color='gold', edgecolor='black', label='Landmarks'
    )

    # People
    locals_scatter = ax.scatter([], [], color='green', label='Locals', s=60)
    tourists_scatter = ax.scatter([], [], color='red', label='Tourists', s=60)
    ax.legend()

    def update(frame):
        for p in city.get_people():
            p.move()

        locals_x = [p.get_x() for p in city.get_people() if isinstance(p, LocalPerson)]
        locals_y = [p.get_y() for p in city.get_people() if isinstance(p, LocalPerson)]
        tourists_x = [p.get_x() for p in city.get_people() if isinstance(p, TouristPerson)]
        tourists_y = [p.get_y() for p in city.get_people() if isinstance(p, TouristPerson)]

        locals_scatter.set_offsets(list(zip(locals_x, locals_y)))
        tourists_scatter.set_offsets(list(zip(tourists_x, tourists_y)))

        ax.set_title(f"City Simulation — Step {frame + 1}/{steps}")
        return locals_scatter, tourists_scatter, landmark_scatter

    ani = animation.FuncAnimation(fig, update, frames=steps, interval=interval, blit=False, repeat=False)
    plt.show()
    return ani

if __name__ == "__main__":
    city = City(width=20, height=20)

    # Add landmarks
    for i in range(5):
        lm = Landmark(name=f"L{i}", loc=(random.randint(0, 19), random.randint(0, 19)))
        city.add_landmark(lm) # Use add_landmark method

    # Add people
    for i in range(10):
        if i < 5:
            p = LocalPerson(city)
        else:
            p = TouristPerson(city)
        city.add_person(p) # Use add_person method

    # Animate!
    ani = visualize_city(city, steps=60, interval=300)
