use rand::prelude::{StdRng, SliceRandom};
use rand::{SeedableRng, Rng, thread_rng};
use std::fmt;
use std::fmt::Formatter;
use std::collections::{HashMap, HashSet};
use city::{HouseLayout, City, House};

mod optimization;
mod population;
mod city;

fn main() {
    let city = City::read_from_file("01.in");
    let mut best_price: Option<u32> = None;

    loop {
        let seed: u64 = thread_rng().gen();
        eprintln!("Starting seed {}", seed);
        let mut rng = StdRng::seed_from_u64(seed);
        let mut layout = HouseLayout::new(&city);
        eprintln!("Starting random population...");
        population::populate_random(&mut layout, &mut rng);
        eprintln!("Finished random init, price: {}", layout.price());
        loop {
            let mut improved = false;
            eprintln!("Starting moving individual houses...");
            if optimization::improve_move_individual_houses(&mut layout, &mut rng) {
                dump_layout(&layout, &mut best_price, seed);
                improved = true;
            }
            eprintln!("Finished moving individual houses...");
            eprintln!("Starting pairwise house merge...");
            if optimization::improve_merge_pairwise(&mut layout) {
                dump_layout(&layout, &mut best_price, seed);
                improved = true;
            }
            eprintln!("Finished pairwise house merge");
            //eprintln!("Starting pairwise house move...");
            //if optimization::improve_move_houses_pairwise(&mut layout) {
            //    dump_layout(&layout, &mut best_price, seed);
            //    improved = true;
            //}
            //eprintln!("Finished pairwise house move");
            if !improved {
                break;
            }
        }
    }
}

fn print_houses(houses: &Vec<House>) {
    println!("{}", houses.len());
    for house in houses {
        println!("{} {}", house.y, house.x);
    }
}

fn dump_layout(layout: &HouseLayout, best_price: &mut Option<u32>, seed: u64) {
    let price = layout.price();
    if best_price.is_none() || price < best_price.unwrap() {
        *best_price = Some(price);
        eprintln!("Printing {} - new best", price);
        println!("New best!");
        println!("Price {}, seed {}", price, seed);
        print_houses(&layout.houses());
        println!();
    } else {
        eprintln!("Printing {}", price);
        println!("Price {}, seed {}", price, seed);
        print_houses(&layout.houses());
        println!();
    }
}