import pygame
import sys
import random
import math

pygame.init()
WIDTH, HEIGHT = 900, 700
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Amusement Park Simulator – Enhanced Visuals")
clock = pygame.time.Clock()
FPS = 60

# ==================== COLORS ====================
WHITE       = (255,255,255)
BLACK       = (0,0,0)
GRASS1      = (120,180,80)
GRASS2      = (140,200,90)
BROWN       = (120,80,50)
DARK_BROWN  = (80,50,30)
YELLOW      = (255,215,0)
RED         = (220,50,50)
BLUE        = (70,130,200)
ORANGE      = (255,140,0)
PURPLE      = (160,50,160)
CYAN        = (0,180,200)
PINK        = (255,120,180)
TEAL        = (0,130,130)
GOLD        = (255,200,50)
SILVER      = (180,180,180)
DARK_GREEN  = (30,80,30)
SKIN        = (255,220,180)
GRAY        = (128,128,128)
GREEN       = (0,255,0)

# ==================== LAYOUT ====================
PANEL_TOP    = 60
PANEL_BOTTOM = 130
MAP_WIDTH    = WIDTH
MAP_HEIGHT   = HEIGHT - PANEL_TOP - PANEL_BOTTOM
MAP_OFFSET_Y = PANEL_TOP

# ==================== FACILITY TYPES ====================
FACILITIES = [
    {'name':'Carousel',     'cost':500,  'income':50,  'color':PURPLE, 'size':60, 'cap':4},
    {'name':'Ferris Wheel', 'cost':1000, 'income':120, 'color':BLUE,   'size':80, 'cap':6},
    {'name':'Roller Coaster','cost':2000,'income':200, 'color':RED,    'size':100,'cap':8},
    {'name':'Bumper Cars',  'cost':800,  'income':80,  'color':ORANGE, 'size':70, 'cap':5},
    {'name':'Haunted House','cost':1200, 'income':100, 'color':SILVER, 'size':65, 'cap':3},
    {'name':'Pirate Ship',  'cost':1500, 'income':150, 'color':BROWN,  'size':85, 'cap':6},
    {'name':'Drop Tower',   'cost':1800, 'income':180, 'color':TEAL,   'size':55, 'cap':4},
    {'name':'Teacups',      'cost':600,  'income':60,  'color':PINK,   'size':50, 'cap':4},
    {'name':'Water Flume',  'cost':1600, 'income':160, 'color':CYAN,   'size':90, 'cap':5},
    {'name':'Mini Golf',    'cost':700,  'income':70,  'color':GREEN,  'size':75, 'cap':4},
]

# ==================== FONTS ====================
font_small  = pygame.font.Font(None, 24)
font_medium = pygame.font.Font(None, 30)

# ==================== HELPER: rounded rectangle ====================
def draw_rounded_rect(surface, color, rect, radius=10):
    x, y, w, h = rect
    pygame.draw.circle(surface, color, (x+radius, y+radius), radius)
    pygame.draw.circle(surface, color, (x+w-radius, y+radius), radius)
    pygame.draw.circle(surface, color, (x+radius, y+h-radius), radius)
    pygame.draw.circle(surface, color, (x+w-radius, y+h-radius), radius)
    pygame.draw.rect(surface, color, (x+radius, y, w-2*radius, h))
    pygame.draw.rect(surface, color, (x, y+radius, w, h-2*radius))

