use crate::city::House;
use rusqlite::{Connection, NO_PARAMS, params, Result, Transaction};
use std::collections::HashMap;

pub struct LayoutDB {
    connection: Connection,
    layouts: Vec<SavedLayout>
}

pub struct SavedLayout {
    id: usize,
    houses: Vec<House>
}

impl SavedLayout {
    pub fn houses(&self) -> &Vec<House> {
        &self.houses
    }

    pub fn id(&self) -> usize {
        self.id
    }
}

impl LayoutDB {
    pub fn from_file(filename: &str) -> Result<Self> {
        let connection = Connection::open(filename)?;
        let mut layouts: HashMap<u32, Vec<(u32, u32)>> = HashMap::new();

        // We need to dispose the statement so we can move the connection later
        {
            let mut stmt = connection.prepare("SELECT layout_id, x, y FROM houses")?;
            let mut rows = stmt.query(NO_PARAMS)?;

            while let Some(row) = rows.next()? {
                let id: u32 = row.get(0)?;
                let x: u32 = row.get(1)?;
                let y: u32 = row.get(2)?;
                layouts.entry(id).or_default().push((x, y));
            }
        }

        let layouts = layouts.into_iter().map(|(id, xy_pairs)|
            SavedLayout
            {
                id: id as usize,
                houses: xy_pairs.into_iter().map(|(x, y)| House { x: x as usize, y: y as usize }).collect(),
            }
        ).collect();

        Ok(LayoutDB { connection, layouts })
    }

    pub fn layouts(&self) -> &Vec<SavedLayout> {
        &self.layouts
    }

    pub fn add_layout(&mut self, houses: &Vec<House>, fully_optimized: bool) -> Result<()> {
        let transaction = self.connection.transaction()?;
        transaction.execute("INSERT INTO layouts (is_fully_optimized) VALUES (?1)",
                            params![fully_optimized])?;
        let layout_id = transaction.last_insert_rowid();
        for house in houses {
            transaction.execute("INSERT INTO houses (layout_id, x, y) VALUES (?1, ?2, ?3)",
                                params![layout_id, house.x as u32, house.y as u32])?;
        }
        transaction.commit()?;
        self.layouts.push(SavedLayout {id: layout_id as usize, houses: houses.clone()});
        Ok(())
    }
}