#include "end_of_siding.h"

// pattern endOfSiding(train : Train, location : Segment) {
//   Segment.train(location, train);
//   Segment.connectedTo(end, location);
//   1 == count find connected(end, _);
// }

#define OPTIMIZED_QUERIES

#ifdef OPTIMIZED_QUERIES

void end_of_siding_matcher(ModelRoot *model, EndOfSidingFrame *frame,
                           int startOpIndex, EndOfSidingMatchSet *results) {
  int match_cntr = 0;

  int loop_bound0 = model->train_count;
  for (int i0 = 0; i0 < loop_bound0; i0++) {
    frame->train = model->trains[i0];
    frame->location = frame->train->location;
    if (frame->location != NULL) {
      int loop_bound1 = frame->location->connected_to_count;
      for (int i1 = 0; i1 < loop_bound1; i1++) {
        frame->end = frame->location->connected_to[i1];
        if (frame->end != NULL && frame->end->connected_to_count == 1) {
          results->matches[match_cntr].location = frame->location;
          results->matches[match_cntr++].train = frame->train;
        }
      }
    }
  }

  results->size = match_cntr;
}

#else // original queries

// private pattern connected(a : Segment, b : Segment) {
// 	Segment.connectedTo(a, b);
// }

typedef struct _ConnectedFrame {
  Segment *s1;
  Segment *s2;
} ConnectedFrame;

typedef struct _ConnectedMatch {
  Segment *s1;
  Segment *s2;
} ConnectedMatch;

typedef struct _ConnectedMatchSet {
  int size;
  ConnectedMatch matches[2];
} ConnectedMatchSet;

// binds the connected segment to s2
void connected_matcher_eos(ModelRoot *model, ConnectedFrame *frame,
                           int startOpIndex, ConnectedMatchSet *results) {
  int match_cntr = 0;

  int loop_bound0 = frame->s1->connected_to_count;
  for (int i0 = 0; i0 < loop_bound0; i0++) {
    frame->s2 = frame->s1->connected_to[i0];
    if (frame->s2 != NULL) {
      results->matches[match_cntr].s1 = frame->s1;
      results->matches[match_cntr++].s2 = frame->s2;
    }
  }

  results->size = match_cntr;
}

void end_of_siding_matcher(ModelRoot *model, EndOfSidingFrame *frame,
                           int startOpIndex, EndOfSidingMatchSet *results) {
  int match_cntr = 0;

  int loop_bound0 = model->train_count;
  for (int i0 = 0; i0 < loop_bound0; i0++) {
    frame->train = model->trains[i0];
    frame->location = frame->train->location;
    if (frame->location != NULL) {
      int loop_bound1 = frame->location->connected_to_count;
      for (int i1 = 0; i1 < loop_bound1; i1++) {
        frame->end = frame->location->connected_to[i1];
        ConnectedFrame connected_vars = {.s1 = frame->end, .s2 = NULL};
        ConnectedMatchSet connected_match_set = {.matches = {NULL}, .size = 0};
        connected_matcher_eos(model, &connected_vars, 0, &connected_match_set);
        if (connected_match_set.size == 1) {
          results->matches[match_cntr].location = frame->location;
          results->matches[match_cntr++].train = frame->train;
        }
      }
    }
  }

  results->size = match_cntr;
}

#endif // OPTIMIZED_QUERIES
