extern crate sdl2;

use sdl2::event::Event;
use sdl2::image::{InitFlag, LoadTexture};
use sdl2::keyboard::Keycode;
use sdl2::pixels::Color;
use sdl2::render::Texture;
use std::collections::HashMap;
use std::time::Duration;

pub fn main() -> Result<(), String> {
    let sdl_context = sdl2::init()?;
    let video_subsystem = sdl_context.video()?;
    let _image = sdl2::image::init(InitFlag::PNG);

    let window = video_subsystem
        .window("rust-sdl2 demo: Video", 1024, 600)
        .position_centered()
        .opengl()
        .fullscreen()
        .build()
        .map_err(|e| e.to_string())?;

    let mut canvas = window.into_canvas().build().map_err(|e| e.to_string())?;

    let texture_creator = canvas.texture_creator();

    let mut textures: HashMap<&str, Texture> = HashMap::default();
    textures.insert(
        "a",
        texture_creator
            .load_texture_bytes(include_bytes!("../assets/a.png"))
            .unwrap(),
    );
    textures.insert(
        "b",
        texture_creator
            .load_texture_bytes(include_bytes!("../assets/b.png"))
            .unwrap(),
    );
    textures.insert(
        "c",
        texture_creator
            .load_texture_bytes(include_bytes!("../assets/c.png"))
            .unwrap(),
    );
    textures.insert(
        "d",
        texture_creator
            .load_texture_bytes(include_bytes!("../assets/d.png"))
            .unwrap(),
    );
    textures.insert(
        "e",
        texture_creator
            .load_texture_bytes(include_bytes!("../assets/e.png"))
            .unwrap(),
    );
    textures.insert(
        "f",
        texture_creator
            .load_texture_bytes(include_bytes!("../assets/f.png"))
            .unwrap(),
    );
    textures.insert(
        "g",
        texture_creator
            .load_texture_bytes(include_bytes!("../assets/g.png"))
            .unwrap(),
    );
    textures.insert(
        "h",
        texture_creator
            .load_texture_bytes(include_bytes!("../assets/h.png"))
            .unwrap(),
    );
    textures.insert(
        "i",
        texture_creator
            .load_texture_bytes(include_bytes!("../assets/i.png"))
            .unwrap(),
    );
    textures.insert(
        "j",
        texture_creator
            .load_texture_bytes(include_bytes!("../assets/j.png"))
            .unwrap(),
    );
    textures.insert(
        "k",
        texture_creator
            .load_texture_bytes(include_bytes!("../assets/k.png"))
            .unwrap(),
    );
    textures.insert(
        "l",
        texture_creator
            .load_texture_bytes(include_bytes!("../assets/l.png"))
            .unwrap(),
    );
    textures.insert(
        "m",
        texture_creator
            .load_texture_bytes(include_bytes!("../assets/m.png"))
            .unwrap(),
    );
    textures.insert(
        "n",
        texture_creator
            .load_texture_bytes(include_bytes!("../assets/n.png"))
            .unwrap(),
    );
    textures.insert(
        "o",
        texture_creator
            .load_texture_bytes(include_bytes!("../assets/o.png"))
            .unwrap(),
    );
    textures.insert(
        "p",
        texture_creator
            .load_texture_bytes(include_bytes!("../assets/p.png"))
            .unwrap(),
    );
    textures.insert(
        "q",
        texture_creator
            .load_texture_bytes(include_bytes!("../assets/q.png"))
            .unwrap(),
    );
    textures.insert(
        "r",
        texture_creator
            .load_texture_bytes(include_bytes!("../assets/r.png"))
            .unwrap(),
    );
    textures.insert(
        "s",
        texture_creator
            .load_texture_bytes(include_bytes!("../assets/s.png"))
            .unwrap(),
    );
    textures.insert(
        "t",
        texture_creator
            .load_texture_bytes(include_bytes!("../assets/t.png"))
            .unwrap(),
    );
    textures.insert(
        "u",
        texture_creator
            .load_texture_bytes(include_bytes!("../assets/u.png"))
            .unwrap(),
    );
    textures.insert(
        "v",
        texture_creator
            .load_texture_bytes(include_bytes!("../assets/v.png"))
            .unwrap(),
    );
    textures.insert(
        "w",
        texture_creator
            .load_texture_bytes(include_bytes!("../assets/w.png"))
            .unwrap(),
    );
    textures.insert(
        "x",
        texture_creator
            .load_texture_bytes(include_bytes!("../assets/x.png"))
            .unwrap(),
    );
    textures.insert(
        "y",
        texture_creator
            .load_texture_bytes(include_bytes!("../assets/y.png"))
            .unwrap(),
    );
    textures.insert(
        "z",
        texture_creator
            .load_texture_bytes(include_bytes!("../assets/z.png"))
            .unwrap(),
    );

    // Special words
    textures.insert(
        "cat",
        texture_creator
            .load_texture_bytes(include_bytes!("../assets/cat.png"))
            .unwrap(),
    );
    textures.insert(
        "dog",
        texture_creator
            .load_texture_bytes(include_bytes!("../assets/dog.png"))
            .unwrap(),
    );
    textures.insert(
        "heart",
        texture_creator
            .load_texture_bytes(include_bytes!("../assets/heart.png"))
            .unwrap(),
    );

    let opts: Vec<&str> = textures.keys().map(|s| *s).collect();

    canvas.set_draw_color(Color::RGB(255, 0, 0));
    canvas.clear();
    canvas.present();
    let mut event_pump = sdl_context.event_pump()?;

    let mut line = String::new();

    'running: loop {
        for event in event_pump.poll_iter() {
            match event {
                Event::KeyDown {
                    keycode: Some(kc), ..
                } => {
                    line.extend(
                        match kc {
                            Keycode::A => "a",
                            Keycode::B => "b",
                            Keycode::C => "c",
                            Keycode::D => "d",
                            Keycode::E => "e",
                            Keycode::F => "f",
                            Keycode::G => "g",
                            Keycode::H => "h",
                            Keycode::I => "i",
                            Keycode::J => "j",
                            Keycode::K => "k",
                            Keycode::L => "l",
                            Keycode::M => "m",
                            Keycode::N => "n",
                            Keycode::O => "o",
                            Keycode::P => "p",
                            Keycode::Q => "q",
                            Keycode::R => "r",
                            Keycode::S => "s",
                            Keycode::T => "t",
                            Keycode::U => "u",
                            Keycode::V => "v",
                            Keycode::W => "w",
                            Keycode::X => "x",
                            Keycode::Y => "y",
                            Keycode::Z => "z",

                            Keycode::Escape => break 'running,
                            _ => "",
                        }
                        .chars(),
                    );
                }
                Event::Quit { .. } => break 'running,
                _ => {}
            }
        }

        // Find the most-exact match to show
        let mut matches = vec![];
        let mut chars = line.chars();
        for i in 0..line.len() {
            let part_line = find_matches(chars.as_str(), &opts);
            for part in part_line {
                matches.push((i, part));
            }
            chars.next();
        }
        let matches: Vec<&str> = matches.into_iter().map(|(_, s)| s).collect();
        let show = if matches.len() > 0 { matches[0] } else { "" };

        // Discard extra characters from line; find any lines that could match
        // and discard up to there
        let mut skip_chars = 0;
        let mut chars = line.chars();
        for i in 0..line.len() {
            let part_line = find_could_match(chars.as_str(), &opts);
            if part_line.len() == 0 {
                // Advance the line so we don't leak chars
                skip_chars = i;
            } else {
                break;
            }
            chars.next();
        }
        let (_, tline) = line.split_at(skip_chars);
        line = String::from(tline);


        //println!("Show {show}");
        //println!("Matches {:?}", matches);
        //println!("Line {line}");

        canvas.clear();
        if let Some(tex) = textures.get(show) {
            canvas.copy(tex, None, None).unwrap();
        }
        canvas.present();
        ::std::thread::sleep(Duration::new(0, 1_000_000_000u32 / 30));
        // The rest of the game loop goes here...
    }

    Ok(())
}

fn find_matches<'a, 'b>(word: &'b str, opts: &'a [&'a str]) -> Vec<&'a str> {
    if word.len() == 0 {
        return vec![];
    }
    let mut ret = vec![];
    for opt in opts {
        if &word == opt {
            ret.push(*opt)
        }
    }
    ret
}


fn find_could_match<'a, 'b>(word: &'b str, opts: &'a [&'a str]) -> Vec<&'a str> {
    if word.len() == 0 {
        return vec![];
    }
    let mut ret = vec![];
    for opt in opts {
        if opt.starts_with(word) {
            ret.push(*opt)
        }
    }
    ret
}
