#include <stdint.h>
#include <stdlib.h>
#include "el_runtime.h"

el_val_t sem_frame(el_val_t intent, el_val_t subject, el_val_t obj, el_val_t modifiers);
el_val_t sem_frame_lang(el_val_t intent, el_val_t subject, el_val_t obj, el_val_t modifiers, el_val_t lang_code);
el_val_t sem_frame_simple(el_val_t intent, el_val_t subject);
el_val_t sem_frame_obj(el_val_t intent, el_val_t subject, el_val_t obj);
el_val_t sem_intent(el_val_t frame);
el_val_t sem_subject(el_val_t frame);
el_val_t sem_object(el_val_t frame);
el_val_t sem_modifiers(el_val_t frame);
el_val_t sem_lang(el_val_t frame);
el_val_t sem_first_modifier(el_val_t mods);
el_val_t sem_intent_to_realize(el_val_t intent);
el_val_t sem_to_spec(el_val_t frame);
el_val_t sem_to_spec_full(el_val_t frame, el_val_t verb, el_val_t tense, el_val_t aspect);
el_val_t sem_realize_greet(el_val_t subject);
el_val_t sem_realize(el_val_t frame);
el_val_t sem_realize_full(el_val_t frame, el_val_t verb, el_val_t tense, el_val_t aspect);
el_val_t sem_realize_lang(el_val_t frame, el_val_t lang_code);

el_val_t sem_frame(el_val_t intent, el_val_t subject, el_val_t obj, el_val_t modifiers) {
  el_val_t r = native_list_empty();
  r = native_list_append(r, EL_STR("intent"));
  r = native_list_append(r, intent);
  r = native_list_append(r, EL_STR("subject"));
  r = native_list_append(r, subject);
  r = native_list_append(r, EL_STR("object"));
  r = native_list_append(r, obj);
  r = native_list_append(r, EL_STR("modifiers"));
  r = native_list_append(r, modifiers);
  r = native_list_append(r, EL_STR("lang"));
  r = native_list_append(r, EL_STR("en"));
  return r;
  return 0;
}

el_val_t sem_frame_lang(el_val_t intent, el_val_t subject, el_val_t obj, el_val_t modifiers, el_val_t lang_code) {
  el_val_t r = native_list_empty();
  r = native_list_append(r, EL_STR("intent"));
  r = native_list_append(r, intent);
  r = native_list_append(r, EL_STR("subject"));
  r = native_list_append(r, subject);
  r = native_list_append(r, EL_STR("object"));
  r = native_list_append(r, obj);
  r = native_list_append(r, EL_STR("modifiers"));
  r = native_list_append(r, modifiers);
  r = native_list_append(r, EL_STR("lang"));
  r = native_list_append(r, lang_code);
  return r;
  return 0;
}

el_val_t sem_frame_simple(el_val_t intent, el_val_t subject) {
  return sem_frame(intent, subject, EL_STR(""), EL_STR(""));
  return 0;
}

el_val_t sem_frame_obj(el_val_t intent, el_val_t subject, el_val_t obj) {
  return sem_frame(intent, subject, obj, EL_STR(""));
  return 0;
}

el_val_t sem_intent(el_val_t frame) {
  return slots_get(frame, EL_STR("intent"));
  return 0;
}

el_val_t sem_subject(el_val_t frame) {
  return slots_get(frame, EL_STR("subject"));
  return 0;
}

el_val_t sem_object(el_val_t frame) {
  return slots_get(frame, EL_STR("object"));
  return 0;
}

el_val_t sem_modifiers(el_val_t frame) {
  return slots_get(frame, EL_STR("modifiers"));
  return 0;
}

el_val_t sem_lang(el_val_t frame) {
  el_val_t code = slots_get(frame, EL_STR("lang"));
  if (str_eq(code, EL_STR(""))) {
    return EL_STR("en");
  }
  return code;
  return 0;
}

