#![feature(async_stream)]
use bytes::Bytes;
use std::collections::HashSet;
use std::future::Future;
use std::marker::Unpin;
use std::stream::Stream;
use tokio::io::{self, AsyncBufRead, AsyncBufReadExt, BufReader};

enum Rule {
    Match(String),
    Branch(Vec<Vec<usize>>),
}

fn parse_rule(s: String) -> Option<Rule> {
    let s = s.trim();
    s.split_once(':').and_then(|(_ix, body)| {
        let body = body.trim();
        parse_match_rule(body).or_else(|| parse_branch_rule(body))
    })
}

fn parse_match_rule(body: &str) -> Option<Rule> {
    let oqi = body.find('\"')?;
    let cqi = body.rfind('\"')?;
    Some(Rule::Match(String::from(&body[oqi + 1 .. cqi])))
}

fn parse_branch_rule(body: &str) -> Option<Rule> {
    use std::str::FromStr;
    let mut result = Vec::new();
    for a in body.split('|') {
        let mut branch = Vec::new();
        for b in a.split_whitespace() {
            branch.push(usize::from_str(b).ok()?);
        }
        result.push(branch);
    }
    Some(Rule::Branch(result))
}

async fn read_rules<I: Unpin + AsyncBufRead>(source: &mut I) -> Vec<Rule> {
    let mut result = Vec::new();
    loop {
        let mut buf = String::new();
        match source.read_line(&mut buf).await {
            Ok(0) => break,
            Ok(_) => match parse_rule(buf) {
                None => break,
                Some(rule) => {
                    result.push(rule);
                }
            },
            _ => break,
        }
    }
    result
}

async fn check_match(rules: Vec<Rule>, rule_index: usize, text: String) -> Option<HashSet<String>> {
    tokio::task::yield_now().await;
    match &rules[rule_index] {
        Rule::Match(lit) => text.strip_prefix(<String as AsRef<str>>::as_ref(lit)).map(|s| {
            let mut r = HashSet::with_capacity(1);
            r.insert(String::from(s));
            r
        }),
        Rule::Branch(branches) => {
            let jhs: Vec<_> = branches.iter().map(|branch| {
                let branch = branch.clone();
                let text = text.clone();
                tokio::spawn(async {
                    tokio::task::yield_now().await;
                    let mut remainders = HashSet::with_capacity(1);
                    remainders.insert(text);
                    for child in branch {
                        let mut nr: HashSet<String> = HashSet::new();
                        for r in remainders {
                            let recursive = Box::new(check_match(rules, child, r)) as Box<dyn Future<Output=Option<HashSet<String>>> + Unpin + Send>;
                            for r2 in recursive.await.unwrap_or_else(|| HashSet::new()) {
                            }
                        }
                    }
                    Some(remainders)
                })
            }).collect();
            todo!()
        }
    }
}

async fn write_str<S: io::AsyncWriteExt + Unpin>(sink: &mut S, text: String) -> io::Result<usize> {
    sink.write_buf(&mut Bytes::from(text)).await
}

#[tokio::main(flavor = "multi_thread")]
async fn main() -> io::Result<()> {
    let stdout = &mut io::stdout();
    let args: Vec<_> = std::env::args().collect();
    let mut input = BufReader::new(tokio::fs::File::open(args[1].clone()).await?);
    let rules = read_rules(&mut input).await;
    write_str(stdout, format!("rules.len() = {}\n", rules.len())).await?;
    Ok(())
}