#![allow(mixed_script_confusables)]

mod morphism;
mod parser;
mod c_gen;
mod struct_layout;

use {
    crate::{
        morphism::LdmcMorphism,
        parser::morphism_base_parser,
    }, ariadne::{Color, Label, Report, ReportKind, Source}, chumsky::prelude::*, laddertypes::{
        parser::ParseLadderType, unparser::*, BimapTypeDict, MorphismType, TypeDict
    }, std::{collections::HashMap, sync::{Arc, RwLock}}, tiny_ansi::TinyAnsi
};

fn main() {
    let mut type_dict = Arc::new(RwLock::new(BimapTypeDict::new()));

    let mut morphism_base = laddertypes::morphism_base::MorphismBase::<LdmcMorphism>::new(vec![
        //type_dict.parse("Seq~MsbCont").expect(""),
        //type_dict.parse("Seq~<ValueTerminated '\\0'>").expect(""),
        type_dict.parse("Seq~<LengthPrefix native.UInt64>").expect(""),
        type_dict.parse("Struct").expect("")
    ]);

    let mut args = std::env::args().skip(1);
    let src_type_arg = args.next().expect("src type expected");
    let dst_type_arg = args.next().expect("dst type expected");

    for mb_path in args {
        let src = std::fs::read_to_string(mb_path.clone()).expect("read");
        let result = morphism_base_parser(type_dict.clone()).parse(src.clone());
        match result {
            Ok((includes, morphisms)) => {
                eprintln!("[{}] parse ok.", mb_path.bright_yellow());
                println!("{}", includes);
                for m in morphisms {
                    morphism_base.add_morphism(LdmcMorphism::Primitive(m));
                }
            }
            Err(errs) => {
                errs.into_iter().for_each(|e| {
                        Report::build(ReportKind::Error, (), e.span().start)
                            .with_message(e.to_string())
                            .with_label(
                                Label::new(e.span())
                                    .with_message(e)
                                    .with_color(Color::Red),
                            )
                            .finish()
                            .print(Source::from(&src))
                            .unwrap()
                        });
            }
        }
    }

    let mut γ = HashMap::new();
    γ.insert(
        type_dict.get_typeid_creat(&"Byte".into()),
        type_dict.parse("<Seq~<StaticLength 8> Bit>").expect("parse")
            .apply_substitution(&|k| γ.get(k).cloned())
            .clone()
    );
    γ.insert(
        type_dict.get_typeid_creat(&"x86.UInt8".into()),
        type_dict.parse("ℤ_2^8 ~ <PosInt 2 BigEndian> ~ <Seq~<StaticLength 8> <Digit 2> ~ Bit>").expect("parse")
            .apply_substitution(&|k| γ.get(k).cloned())
            .clone()
    );
    γ.insert(
        type_dict.get_typeid_creat(&"x86.UInt16".into()),
        type_dict.parse("ℤ_2^16 ~ <PosInt 256 LittleEndian> ~ <Seq~<StaticLength 2> <Digit 256> ~ x86.UInt8 >").expect("parse")
            .apply_substitution(&|k| γ.get(k).cloned())
            .clone()
    );
    γ.insert(
        type_dict.get_typeid_creat(&"x86.UInt32".into()),
        type_dict.parse("ℤ_2^32 ~ <PosInt 256 LittleEndian> ~ <Seq~<StaticLength 4> <Digit 256> ~ x86.UInt8 >").expect("parse")
            .apply_substitution(&|k| γ.get(k).cloned())
            .clone()
    );
    γ.insert(
        type_dict.get_typeid_creat(&"x86.UInt64".into()),
        type_dict.parse("ℤ_2^64 ~ <PosInt 256 LittleEndian> ~ <Seq~<StaticLength 8> <Digit 256> ~ x86.UInt8 >").expect("parse")
            .apply_substitution(&|k| γ.get(k).cloned())
            .clone()
    );
    γ.insert(
        type_dict.get_typeid_creat(&"x86.Float".into()),
        type_dict.parse("ℝ ~ IEEE-754.Float32 ~ <Seq~<StaticLength 4> Byte>").expect("parse")
            .apply_substitution(&|k| γ.get(k).cloned())
            .clone()
    );

    let struct_type1 = type_dict.parse("
        <Struct
            <Struct.Field  online-since          TimePoint ~ <TimeSince UnixEpoch> ~ Duration ~ Seconds ~ ℝ ~ <QuantizedLinear 0 1 1000> ~ ℕ ~ native.UInt64 >
            <Struct.Field  battery-capacity      Energy ~ Wh ~ ℝ ~ <QuantizedLinear 0 1 1000> ~ ℕ ~ native.UInt64 >
            <Struct.Field  battery-charge        Energy ~ Wh ~ ℝ ~ <QuantizedLinear 0 1 1000> ~ ℕ ~ native.UInt64 >
            <Struct.Field  min-sampling-period   Duration ~ Seconds ~ ℝ ~ <QuantizedLinear 0 1 1000> ~ ℕ ~ native.UInt32 >
            <Struct.Field  cur-sampling-period   Duration ~ Seconds ~ ℝ ~ <QuantizedLinear 0 1 1000> ~ ℕ ~ native.UInt32 >
            <Struct.Field  max-chunk-size        ℕ ~ native.UInt32 >
            <Struct.Field  cur-chunk-size        ℕ ~ native.UInt32 >
            <Struct.Field  n-chunks-capacity     ℕ ~ native.UInt32 >
            <Struct.Field  n-full-data-chunks    ℕ ~ native.UInt32 >
            <Struct.Field  n-empty-data-chunks   ℕ ~ native.UInt32 >
        >
    ").expect("parse struct type");

    eprintln!("{}
    ",
    struct_type1.clone().sugar(&mut type_dict).pretty(&type_dict, 0)
    );

    for m in morphism_base.enum_morphisms( &struct_type1 ) {
        eprintln!("{:?} -> {:?}",
            type_dict.read().unwrap().unparse(&m.get_type().src_type),
            type_dict.read().unwrap().unparse(&m.get_type().dst_type)
        );
    }
    return;

    let struct_type2 = type_dict.parse("
        <Struct

            <Struct.Field  battery-charge        Energy ~ Wh ~ ℝ ~ native.Double >
            <Struct.Field  battery-capacity      Energy ~ Wh ~ ℝ ~ native.Double >
            <Struct.Field  min-sampling-period   Duration ~ Seconds ~ ℝ ~ native.Double >
            <Struct.Field  cur-sampling-period   Duration ~ Seconds ~ ℝ ~ native.Double >
            <Struct.Field  max-chunk-size        ℕ ~ native.UInt32 >
            <Struct.Field  cur-chunk-size        ℕ ~ native.UInt32 >
            <Struct.Field  n-chunks-capacity     ℕ ~ native.UInt32 >
            <Struct.Field  n-full-data-chunks    ℕ ~ native.UInt32 >
            <Struct.Field  n-empty-data-chunks   ℕ ~ native.UInt32 >

            <Struct.Field  online-since          TimePoint ~ <TimeSince UnixEpoch> ~ Duration ~ Seconds ~ ℝ ~ native.Double >
        >
    ").expect("parse struct type");

    let m = LdmcMorphism::new_struct_map(&mut type_dict, &morphism_base, struct_type1, struct_type2);

    println!("{}",
        m.into_prim_c_morphism(&mut type_dict, "morph_my_struct".into(), &HashMap::new())
            .c_source
    );
return;
    let src_type = type_dict.parse( src_type_arg.as_str() ).expect("");
    let dst_type = type_dict.parse( dst_type_arg.as_str() ).expect("");

    let path = laddertypes::morphism_path::ShortestPathProblem::new(
        &morphism_base,
        MorphismType {
            src_type: src_type.clone(),
            dst_type: dst_type.clone(),
        }).solve();

    match path {
        Some(path) => {
            c_gen::generate_main(&mut type_dict, path, src_type, dst_type);
        }
        None => {
            eprintln!("Error: could not find morphism path");
            std::process::exit(-1);
        }
    }
}