#include "model/fault_tree/fault_tree.h"

#include <iostream>
#include <cassert>

using namespace std;

ostream& Output_Descriptive_Input_Sequence(ostream& in_ostream, const Fault_Tree& in_fault_tree, const Input_Sequence& in_input_sequence)
{
  Input_Sequence::const_iterator an_event;
  for (an_event = in_input_sequence.begin(); an_event != in_input_sequence.end(); an_event++)
  {
    in_ostream << in_fault_tree.Get_Event_Description(*an_event) << " (id: " << *an_event << ")";

    Input_Sequence::const_iterator next_event(an_event);
    next_event++;
    if (next_event != in_input_sequence.end())
      in_ostream << ", ";
  }

  return in_ostream;
}

// This output function uses a custom input sequence output routine that
// prints event descriptions (from the fault tree) instead of relying on the
// default << output of Input_Sequence.
ostream& operator<< (ostream& in_ostream, const Fault_Tree& in_fault_tree)
{
  in_ostream << "== System event:" << endl;
  in_ostream << in_fault_tree.Get_Event_Description(in_fault_tree.Get_System_Event()) << " (id: " << in_fault_tree.Get_System_Event() << ", replication: " <<
    in_fault_tree.Get_Replication(in_fault_tree.Get_System_Event()) << ")" << endl;

  in_ostream << "== Basic events:" << endl;
  if (in_fault_tree.Get_Basic_Events().size() > 0)
  {
    set<Event>::const_iterator an_event;

    for (an_event = in_fault_tree.Get_Basic_Events().begin(); an_event != in_fault_tree.Get_Basic_Events().end(); an_event++)
      in_ostream << in_fault_tree.Get_Event_Description(*an_event) << " (id: " << *an_event << ", replication: " <<
        in_fault_tree.Get_Replication(*an_event) << ")" << endl;
  }
  else
    in_ostream << "<NONE>" << endl;

  in_ostream << "== AND gates:" << endl;
  if (in_fault_tree.Get_And_Gates().size() > 0)
  {
    set<Event>::const_iterator an_event;

    for (an_event = in_fault_tree.Get_And_Gates().begin(); an_event != in_fault_tree.Get_And_Gates().end(); an_event++)
    {
      in_ostream << in_fault_tree.Get_Event_Description(*an_event) << " (id: " << *an_event << ", replication: " <<
        in_fault_tree.Get_Replication(*an_event) << ", inputs: ";
      Output_Descriptive_Input_Sequence(in_ostream, in_fault_tree, in_fault_tree.Get_Gate_Inputs(*an_event));
      in_ostream << ")" << endl;
    }
  }
  else
    in_ostream << "<NONE>" << endl;

  in_ostream << "== OR gates:" << endl;
  if (in_fault_tree.Get_Or_Gates().size() > 0)
  {
    set<Event>::const_iterator an_event;

    for (an_event = in_fault_tree.Get_Or_Gates().begin(); an_event != in_fault_tree.Get_Or_Gates().end(); an_event++)
    {
      in_ostream << in_fault_tree.Get_Event_Description(*an_event) << " (id: " << *an_event << ", replication: " <<
        in_fault_tree.Get_Replication(*an_event) << ", inputs: ";
      Output_Descriptive_Input_Sequence(in_ostream, in_fault_tree, in_fault_tree.Get_Gate_Inputs(*an_event));
      in_ostream << ")" << endl;
    }
  }
  else
    in_ostream << "<NONE>" << endl;

  in_ostream << "== PAND gates:" << endl;
  if (in_fault_tree.Get_Pand_Gates().size() > 0)
  {
    set<Event>::const_iterator an_event;

    for (an_event = in_fault_tree.Get_Pand_Gates().begin(); an_event != in_fault_tree.Get_Pand_Gates().end(); an_event++)
    {
      in_ostream << in_fault_tree.Get_Event_Description(*an_event) << " (id: " << *an_event << ", replication: " <<
        in_fault_tree.Get_Replication(*an_event) << ", inputs: ";
      Output_Descriptive_Input_Sequence(in_ostream, in_fault_tree, in_fault_tree.Get_Gate_Inputs(*an_event));
      in_ostream << ")" << endl;
    }
  }
  else
    in_ostream << "<NONE>" << endl;

  in_ostream << "== Spare gates:" << endl;
  if (in_fault_tree.Get_Spare_Gates().size() > 0)
  {
    set<Event>::const_iterator an_event;

    for (an_event = in_fault_tree.Get_Spare_Gates().begin(); an_event != in_fault_tree.Get_Spare_Gates().end(); an_event++)
    {
      in_ostream << in_fault_tree.Get_Event_Description(*an_event) << " (id: " << *an_event << ", replication: " <<
        in_fault_tree.Get_Replication(*an_event) << ", inputs: ";
      Output_Descriptive_Input_Sequence(in_ostream, in_fault_tree, in_fault_tree.Get_Gate_Inputs(*an_event));
      in_ostream << ")" << endl;
    }
  }
  else
    in_ostream << "<NONE>" << endl;

  in_ostream << "== Threshold gates:" << endl;
  if (in_fault_tree.Get_Threshold_Gates().size() > 0)
  {
    // This is like Output_Descriptive_Input_Sequence, but also includes the
    // threshold value of the threshold gate.
    set<Event>::const_iterator an_event;

    for (an_event = in_fault_tree.Get_Threshold_Gates().begin(); an_event != in_fault_tree.Get_Threshold_Gates().end(); an_event++)
    {
      in_ostream << in_fault_tree.Get_Event_Description(*an_event) << " (id: " << *an_event << ", replication: " <<
        in_fault_tree.Get_Replication(*an_event) << ", inputs: ";
      Output_Descriptive_Input_Sequence(in_ostream, in_fault_tree, in_fault_tree.Get_Gate_Inputs(*an_event));
      in_ostream << ", threshold: " << in_fault_tree.Get_Threshold(*an_event);
      in_ostream << ")" << endl;
    }
  }
  else
    in_ostream << "<NONE>" << endl;

  in_ostream << "== SEQ constraints:" << endl;
  if (in_fault_tree.Get_SEQ_Constraints().size() > 0)
  {
    set<Input_Sequence>::const_iterator an_seq;
    for (an_seq = in_fault_tree.Get_SEQ_Constraints().begin(); an_seq != in_fault_tree.Get_SEQ_Constraints().end(); an_seq++)
    {
      Output_Descriptive_Input_Sequence(in_ostream, in_fault_tree, *an_seq);
      in_ostream << endl;
    }
  }
  else
    in_ostream << "<NONE>" << endl;

  in_ostream << "== FDEP constraints:" << endl;
  if (in_fault_tree.Get_FDEP_Constraints().size() > 0)
  {
    set<Functional_Dependency>::const_iterator an_fdep;
    for (an_fdep = in_fault_tree.Get_FDEP_Constraints().begin(); an_fdep != in_fault_tree.Get_FDEP_Constraints().end(); an_fdep++)
    {
      in_ostream << in_fault_tree.Get_Event_Description((*an_fdep).Get_Trigger()) << " (id: " << (*an_fdep).Get_Trigger() << ") -> ";
      Output_Descriptive_Input_Sequence(in_ostream, in_fault_tree, (*an_fdep).Get_Dependents());
      in_ostream << endl;
    }
  }
  else
    in_ostream << "<NONE>" << endl;

  return in_ostream;
}