el_val_t sem_first_modifier(el_val_t mods) {
  el_val_t n = str_len(mods);
  if (n == 0) {
    return EL_STR("");
  }
  el_val_t i = 0;
  el_val_t running = 1;
  while (running) {
    if (i >= n) {
      running = 0;
    } else {
      el_val_t c = str_slice(mods, i, (i + 1));
      if (str_eq(c, EL_STR(";"))) {
        running = 0;
      } else {
        i = (i + 1);
      }
    }
  }
  return str_slice(mods, 0, i);
  return 0;
}

el_val_t sem_intent_to_realize(el_val_t intent) {
  if (str_eq(intent, EL_STR("assert"))) {
    return EL_STR("assert");
  }
  if (str_eq(intent, EL_STR("query"))) {
    return EL_STR("question");
  }
  if (str_eq(intent, EL_STR("describe"))) {
    return EL_STR("assert");
  }
  if (str_eq(intent, EL_STR("greet"))) {
    return EL_STR("greet");
  }
  return EL_STR("assert");
  return 0;
}

el_val_t sem_to_spec(el_val_t frame) {
  el_val_t intent = sem_intent(frame);
  el_val_t subject = sem_subject(frame);
  el_val_t obj = sem_object(frame);
  el_val_t mods = sem_modifiers(frame);
  el_val_t lang_code = sem_lang(frame);
  el_val_t location = sem_first_modifier(mods);
  if (str_eq(intent, EL_STR("greet"))) {
    el_val_t spec = native_list_empty();
    spec = native_list_append(spec, EL_STR("intent"));
    spec = native_list_append(spec, EL_STR("greet"));
    spec = native_list_append(spec, EL_STR("agent"));
    spec = native_list_append(spec, subject);
    spec = native_list_append(spec, EL_STR("predicate"));
    spec = native_list_append(spec, EL_STR(""));
    spec = native_list_append(spec, EL_STR("patient"));
    spec = native_list_append(spec, EL_STR(""));
    spec = native_list_append(spec, EL_STR("location"));
    spec = native_list_append(spec, EL_STR(""));
    spec = native_list_append(spec, EL_STR("tense"));
    spec = native_list_append(spec, EL_STR("present"));
    spec = native_list_append(spec, EL_STR("aspect"));
    spec = native_list_append(spec, EL_STR("simple"));
    spec = native_list_append(spec, EL_STR("lang"));
    spec = native_list_append(spec, lang_code);
    return spec;
  }
  if (str_eq(intent, EL_STR("describe"))) {
    el_val_t spec = native_list_empty();
    spec = native_list_append(spec, EL_STR("intent"));
    spec = native_list_append(spec, EL_STR("assert"));
    spec = native_list_append(spec, EL_STR("agent"));
    spec = native_list_append(spec, subject);
    spec = native_list_append(spec, EL_STR("predicate"));
    spec = native_list_append(spec, EL_STR("be"));
    spec = native_list_append(spec, EL_STR("patient"));
    spec = native_list_append(spec, obj);
    spec = native_list_append(spec, EL_STR("location"));
    spec = native_list_append(spec, location);
    spec = native_list_append(spec, EL_STR("tense"));
    spec = native_list_append(spec, EL_STR("present"));
    spec = native_list_append(spec, EL_STR("aspect"));
    spec = native_list_append(spec, EL_STR("simple"));
    spec = native_list_append(spec, EL_STR("lang"));
    spec = native_list_append(spec, lang_code);
    return spec;
  }
  el_val_t realize_intent = sem_intent_to_realize(intent);
  el_val_t spec = native_list_empty();
  spec = native_list_append(spec, EL_STR("intent"));
  spec = native_list_append(spec, realize_intent);
  spec = native_list_append(spec, EL_STR("agent"));
  spec = native_list_append(spec, subject);
  spec = native_list_append(spec, EL_STR("predicate"));
  spec = native_list_append(spec, obj);
  spec = native_list_append(spec, EL_STR("patient"));
  spec = native_list_append(spec, EL_STR(""));
  spec = native_list_append(spec, EL_STR("location"));
  spec = native_list_append(spec, location);
  spec = native_list_append(spec, EL_STR("tense"));
  spec = native_list_append(spec, EL_STR("present"));
  spec = native_list_append(spec, EL_STR("aspect"));
  spec = native_list_append(spec, EL_STR("simple"));
  spec = native_list_append(spec, EL_STR("lang"));
  spec = native_list_append(spec, lang_code);
  return spec;
  return 0;
}