# ==================== FACILITY CLASS ====================
class Facility:
    def __init__(self, x, y, info):
        self.x = x
        self.y = y
        self.name   = info['name']
        self.cost   = info['cost']
        self.income = info['income']
        self.color  = info['color']
        self.size   = info['size']
        self.cap    = info['cap']
        self.visitors = 0
        self.timer    = 0
        self.anim_angle = 0

    def rect(self):
        return pygame.Rect(self.x - self.size//2, self.y - self.size//2,
                           self.size, self.size)

    def update(self):
        income = 0
        if self.visitors > 0:
            self.timer += 1
            if self.timer >= 180:
                self.timer = 0
                self.visitors -= 1
                income = self.income
        self.anim_angle = (self.anim_angle + 2) % 360
        return income

    def draw(self, surface):
        cx, cy = self.x, self.y
        s = self.size
        plate_rect = (cx - s//2, cy - s//2, s, s)
        pygame.draw.rect(surface, (60,60,60), plate_rect, border_radius=8)
        pygame.draw.rect(surface, self.color, plate_rect, border_radius=8)

        if self.name == 'Carousel':
            pygame.draw.ellipse(surface, RED, (cx-s//2+5, cy-s//2+5, s-10, s//3))
            for i in range(4):
                px = cx + (s//2-10)*math.cos(i*math.pi/2 + math.radians(self.anim_angle))
                py = cy + (s//2-10)*math.sin(i*math.pi/2 + math.radians(self.anim_angle))
                pygame.draw.line(surface, GOLD, (cx, cy+s//4), (px, py+s//4), 4)
            pygame.draw.line(surface, GOLD, (cx, cy+s//2), (cx, cy+s//4), 6)
            pygame.draw.ellipse(surface, GOLD, (cx-s//3, cy+s//2-8, 2*s//3, 16))

        elif self.name == 'Ferris Wheel':
            wheel_radius = s//2 - 10
            pygame.draw.circle(surface, SILVER, (cx, cy), wheel_radius, 4)
            for i in range(8):
                angle = math.radians(i*45 + self.anim_angle)
                ex = cx + wheel_radius*math.cos(angle)
                ey = cy + wheel_radius*math.sin(angle)
                pygame.draw.line(surface, SILVER, (cx, cy), (ex, ey), 2)
                pygame.draw.circle(surface, RED, (int(ex), int(ey)), 6)
            pygame.draw.line(surface, GRAY, (cx, cy), (cx, cy+wheel_radius), 6)

        elif self.name == 'Roller Coaster':
            pts = [(cx-s//2, cy), (cx-s//4, cy-s//4), (cx, cy-s//4),
                   (cx+s//4, cy), (cx+s//2, cy+s//3)]
            pygame.draw.lines(surface, WHITE, False, pts, 4)
            progress = (self.timer % 180) / 180.0
            idx = int(progress * (len(pts)-1))
            car_x = pts[idx][0] + (pts[(idx+1)%len(pts)][0]-pts[idx][0])*(progress*(len(pts)-1)-idx)
            car_y = pts[idx][1] + (pts[(idx+1)%len(pts)][1]-pts[idx][1])*(progress*(len(pts)-1)-idx)
            pygame.draw.rect(surface, YELLOW, (car_x-6, car_y-4, 12, 8))

        elif self.name == 'Bumper Cars':
            pygame.draw.ellipse(surface, GRAY, (cx-s//2, cy-s//3, s, 2*s//3), 3)
            for i in range(3):
                angle = math.radians(self.anim_angle + i*120)
                bx = cx + (s//3)*math.cos(angle)
                by = cy + (s//4)*math.sin(angle)
                pygame.draw.circle(surface, RED, (int(bx), int(by)), 8)
                pygame.draw.circle(surface, BLACK, (int(bx), int(by)), 8, 1)

        elif self.name == 'Haunted House':
            house_rect = (cx-s//2+5, cy-s//2+15, s-10, s-30)
            pygame.draw.rect(surface, DARK_GREEN, house_rect)
            roof_pts = [(cx-s//2, cy-s//2+15), (cx, cy-s//2-5), (cx+s//2, cy-s//2+15)]
            pygame.draw.polygon(surface, PURPLE, roof_pts)
            pygame.draw.rect(surface, BLACK, (cx-8, cy+s//2-25, 16, 20))
            for dx in [-15, 15]:
                pygame.draw.rect(surface, YELLOW, (cx+dx-5, cy-10, 10, 12))

        elif self.name == 'Pirate Ship':
            ship_rect = (cx-s//2+5, cy-s//4, s-10, s//2)
            pygame.draw.rect(surface, BROWN, ship_rect, border_radius=5)
            pygame.draw.line(surface, BLACK, (cx, cy-s//4), (cx, cy-s//2), 4)
            pygame.draw.polygon(surface, WHITE, [(cx, cy-s//2), (cx-15, cy-s//2+20), (cx+15, cy-s//2+20)])

        elif self.name == 'Drop Tower':
            pygame.draw.rect(surface, SILVER, (cx-6, cy-s//2+5, 12, s-20))
            drop = self.anim_angle % 180
            if drop < 30: drop = 0
            elif drop < 60: drop = drop-30
            else: drop = 30
            seat_y = cy - s//2 + 5 + drop
            pygame.draw.rect(surface, RED, (cx-10, seat_y, 20, 10))

        elif self.name == 'Teacups':
            for i, offset in enumerate([(-10,5), (10,5), (0,-10)]):
                cup_x = cx + offset[0] + 3*math.cos(math.radians(self.anim_angle+i*120))
                cup_y = cy + offset[1] + 3*math.sin(math.radians(self.anim_angle+i*120))
                pygame.draw.circle(surface, PINK, (int(cup_x), int(cup_y)), 10)
                pygame.draw.circle(surface, WHITE, (int(cup_x), int(cup_y)), 10, 2)

        elif self.name == 'Water Flume':
            wave_pts = [(cx-s//2, cy), (cx-s//4, cy-s//4), (cx, cy),
                        (cx+s//4, cy-s//4), (cx+s//2, cy)]
            pygame.draw.lines(surface, CYAN, False, wave_pts, 6)
            boat_x = cx - s//2 + (self.timer%180)/180 * s
            pygame.draw.ellipse(surface, RED, (boat_x-8, cy-6, 16, 8))

        elif self.name == 'Mini Golf':
            pygame.draw.ellipse(surface, DARK_GREEN, (cx-s//2, cy-s//3, s, 2*s//3))
            flag_x = cx + s//4
            flag_y = cy - s//4
            pygame.draw.line(surface, WHITE, (flag_x, flag_y), (flag_x, flag_y+20), 2)
            pygame.draw.polygon(surface, RED, [(flag_x, flag_y), (flag_x+10, flag_y+8), (flag_x, flag_y+16)])

        cap_text = font_small.render(f"{self.visitors}/{self.cap}", True, WHITE)
        surface.blit(cap_text, (cx - cap_text.get_width()//2, cy + s//2 - 18))

# ==================== VISITOR CLASS ====================
class Visitor:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.speed = 1.5
        self.state = 'idle'
        self.angle = random.uniform(0, 2*math.pi)
        self.wander_timer = random.randint(60, 180)

    def update(self, facilities):
        if self.state == 'riding':
            return
        for fac in facilities:
            if fac.visitors < fac.cap:
                dist = math.hypot(self.x - fac.x, self.y - fac.y)
                if dist < 60:
                    self.state = 'riding'
                    fac.visitors += 1
                    self.x, self.y = fac.x, fac.y
                    return
        self.wander_timer -= 1
        if self.wander_timer <= 0:
            self.angle += random.uniform(-math.pi/2, math.pi/2)
            self.wander_timer = random.randint(60, 180)
        self.x += math.cos(self.angle) * self.speed
        self.y += math.sin(self.angle) * self.speed
        self.x = max(20, min(MAP_WIDTH-20, self.x))
        self.y = max(20, min(MAP_HEIGHT-20, self.y))

    def draw(self, surface):
        sx = int(self.x)
        sy = int(self.y + MAP_OFFSET_Y)
        body_color = (255,220,80) if self.state == 'riding' else (80,150,255)
        head_color = SKIN
        pygame.draw.line(surface, body_color, (sx-3, sy+6), (sx-3, sy+10), 3)
        pygame.draw.line(surface, body_color, (sx+3, sy+6), (sx+3, sy+10), 3)
        pygame.draw.ellipse(surface, body_color, (sx-5, sy-2, 10, 8))
        pygame.draw.circle(surface, head_color, (sx, sy-6), 5)
        pygame.draw.circle(surface, BLACK, (sx-2, sy-7), 1)
        pygame.draw.circle(surface, BLACK, (sx+2, sy-7), 1)

# ==================== GAME CLASS ====================
class Game:
    def __init__(self):
        self.money = 5000
        self.facilities = []
        self.visitors = [Visitor(random.randint(50, MAP_WIDTH-50),
                                 random.randint(50, MAP_HEIGHT-50))
                         for _ in range(50)]
        self.selected = None
        self.message = ""
        self.msg_timer = 0
        self.spawn_timer = 0

    def handle_events(self):
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                pygame.quit(); sys.exit()
            if event.type == pygame.MOUSEBUTTONDOWN:
                mx, my = event.pos
                if my > HEIGHT - PANEL_BOTTOM:
                    bw, bh = 110, 45
                    cols = 5
                    sx, sy = 30, HEIGHT - PANEL_BOTTOM + 15
                    for idx, fac in enumerate(FACILITIES):
                        row, col = idx//cols, idx%cols
                        rect = pygame.Rect(sx+col*(bw+15), sy+row*(bh+10), bw, bh)
                        if rect.collidepoint(mx, my):
                            if self.money >= fac['cost']:
                                self.selected = idx
                                self.message = f"Selected {fac['name']} (${fac['cost']})"
                            else:
                                self.message = "Not enough money!"
                            self.msg_timer = 180
                            break
                elif my > MAP_OFFSET_Y and self.selected is not None:
                    map_x, map_y = mx, my - MAP_OFFSET_Y
                    info = FACILITIES[self.selected]
                    new_rect = pygame.Rect(map_x-info['size']//2, map_y-info['size']//2,
                                           info['size'], info['size'])
                    overlap = any(fac.rect().colliderect(new_rect) for fac in self.facilities)
                    if not overlap:
                        if self.money >= info['cost']:
                            self.facilities.append(Facility(map_x, map_y, info))
                            self.money -= info['cost']
                            self.message = f"Built {info['name']}"
                            self.selected = None
                        else:
                            self.message = "Not enough money!"
                        self.msg_timer = 180
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_ESCAPE:
                    self.selected = None
                    self.message = "Selection canceled"
                    self.msg_timer = 60

    def update(self):
        # Facility income
        for fac in self.facilities:
            self.money += fac.update()

        # Visitor behavior
        for vis in self.visitors:
            vis.update(self.facilities)

        # ★ SPAWN CONTROL: max 70 visitors, one every 20 seconds
        self.spawn_timer += 1
        if self.spawn_timer >= 1200 and len(self.visitors) < 70:
            self.spawn_timer = 0
            self.visitors.append(Visitor(random.randint(50, MAP_WIDTH-50),
                                         random.randint(50, MAP_HEIGHT-50)))

        # Message timer
        if self.msg_timer > 0:
            self.msg_timer -= 1
        else:
            self.message = ""

    def draw_background(self):
        for y in range(0, MAP_HEIGHT, 20):
            for x in range(0, MAP_WIDTH, 20):
                color = GRASS1 if (x//20 + y//20) % 2 == 0 else GRASS2
                pygame.draw.rect(screen, color, (x, y+MAP_OFFSET_Y, 20, 20))
        random.seed(42)
        for _ in range(200):
            fx = random.randint(5, MAP_WIDTH-5)
            fy = random.randint(5, MAP_HEIGHT-5)
            pygame.draw.circle(screen, YELLOW, (fx, fy+MAP_OFFSET_Y), 2)
            pygame.draw.circle(screen, PINK, (fx+2, fy+MAP_OFFSET_Y+1), 1)
        for x in range(0, MAP_WIDTH, 120):
            pygame.draw.line(screen, (180,160,120), (x, MAP_OFFSET_Y), (x, MAP_OFFSET_Y+MAP_HEIGHT), 1)

    def draw_ui_top(self):
        pygame.draw.rect(screen, (30,30,30), (0,0,WIDTH,PANEL_TOP))
        money_text = font_medium.render(f"💰 ${self.money}", True, YELLOW)
        screen.blit(money_text, (10,15))
        vis_text = font_medium.render(f"👥 {len(self.visitors)}", True, WHITE)
        screen.blit(vis_text, (250,15))
        if self.message:
            msg = font_small.render(self.message, True, CYAN)
            screen.blit(msg, (500,20))

    def draw_ui_bottom(self):
        pygame.draw.rect(screen, (30,30,30), (0, HEIGHT-PANEL_BOTTOM, WIDTH, PANEL_BOTTOM))
        bw, bh = 110, 45
        cols = 5
        start_x, start_y = 30, HEIGHT-PANEL_BOTTOM+15
        for idx, fac in enumerate(FACILITIES):
            row, col = idx//cols, idx%cols
            x = start_x + col*(bw+15)
            y = start_y + row*(bh+10)
            rect = pygame.Rect(x, y, bw, bh)
            if idx == self.selected:
                col_btn = WHITE
            elif self.money >= fac['cost']:
                col_btn = (200,200,200)
            else:
                col_btn = (120,120,120)
            draw_rounded_rect(screen, col_btn, (x,y,bw,bh), radius=8)
            name = font_small.render(fac['name'], True, BLACK)
            cost = font_small.render(f"${fac['cost']}", True, RED if self.money < fac['cost'] else DARK_GREEN)
            screen.blit(name, (x+5, y+5))
            screen.blit(cost, (x+5, y+25))

    def draw(self):
        screen.fill(BLACK)
        self.draw_background()
        for fac in self.facilities:
            fac.draw(screen)
        for vis in self.visitors:
            vis.draw(screen)
        self.draw_ui_top()
        self.draw_ui_bottom()
        pygame.display.flip()

    def run(self):
        while True:
            dt = clock.tick(FPS)
            self.handle_events()
            self.update()
            self.draw()

if __name__ == "__main__":
    game = Game()
    game.run()