use crate::city::{Rectangle, HOUSE_RANGE, House, HouseLayout, City};

pub struct Subcity {
    city: City,
    bought_houses: Vec<House>,
    x_offset: usize,
    y_offset: usize
}

impl Subcity {
    pub fn city(&self) -> &City {
        &self.city
    }
    pub fn bought_houses(&self) -> &Vec<House> {
        &self.bought_houses
    }
    pub fn x_offset(&self) -> usize {
        self.x_offset
    }
    pub fn y_offset(&self) -> usize {
        self.y_offset
    }
    pub fn to_full_houses(&self, subcity_houses: &Vec<House>) -> Vec<House> {
        let mut full_houses: Vec<House> = Vec::new();
        for house in subcity_houses {
            full_houses.push(House::new(house.x + self.x_offset, house.y + self.y_offset))
        }
        for house in &self.bought_houses {
            full_houses.push(*house)
        }

        full_houses
    }
}

/// Creates a new city that is a subset of the original city.
/// The provided houses and houses they cover are removed. If there is an empty margin
/// around uncovered houses, it is removed - this may result in a smaller city.
pub fn build_subcity(city: &City, bought_houses: &[House]) -> Subcity {
    let mut covered = vec![false; city.width() * city.height()];

    for house in bought_houses {
        assert!(city.is_house(*house));

        let range_rect = house.range_rectangle(city);
        for y in range_rect.top..=range_rect.bottom {
            for x in range_rect.left..=range_rect.right {
                covered[y as usize * city.width() + x as usize] = true;
            }
        }
    }

    // Inclusive bounds for uncovered houses in the new subcity
    let mut min_x = None;
    let mut min_y = None;
    let mut max_x = None;
    let mut max_y = None;

    for y in 0..city.height() {
        for x in 0..city.width() {
            if !covered[y * city.width() + x] && city.is_house_xy(x, y) {
                min_x = Some(min_x.unwrap_or(usize::MAX).min(x));
                min_y = Some(min_y.unwrap_or(usize::MAX).min(y));
                max_x = Some(max_x.unwrap_or(usize::MIN).max(x));
                max_y = Some(max_x.unwrap_or(usize::MIN).max(y));
            }
        }
    }

    if min_x.is_none() {
        assert!(min_y.is_none());
        assert!(max_x.is_none());
        assert!(max_y.is_none());
        return Subcity {
            city: City::new(Vec::new(), 0, 0),
            bought_houses: bought_houses.iter().map(|&h| h).collect(),
            x_offset: 0,
            y_offset: 0,
        };
    }

    let min_x = min_x.unwrap();
    let min_y = min_y.unwrap();
    let max_x = max_x.unwrap();
    let max_y = max_y.unwrap();

    let width = max_x - min_x + 1;
    let height = max_y - min_y + 1;

    let mut prices = vec![0; height * width];
    for y in 0..height {
        for x in 0..width {
            let original_x = min_x + x;
            let original_y = min_y + y;

            // Copy prices for uncovered tiles, the covered tiles default to 0 - non-houses.
            if !covered[original_y * city.width() + original_x] {
                prices[y * width + x] = city.get_price_xy(original_x, original_y)
            }
        }
    }

    Subcity {
        city: City::new(prices, width, height),
        bought_houses: bought_houses.iter().map(|&h| h).collect(),
        x_offset: min_x,
        y_offset: min_y
    }
}