Fault_Tree::Fault_Tree()
{
  system_event_initialized = false;
}

Fault_Tree::Fault_Tree(const Fault_Tree& in_fault_tree)
{
  *this = in_fault_tree;
}

Fault_Tree::~Fault_Tree()
{
}

void Fault_Tree::Clear()
{
  basic_events.clear();
  and_gates.clear();
  or_gates.clear();
  threshold_gates.clear();
  pand_gates.clear();
  spare_gates.clear();
  seq_constraints.clear();
  fdep_constraints.clear();
  thresholds.clear();
  inputs.clear();
  gates.clear();
  events.clear();
  replications.clear();
  event_descriptions.clear();
  seq_descriptions.clear();
  fdep_descriptions.clear();

  system_event_initialized = false;
}

void Fault_Tree::Set_System_Event(const Event& in_system_event)
{
  system_event = in_system_event;
  system_event_initialized = true;
}

void Fault_Tree::Unset_System_Event()
{
  Event unknown;
  system_event = unknown;
  system_event_initialized = false;
}

void Fault_Tree::Insert_Basic_Event(const Event& in_basic_event,
                            const Replication& in_replication)
{
  basic_events.insert(in_basic_event);
  events.insert(in_basic_event);
  replications[in_basic_event] = in_replication;
}

