/*
    Hurry Curry! - a game about cooking
    Copyright (C) 2025 Hurry Curry! Contributors

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Affero General Public License as published by
    the Free Software Foundation, version 3 of the License only.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Affero General Public License for more details.

    You should have received a copy of the GNU Affero General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.

*/

use anyhow::Result;
use hurrycurry_data::index::DataIndex;
use hurrycurry_protocol::{ItemIndex, Recipe, TileIndex};
use std::collections::HashSet;

pub(crate) fn graph_summary() -> Result<()> {
    let index = DataIndex::new("data".into())?;

    println!("digraph {{");

    let (data, sdata) = index.generate("5star")?;

    struct Node {
        inputs: Vec<ItemIndex>,
        outputs: Vec<ItemIndex>,
        tool_item: Option<ItemIndex>,
        tool_tile: Option<TileIndex>,
        instant: bool,
        demand: bool,
    }

    let map_items = sdata
        .initial_map
        .iter()
        .flat_map(|(_, (_, i))| *i)
        .collect::<HashSet<_>>();

    let mut nodes = Vec::new();
    for r in &data.recipes {
        nodes.push(Node {
            inputs: r.inputs(),
            outputs: r.outputs(),
            tool_item: None,
            tool_tile: r.tile(),
            instant: matches!(r, Recipe::Instant { .. }),
            demand: false,
        })
    }
    for d in &data.demands {
        nodes.push(Node {
            demand: true,
            instant: false,
            inputs: vec![d.input],
            outputs: d.output.into_iter().collect(),
            tool_item: None,
            tool_tile: None,
        })
    }

    loop {
        let node_count_before = nodes.len();

        let mut has_fdeps = vec![false; data.item_names.len()];
        for n in &nodes {
            for ItemIndex(i) in &n.inputs {
                has_fdeps[*i] = true;
            }
        }
        // Remove demand outputs
        for n in &mut nodes {
            n.outputs.retain(|_item| !n.demand);
        }
        // Remove outputs that are not depended on
        for n in &mut nodes {
            n.outputs.retain(|item| n.demand || has_fdeps[item.0])
        }
        // Remove outputs that exist on the map, like pots and plates
        for n in &mut nodes {
            n.outputs.retain(|item| !map_items.contains(item))
        }
        // Convert map item inputs to tools
        for n in &mut nodes {
            n.inputs.retain(|i| {
                if map_items.contains(i) {
                    n.tool_item = Some(*i);
                    false
                } else {
                    true
                }
            })
        }
        // Remove outputless recipes
        nodes.retain(|n| n.demand || !n.outputs.is_empty());

        if nodes.len() == node_count_before {
            break;
        }
    }

    let mut items = HashSet::<ItemIndex>::new();
    for n in &nodes {
        items.extend(&n.inputs);
        items.extend(&n.outputs);
    }

    for ItemIndex(i) in items {
        println!("i{i} [label=\"{}\"]", data.item_name(ItemIndex(i)))
    }
    for (ni, node) in nodes.iter().enumerate() {
        let color = if node.demand {
            "#c4422b"
        } else if node.instant {
            "#5452d8"
        } else {
            "#47c42b"
        };
        println!(
            "r{ni} [label=\"{}\" shape=box color={color:?} fillcolor={color:?} style=filled]",
            if let Some(tool) = node.tool_tile {
                data.tile_name(tool)
            } else if let Some(tool) = node.tool_item {
                data.item_name(tool)
            } else if node.instant {
                "Combine"
            } else if node.demand {
                "Demand"
            } else {
                "Passive"
            }
        );
        for ItemIndex(input) in &node.inputs {
            println!("i{input} -> r{ni}")
        }
        for ItemIndex(output) in &node.outputs {
            println!("r{ni} -> i{output}")
        }
    }

    println!("}}");
    Ok(())
}
