use crate::ebi_framework::ebi_input::FallibleImporterError;
use ebi_objects::{
    BusinessProcessModelAndNotation, DeterministicFiniteAutomaton, DirectlyFollowsModel, EbiObject, EventLog, EventLogCsv, EventLogOcel, EventLogTraceAttributes, EventLogXes, FiniteLanguage, FiniteStochasticLanguage, LabelledPetriNet, LanguageOfAlignments, ProcessTree, StochasticBusinessProcessModelAndNotation, StochasticDeterministicFiniteAutomaton, StochasticDirectlyFollowsModel, StochasticLabelledPetriNet, StochasticLanguageOfAlignments, StochasticNondeterministicFiniteAutomaton, StochasticProcessTree, anyhow::{Error, Result}, ebi_objects::event_log_event_attributes::EventLogEventAttributes, traits::importable::{Importable, ImporterParameterValues}
};
use pastey::paste;
use std::io::BufRead;

macro_rules! import_as_object {
    ($t:ident, $u:expr) => {
        paste! {
            pub trait [<To $t Object>] {
                fn [<to_ $u _object>](self) -> EbiObject;
            }

            pub trait [<ImportAs $t Object>] {
                /// A To will first import, and then convert. The latter conversion may not fail.
                fn [<import_as_ $u _object>](reader: &mut dyn BufRead, parameter_values: &ImporterParameterValues) -> Result<EbiObject>;
            }

            impl<T> [<To $t Object>] for T
            where
                T: Into<$t>,
            {
                fn [<to_ $u _object>](self) -> EbiObject {
                    EbiObject::$t(self.into())
                }
            }

            impl<T: Importable> [<ImportAs $t Object>] for T
            where T: Into<$t>{
                fn [<import_as_ $u _object>](reader: &mut dyn BufRead, parameter_values: &ImporterParameterValues) -> Result<EbiObject> {
                    let x = Self::import(reader, parameter_values)?.into();
                    Ok(EbiObject::$t(x))
                }
            }

            pub trait [<TryTo $t Object>] {
                /// A TryTo will first import, and then try to convert. The latter conversion may fail.
                fn [<try_import_as_ $u _object>](reader: &mut dyn BufRead, parameter_values: &ImporterParameterValues) ->  Result<EbiObject, FallibleImporterError>;
            }

            impl<T: Importable> [<TryTo $t Object>] for T
            where
                T: TryInto<$t, Error = Error>,
            {
                fn [<try_import_as_ $u _object>](reader: &mut dyn BufRead, parameter_values: &ImporterParameterValues) ->  Result<EbiObject, FallibleImporterError> {
                    match Self::import(reader, parameter_values) {
                        Ok(object) => {
                            match object.try_into() {
                                Ok(converted) => Ok(EbiObject::$t(converted)),
                                Err(err) => Err(FallibleImporterError::ConversionError(err.into()))
                            }
                        },
                        Err(err) => Err(FallibleImporterError::ImporterError(err))
                    }
                }
            }
        }
    };
}

import_as_object!(
    BusinessProcessModelAndNotation,
    business_process_model_and_notation
);
import_as_object!(
    StochasticBusinessProcessModelAndNotation,
    stochastic_business_process_model_and_notation
);
import_as_object!(EventLog, event_log);
import_as_object!(EventLogEventAttributes, event_log_event_attributes);
import_as_object!(EventLogTraceAttributes, event_log_trace_attributes);
import_as_object!(EventLogXes, event_log_xes);
import_as_object!(EventLogCsv, event_log_csv);
import_as_object!(EventLogOcel, event_log_ocel);
import_as_object!(LabelledPetriNet, labelled_petri_net);
import_as_object!(DirectlyFollowsModel, directly_follows_model);
import_as_object!(
    StochasticDirectlyFollowsModel,
    stochastic_directly_follows_model
);
import_as_object!(StochasticLabelledPetriNet, stochastic_labelled_petri_net);
import_as_object!(
    StochasticDeterministicFiniteAutomaton,
    stochastic_deterministic_finite_automaton
);
import_as_object!(
    StochasticNondeterministicFiniteAutomaton,
    stochastic_nondeterministic_finite_automaton
);
import_as_object!(FiniteLanguage, finite_language);
import_as_object!(FiniteStochasticLanguage, finite_stochastic_language);
import_as_object!(DeterministicFiniteAutomaton, deterministic_finite_automaton);
import_as_object!(ProcessTree, process_tree);
import_as_object!(StochasticProcessTree, stochastic_process_tree);
import_as_object!(LanguageOfAlignments, language_of_alignments);
import_as_object!(
    StochasticLanguageOfAlignments,
    stochastic_language_of_alignments
);
