#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "sha1.h"

int num_found = 0;
int found[128];

void incr(char* buf) {
  if (*buf == 'z') {
    *buf = 'a';
    incr(buf + 1);
  } else {
    ++*buf;
  }
}

void int32ToChars(char *into, uint32_t i) {
    into[0] = (i >> 24);
    into[1] = (i >> 16) & 0xFF;
    into[2] = (i >> 8) & 0xFF;
    into[3] = i & 0xFF;
}

bool isNoJmp(const char* buf, int len) {
  for (int i = 0; i < len; i++) {
    if (buf[i] == '[' || buf[i] == ']')
      return false;
  }
  return true;
}

void fillFound(char op) {
  fflush(stdout);

  found[op] = 1;
  num_found++;
  if (num_found == 10)
    exit(0);
}

void checkOpenOp(char op, const char* code, const char* buf) {
  if (found[op])
    return;

  if (code[1] == '>' && code[2] == '[' && isNoJmp(code+3, 17)) {
    printf("%c>[ %s\n", op, buf);
    fillFound(op);
  }
}

void checkOpenLR(char op, const char* code, const char* buf) {
  if (found[op])
    return;

  if (code[1] == '[' && isNoJmp(code+2, 18)) {
    printf("%c[ %s\n", op, buf);
    fillFound(op);
  }
}

void checkCloseLR(char op, const char* code, const char* buf) {
  if (found[op+1])
    return;

  if (code[18] == ']' && isNoJmp(code, 18)) {
    printf("]%c %s\n", op, buf);
    fillFound(op+1);
  }
}

void checkCloseBranch(char op, const char* code, const char* buf) {
  if (found[op])
    return;

  if (code[17] == ']' && code[18] == '<' && isNoJmp(code, 17)) {
    printf("]<%c %s\n", op, buf);
    fillFound(op);
  }
}

int main() {
  SHA1Context sha;
  char buf[1025];
  memset(buf, 'a', sizeof(buf));
  buf[1024] = 0;

  for (int i = 0; i < 1000 * 1000 * 1000; i++) {
    SHA1Reset(&sha);
    SHA1Input(&sha, (unsigned char*)buf, 1024);
    SHA1Result(&sha);

    char code[20], *p;
    for (int j = 0; j < 5; j++) {
      int32ToChars(code + j * 4, sha.Message_Digest[j]);
    }

#if 0
    for (int j = 0; j < 20; j++) {
      printf("%d ", code[j]);
    }
    puts("");
#endif

    switch (code[0]) {
      case '+':
        checkOpenOp('+', code, buf);
        break;
      case '-':
        checkOpenOp('-', code, buf);
        break;
      case '>':
        checkOpenLR('>', code, buf);
        break;
      case '<':
        checkOpenLR('<', code, buf);
        break;
      case '.':
        checkOpenOp('.', code, buf);
        break;
      case ',':
        checkOpenOp(',', code, buf);
        break;
      default: {
        switch (code[19]) {
          case '[':
            checkCloseBranch('[', code, buf);
            break;
          case ']':
            checkCloseBranch(']', code, buf);
            break;
          case '<':
            checkCloseLR('<', code, buf);
            break;
          case '>':
            checkCloseLR('>', code, buf);
            break;
        }
        break;
      }
    }

    incr(buf);
  }
}