void Fault_Tree::Insert_And_Gate(const Event& in_and_gate)
{
  and_gates.insert(in_and_gate);
  gates.insert(in_and_gate);
  events.insert(in_and_gate);
  replications[in_and_gate] = 1;
}

void Fault_Tree::Insert_Or_Gate(const Event& in_or_gate)
{
  or_gates.insert(in_or_gate);
  gates.insert(in_or_gate);
  events.insert(in_or_gate);
  replications[in_or_gate] = 1;
}

void Fault_Tree::Insert_Threshold_Gate(const Event& in_threshold_gate,
                           const Threshold& in_threshold)
{
  threshold_gates.insert(in_threshold_gate);
  gates.insert(in_threshold_gate);
  events.insert(in_threshold_gate);
  thresholds[in_threshold_gate] = in_threshold;
  replications[in_threshold_gate] = 1;
}

void Fault_Tree::Insert_Pand_Gate(const Event& in_pand_gate)
{
  pand_gates.insert(in_pand_gate);
  gates.insert(in_pand_gate);
  events.insert(in_pand_gate);
  replications[in_pand_gate] = 1;
}

void Fault_Tree::Insert_Spare_Gate(const Event& in_spare_gate)
{
  spare_gates.insert(in_spare_gate);
  gates.insert(in_spare_gate);
  events.insert(in_spare_gate);
  replications[in_spare_gate] = 1;
}

void Fault_Tree::Insert_SEQ_Constraint(const Input_Sequence& in_sequence)
{
  seq_constraints.insert(in_sequence);
}

void Fault_Tree::Insert_FDEP_Constraint(const Functional_Dependency& in_fdep)
{
  fdep_constraints.insert(in_fdep);
}

void Fault_Tree::Remove_Basic_Event(const Event& in_basic_event)
{
  basic_events.erase(in_basic_event);
  events.erase(in_basic_event);
  replications.erase(in_basic_event);
}

void Fault_Tree::Remove_And_Gate(const Event& in_and_gate)
{
  and_gates.erase(in_and_gate);
  gates.erase(in_and_gate);
  events.erase(in_and_gate);
}

void Fault_Tree::Remove_Or_Gate(const Event& in_or_gate)
{
  or_gates.erase(in_or_gate);
  gates.erase(in_or_gate);
  events.erase(in_or_gate);
}

void Fault_Tree::Remove_Threshold_Gate(const Event& in_threshold_gate)
{
  threshold_gates.erase(in_threshold_gate);
  gates.erase(in_threshold_gate);
  events.erase(in_threshold_gate);
  thresholds.erase(in_threshold_gate);
}

void Fault_Tree::Remove_Pand_Gate(const Event& in_pand_gate)
{
  pand_gates.erase(in_pand_gate);
  gates.erase(in_pand_gate);
  events.erase(in_pand_gate);
}

void Fault_Tree::Remove_Spare_Gate(const Event& in_spare_gate)
{
  spare_gates.erase(in_spare_gate);
  gates.erase(in_spare_gate);
  events.erase(in_spare_gate);
}

