// SPDX-License-Identifier: AGPL-3.0-or-later
// Commercial license available
// © Concepts 1996–2026 Miroslav Šotek. All rights reserved.
// © Code 2020–2026 Miroslav Šotek. All rights reserved.
// ORCID: 0009-0009-3560-0851
// Contact: www.anulum.li | protoscience@anulum.li
// SC-NeuroCore — Text-format printer for SC IR graphs

//! Text-format printer for SC IR graphs.
//!
//! # Format
//!
//! ```text
//! sc.graph @module_name {
//!   %0 = sc.input "x_in" : rate
//!   %1 = sc.constant 0.5 : rate
//!   %2 = sc.encode %0, length=1024, seed=0xACE1 : bitstream<1024>
//!   %3 = sc.encode %1, length=1024, seed=0xBEEF : bitstream<1024>
//!   %4 = sc.and %2, %3 : bitstream<1024>
//!   %5 = sc.popcount %4 : u64
//!   sc.output "result" %5
//! }
//! ```

use crate::ir::graph::*;

/// Print a graph to its text representation.
pub fn print(graph: &ScGraph) -> String {
    let mut out = String::new();
    out.push_str(&format!("sc.graph @{} {{\n", graph.name));

    for op in &graph.ops {
        out.push_str("  ");
        match op {
            ScOp::Input { id, name, ty } => {
                out.push_str(&format!("{} = sc.input \"{}\" : {}\n", id, name, ty));
            }
            ScOp::Output { name, source, .. } => {
                out.push_str(&format!("sc.output \"{}\" {}\n", name, source));
            }
            ScOp::Constant { id, value, ty } => {
                let val_str = match value {
                    ScConst::F64(v) => format!("{v}"),
                    ScConst::I64(v) => format!("{v}"),
                    ScConst::U64(v) => format!("{v}"),
                    ScConst::F64Vec(v) => format!(
                        "[{}]",
                        v.iter()
                            .map(|x| format!("{x}"))
                            .collect::<Vec<_>>()
                            .join(", ")
                    ),
                    ScConst::I64Vec(v) => format!(
                        "[{}]",
                        v.iter()
                            .map(|x| format!("{x}"))
                            .collect::<Vec<_>>()
                            .join(", ")
                    ),
                };
                out.push_str(&format!("{} = sc.constant {} : {}\n", id, val_str, ty));
            }
            ScOp::Encode {
                id,
                prob,
                length,
                seed,
            } => {
                out.push_str(&format!(
                    "{} = sc.encode {}, length={}, seed=0x{:04X} : bitstream<{}>\n",
                    id, prob, length, seed, length
                ));
            }
            ScOp::BitwiseAnd { id, lhs, rhs } => {
                out.push_str(&format!("{} = sc.and {}, {} : bitstream\n", id, lhs, rhs));
            }
            ScOp::BitwiseXor { id, lhs, rhs } => {
                out.push_str(&format!("{} = sc.xor {}, {} : bitstream\n", id, lhs, rhs));
            }
            ScOp::Popcount { id, input } => {
                out.push_str(&format!("{} = sc.popcount {} : u64\n", id, input));
            }
            ScOp::Reduce { id, input, mode } => {
                out.push_str(&format!(
                    "{} = sc.reduce {}, mode={} : rate\n",
                    id, input, mode
                ));
            }
            ScOp::LifStep {
                id,
                current,
                leak,
                gain,
                noise,
                params,
            } => {
                out.push_str(&format!(
                    "{} = sc.lif_step {}, leak={}, gain={}, noise={}, \
                     dw={}, frac={}, vt={}, rp={} : (bool, fixed<{},{}>)\n",
                    id,
                    current,
                    leak,
                    gain,
                    noise,
                    params.data_width,
                    params.fraction,
                    params.v_threshold,
                    params.refractory_period,
                    params.data_width,
                    params.fraction
                ));
            }
            ScOp::DenseForward {
                id,
                inputs,
                weights,
                leak,
                gain,
                params,
            } => {
                out.push_str(&format!(
                    "{} = sc.dense_forward {}, weights={}, leak={}, gain={}, \
                     ni={}, nn={}, len={} : vec<bool,{}>\n",
                    id,
                    inputs,
                    weights,
                    leak,
                    gain,
                    params.n_inputs,
                    params.n_neurons,
                    params.stream_length,
                    params.n_neurons
                ));
            }
            ScOp::DclsLayer {
                id,
                spike,
                weights,
                centre,
                sigma,
                params,
            } => {
                out.push_str(&format!(
                    "{} = sc.dcls_layer {}, weights={}, centre={}, sigma={}, \
                     taps={}, depth={}, dw={}, frac={} : fixed<{},{}>\n",
                    id,
                    spike,
                    weights,
                    centre,
                    sigma,
                    params.n_taps,
                    params.delay_depth,
                    params.data_width,
                    params.fraction,
                    params.data_width,
                    params.fraction
                ));
            }
            ScOp::GraphForward {
                id,
                features,
                adjacency,
                n_nodes,
                n_features,
            } => {
                out.push_str(&format!(
                    "{} = sc.graph_forward {}, adj={}, nodes={}, features={} : rate\n",
                    id, features, adjacency, n_nodes, n_features
                ));
            }
            ScOp::SoftmaxAttention { id, q, k, v, dim_k } => {
                out.push_str(&format!(
                    "{} = sc.softmax_attention {}, {}, {}, dim_k={} : rate\n",
                    id, q, k, v, dim_k
                ));
            }
            ScOp::KuramotoStep {
                id,
                phases,
                omega,
                coupling,
                dt,
            } => {
                out.push_str(&format!(
                    "{} = sc.kuramoto_step {}, omega={}, K={}, dt={} : rate\n",
                    id, phases, omega, coupling, dt
                ));
            }
            ScOp::Scale { id, input, factor } => {
                out.push_str(&format!(
                    "{} = sc.scale {}, factor={} : rate\n",
                    id, input, factor
                ));
            }
            ScOp::Offset { id, input, offset } => {
                out.push_str(&format!(
                    "{} = sc.offset {}, offset={} : rate\n",
                    id, input, offset
                ));
            }
            ScOp::DivConst { id, input, divisor } => {
                out.push_str(&format!(
                    "{} = sc.div_const {}, divisor={} : u64\n",
                    id, input, divisor
                ));
            }
        }
    }

    out.push_str("}\n");
    out
}
