"""Starter bot — a simple example to get you going.

This bot demonstrates the basics:
  - The core spawns builder bots
  - Builder bots explore the map, laying conveyors as they go
  - When a builder finds titanium ore, it builds a harvester on it

Each unit (core, builder bot, turret) gets its own Player instance.
The engine calls run() once per turn for each unit.
"""

import random

from cambc import Controller, Direction, EntityType, Environment, Position


# All 8 movement directions (no CENTRE)
DIRECTIONS = [d for d in Direction if d != Direction.CENTRE]


class Player:
    def __init__(self):
        self.target = None

    def run(self, rc: Controller):
        entity_type = rc.get_entity_type()

        if entity_type == EntityType.CORE:
            self.run_core(rc)
        elif entity_type == EntityType.BUILDER_BOT:
            self.run_builder(rc)

    # ------------------------------------------------------------------
    # Core logic: spawn builder bots when we can afford them
    # ------------------------------------------------------------------

    def run_core(self, rc: Controller):
        titanium, axionite = rc.get_global_resources()
        cost_ti, cost_ax = rc.get_builder_bot_cost()
        if titanium < cost_ti or axionite < cost_ax:
            return

        # Try each direction around the core to find a valid spawn tile
        for d in DIRECTIONS:
            pos = rc.get_position().add(d)
            if rc.can_spawn(pos):
                rc.spawn_builder(pos)
                return

    # ------------------------------------------------------------------
    # Builder bot logic: explore and build harvesters on ore
    # ------------------------------------------------------------------

    def run_builder(self, rc: Controller):
        pos = rc.get_position()

        # Look for nearby titanium ore without a harvester
        ore = self.find_nearby_ore(rc)
        if ore is not None:
            self.target = ore

        # If we're next to our target ore, build a harvester
        if self.target and pos.distance_squared(self.target) <= 2:
            if rc.get_tile_building_id(self.target) is None:
                ti, ax = rc.get_global_resources()
                cost_ti, cost_ax = rc.get_harvester_cost()
                if ti >= cost_ti and ax >= cost_ax:
                    if rc.can_build_harvester(self.target):
                        rc.build_harvester(self.target)
                        self.target = None
                        return
            # Ore already has a building, pick a new target
            self.target = None

        # Pick a random exploration target if we don't have one
        if self.target is None:
            w, h = rc.get_map_width(), rc.get_map_height()
            self.target = Position(random.randrange(w), random.randrange(h))

        # Move toward the target
        direction = pos.direction_to(self.target)
        self.try_move(rc, pos, direction)

    def find_nearby_ore(self, rc: Controller):
        """Find the closest visible titanium ore tile without a building."""
        pos = rc.get_position()
        best = None
        best_dist = None
        for tile in rc.get_nearby_tiles():
            env = rc.get_tile_env(tile)
            if env not in (Environment.ORE_TITANIUM, Environment.ORE_AXIONITE):
                continue
            if rc.get_tile_building_id(tile) is not None:
                continue
            dist = pos.distance_squared(tile)
            if best is None or dist < best_dist:
                best = tile
                best_dist = dist
        return best

    def try_move(self, rc: Controller, pos: Position, direction: Direction):
        """Try to move in the given direction, building a conveyor first if needed."""
        if direction == Direction.CENTRE:
            return

        target_tile = pos.add(direction)

        # If the tile is empty, lay a conveyor so we can walk on it
        if self.is_buildable(rc, target_tile):
            ti, ax = rc.get_global_resources()
            cost_ti, cost_ax = rc.get_conveyor_cost()
            if ti >= cost_ti and ax >= cost_ax:
                if rc.can_build_conveyor(target_tile, direction.opposite()):
                    rc.build_conveyor(target_tile, direction.opposite())

        # Now try to move
        if rc.can_move(direction):
            rc.move(direction)
            return

        # If blocked, try rotating left and right
        for rotated in [direction.rotate_left(), direction.rotate_right()]:
            adj = pos.add(rotated)
            if self.is_buildable(rc, adj):
                ti, ax = rc.get_global_resources()
                cost_ti, cost_ax = rc.get_conveyor_cost()
                if ti >= cost_ti and ax >= cost_ax:
                    if rc.can_build_conveyor(adj, rotated.opposite()):
                        rc.build_conveyor(adj, rotated.opposite())
            if rc.can_move(rotated):
                rc.move(rotated)
                return

    def is_buildable(self, rc: Controller, pos: Position):
        """Check if a tile is empty and buildable (not a wall, no existing building)."""
        try:
            env = rc.get_tile_env(pos)
        except Exception:
            return False
        if env == Environment.WALL:
            return False
        return rc.get_tile_building_id(pos) is None and rc.get_tile_builder_bot_id(pos) is None