el_val_t sem_to_spec_full(el_val_t frame, el_val_t verb, el_val_t tense, el_val_t aspect) {
  el_val_t intent = sem_intent(frame);
  el_val_t subject = sem_subject(frame);
  el_val_t obj = sem_object(frame);
  el_val_t mods = sem_modifiers(frame);
  el_val_t lang_code = sem_lang(frame);
  el_val_t location = sem_first_modifier(mods);
  if (str_eq(intent, EL_STR("greet"))) {
    return sem_to_spec(frame);
  }
  if (str_eq(intent, EL_STR("describe"))) {
    el_val_t spec = native_list_empty();
    spec = native_list_append(spec, EL_STR("intent"));
    spec = native_list_append(spec, EL_STR("assert"));
    spec = native_list_append(spec, EL_STR("agent"));
    spec = native_list_append(spec, subject);
    spec = native_list_append(spec, EL_STR("predicate"));
    spec = native_list_append(spec, EL_STR("be"));
    spec = native_list_append(spec, EL_STR("patient"));
    spec = native_list_append(spec, obj);
    spec = native_list_append(spec, EL_STR("location"));
    spec = native_list_append(spec, location);
    spec = native_list_append(spec, EL_STR("tense"));
    spec = native_list_append(spec, tense);
    spec = native_list_append(spec, EL_STR("aspect"));
    spec = native_list_append(spec, aspect);
    spec = native_list_append(spec, EL_STR("lang"));
    spec = native_list_append(spec, lang_code);
    return spec;
  }
  el_val_t realize_intent = sem_intent_to_realize(intent);
  el_val_t spec = native_list_empty();
  spec = native_list_append(spec, EL_STR("intent"));
  spec = native_list_append(spec, realize_intent);
  spec = native_list_append(spec, EL_STR("agent"));
  spec = native_list_append(spec, subject);
  spec = native_list_append(spec, EL_STR("predicate"));
  spec = native_list_append(spec, verb);
  spec = native_list_append(spec, EL_STR("patient"));
  spec = native_list_append(spec, obj);
  spec = native_list_append(spec, EL_STR("location"));
  spec = native_list_append(spec, location);
  spec = native_list_append(spec, EL_STR("tense"));
  spec = native_list_append(spec, tense);
  spec = native_list_append(spec, EL_STR("aspect"));
  spec = native_list_append(spec, aspect);
  spec = native_list_append(spec, EL_STR("lang"));
  spec = native_list_append(spec, lang_code);
  return spec;
  return 0;
}

el_val_t sem_realize_greet(el_val_t subject) {
  if (str_eq(subject, EL_STR(""))) {
    return EL_STR("Hello.");
  }
  return el_str_concat(el_str_concat(EL_STR("Hello, "), subject), EL_STR("."));
  return 0;
}

el_val_t sem_realize(el_val_t frame) {
  el_val_t intent = sem_intent(frame);
  if (str_eq(intent, EL_STR("greet"))) {
    return sem_realize_greet(sem_subject(frame));
  }
  el_val_t spec = sem_to_spec(frame);
  return realize(spec);
  return 0;
}

el_val_t sem_realize_full(el_val_t frame, el_val_t verb, el_val_t tense, el_val_t aspect) {
  el_val_t intent = sem_intent(frame);
  if (str_eq(intent, EL_STR("greet"))) {
    return sem_realize_greet(sem_subject(frame));
  }
  el_val_t spec = sem_to_spec_full(frame, verb, tense, aspect);
  return realize(spec);
  return 0;
}

el_val_t sem_realize_lang(el_val_t frame, el_val_t lang_code) {
  el_val_t intent = sem_intent(frame);
  if (str_eq(intent, EL_STR("greet"))) {
    return sem_realize_greet(sem_subject(frame));
  }
  el_val_t patched = slots_set(frame, EL_STR("lang"), lang_code);
  el_val_t spec = sem_to_spec(patched);
  return realize(spec);
  return 0;
}