void Fault_Tree::Remove_SEQ_Constraint(const Input_Sequence& in_sequence)
{
  seq_constraints.erase(in_sequence);
}

void Fault_Tree::Remove_FDEP_Constraint(const Functional_Dependency& in_fdep)
{
  fdep_constraints.erase(in_fdep);
}

const set<Event>& Fault_Tree::Get_Basic_Events() const
{
  return basic_events;
}

const set<Event>& Fault_Tree::Get_And_Gates() const
{
  return and_gates;
}

const set<Event>& Fault_Tree::Get_Or_Gates() const
{
  return or_gates;
}

const set<Event>& Fault_Tree::Get_Threshold_Gates() const
{
  return threshold_gates;
}

const set<Event>& Fault_Tree::Get_Pand_Gates() const
{
  return pand_gates;
}

const set<Event>& Fault_Tree::Get_Spare_Gates() const
{
  return spare_gates;
}

const set<Event>& Fault_Tree::Get_Gates() const
{
  return gates;
}

const set<Event>& Fault_Tree::Get_Events() const
{
  return events;
}

const Event& Fault_Tree::Get_System_Event() const
{
  assert(system_event_initialized);

  return system_event;
}

const set<Input_Sequence>& Fault_Tree::Get_SEQ_Constraints() const
{
  return seq_constraints;
}

const set<Functional_Dependency>& Fault_Tree::Get_FDEP_Constraints() const
{
  return fdep_constraints;
}

const Replication_Map& Fault_Tree::Get_Replication_Map() const
{
  return replications;
}

void Fault_Tree::Set_Gate_Inputs(const Event& in_gate, const Input_Sequence& in_inputs)
{
  inputs[in_gate] = in_inputs;
}

void Fault_Tree::Remove_Gate_Inputs(const Event& in_gate)
{
  inputs.erase(in_gate);
}

const Input_Sequence& Fault_Tree::Get_Gate_Inputs(const Event& in_gate) const
{
  assert(inputs.find(in_gate) != inputs.end());

  return (*inputs.find(in_gate)).second;
}

const set<Event> Fault_Tree::Get_Gates_Event_Is_Input_To(const Event& in_event) const
{
  set<Event> gates_input_to;

  const set<Event> fault_tree_gates = Get_Gates();

  set<Event>::const_iterator gate;
  for (gate = fault_tree_gates.begin(); gate != fault_tree_gates.end(); gate++)
  {
    Input_Sequence gate_inputs = Get_Gate_Inputs(*gate);

    Input_Sequence::const_iterator input;
    for (input = gate_inputs.begin(); input != gate_inputs.end(); input++)
      if (*input == in_event)
      {
        gates_input_to.insert(*gate);
        break;
      }
  }

  return gates_input_to;
}


const set<Event> Fault_Tree::Get_FDEP_Triggers_Of_Event(const Event& in_event) const
{
  set<Event> triggers_of_event;

  const set<Functional_Dependency> fault_tree_fdeps = Get_FDEP_Constraints();

  set<Functional_Dependency>::const_iterator an_fdep;
  for (an_fdep = fault_tree_fdeps.begin(); an_fdep != fault_tree_fdeps.end(); an_fdep++)
  {
    Input_Sequence dependents = (*an_fdep).Get_Dependents();

    if (dependents.find(in_event) != dependents.end())
      triggers_of_event.insert((*an_fdep).Get_Trigger());
  }

  return triggers_of_event;
}


const set<Input_Sequence> Fault_Tree::Get_FDEP_Dependents_Of_Event(const Event& in_event) const
{
  set<Input_Sequence> dependents_of_event;

  const set<Functional_Dependency> fault_tree_fdeps = Get_FDEP_Constraints();

  set<Functional_Dependency>::const_iterator an_fdep;
  for (an_fdep = fault_tree_fdeps.begin(); an_fdep != fault_tree_fdeps.end(); an_fdep++)
  {
    if ((*an_fdep).Get_Trigger() == in_event)
      dependents_of_event.insert((*an_fdep).Get_Dependents());
  }

  return dependents_of_event;
}


void Fault_Tree::Set_Replication(const Event& in_event, const Replication& in_replication)
{
  replications[in_event] = in_replication;
}

const Replication& Fault_Tree::Get_Replication(const Event& in_event) const
{
  return (*replications.find(in_event)).second;
}


void Fault_Tree::Set_Threshold(const Event& in_threshold_gate, const Threshold& in_threshold)
{
  thresholds[in_threshold_gate] = in_threshold;
}

const Threshold& Fault_Tree::Get_Threshold(const Event& in_threshold_gate) const
{
  return (*thresholds.find(in_threshold_gate)).second;
}


void Fault_Tree::Set_Event_Description(const Event& in_event, const string& in_description)
{
  event_descriptions[in_event] = in_description;
}

void Fault_Tree::Remove_Event_Description(const Event& in_event)
{
  event_descriptions.erase(in_event);
}

const string& Fault_Tree::Get_Event_Description(const Event& in_event) const
{
  assert(event_descriptions.find(in_event) != event_descriptions.end());

  return (*event_descriptions.find(in_event)).second;
}

const bijection<Event, string>& Fault_Tree::Get_Event_Descriptions() const
{
  return event_descriptions;
}

void Fault_Tree::Set_SEQ_Description(const Input_Sequence& in_seq,const string& in_description)
{
  seq_descriptions[in_seq] = in_description;
}

void Fault_Tree::Remove_SEQ_Description(const Input_Sequence& in_seq)
{
  seq_descriptions.erase(in_seq);
}

const string& Fault_Tree::Get_SEQ_Description(const Input_Sequence& in_seq) const
{
  assert(seq_descriptions.find(in_seq) != seq_descriptions.end());

  return (*seq_descriptions.find(in_seq)).second;
}

void Fault_Tree::Set_FDEP_Description(const Functional_Dependency& in_fdep, const string& in_description)
{
  fdep_descriptions[in_fdep] = in_description;
}

void Fault_Tree::Remove_FDEP_Description(const Functional_Dependency& in_fdep)
{
  fdep_descriptions.erase(in_fdep);
}

const string& Fault_Tree::Get_FDEP_Description(const Functional_Dependency& in_fdep) const
{
  assert(fdep_descriptions.find(in_fdep) != fdep_descriptions.end());

  return (*fdep_descriptions.find(in_fdep)).second;
}

const bool Fault_Tree::Event_Not_Referenced(const Event &in_event)
{
  if (events.find(in_event) != events.end())
    return false;

  set<Input_Sequence>::const_iterator an_seq;
  for(an_seq = seq_constraints.begin(); an_seq != seq_constraints.end(); an_seq++)
  {
    if ((*an_seq).find(in_event) != (*an_seq).end())
      return false;
  }

  set<Functional_Dependency>::const_iterator an_fdep;
  for(an_fdep = fdep_constraints.begin(); an_fdep != fdep_constraints.end(); an_fdep++)
  {
    Input_Sequence dependents = (*an_fdep).Get_Dependents();
    if (dependents.find(in_event) != dependents.end())
      return false;

    if ((*an_fdep).Get_Trigger() == in_event)
      return false;
  }

  if (system_event_initialized)
  {
    if (Get_System_Event() == in_event)
      return false;
  }

  set<Event>::const_iterator a_gate;
  for(a_gate = gates.begin(); a_gate != gates.end(); a_gate++)
  {
    if (inputs.find(*a_gate) != inputs.end())
    {
      Input_Sequence gate_inputs = Get_Gate_Inputs(*a_gate);
      if (gate_inputs.find(in_event) != gate_inputs.end())
        return false;
    }
  }

  return true;
}
