package http_parser.lolevel;

import java.nio.*; import java.util.*;

import primitive.collection.ByteList;

import http_parser.*;

public class Util {

static final ParserSettings SETTINGS_NULL = new ParserSettings();

static String str (ByteBuffer b, int pos, int len) {
  byte [] by = new byte[len];
  int saved = b.position();
  b.position(pos);
  b.get(by);
  b.position(saved);
  return new String(by);
}
static String str (ByteBuffer b) {
  int len = b.limit() - b.position(); 
  byte [] by = new byte[len];
  int saved = b.position();
  b.get(by);
  b.position(saved);
  return new String(by);
}

static ByteBuffer buffer(String str) {
  return ByteBuffer.wrap(str.getBytes());
}

static ByteBuffer empty() {
  return ByteBuffer.wrap(new byte[0]);
}

static void check(boolean betterBtrue) {
  if (!betterBtrue) {
    throw new RuntimeException("!");
  }
}
static void check (int should, int is) {
  if (should != is) {
    throw new RuntimeException("should be: "+should+" is:"+is);
  }
}

static void test_message(Message mes) {
  int raw_len = mes.raw.length;
  for (int msg1len = 0; msg1len != raw_len; ++msg1len) {
    mes.reset();
    ByteBuffer msg1 = ByteBuffer.wrap(mes.raw, 0, msg1len);
    ByteBuffer msg2 = ByteBuffer.wrap(mes.raw, msg1len, mes.raw.length - msg1len);

    HTTPParser parser = new HTTPParser(mes.type);
    ParserSettings settings = mes.settings();

    int read = 0;
    if (msg1len !=0) {
      read = parser.execute(settings, msg1);
      if (mes.upgrade() && parser.upgrade) {
        // Messages have a settings() that checks itself...
        check(1 == mes.num_called);
        continue; 
      }
      check(read == msg1len);
    }

    read = parser.execute(settings, msg2);
    if (mes.upgrade() && parser.upgrade) {
      check(1 == mes.num_called);
      continue; 
    }

    check( mes.raw.length - msg1len, read);

    ByteBuffer empty = Util.empty();
    read = parser.execute(settings, empty);

    if (mes.upgrade() && parser.upgrade) {
      check(1 == mes.num_called);
      continue;
    }
    check(empty.position() == empty.limit());
    check(0 == read);
    check(1 == mes.num_called);

  }
}

static void test_multiple3(Message r1, Message r2, Message r3) {
  int message_count = 1;
  if (!r1.upgrade()) {
    message_count++;
    if (!r2.upgrade()) {
      message_count++;
    }
  }
  boolean has_upgrade = (message_count < 3 || r3.upgrade());

  ByteList blist = new ByteList();
  blist.addAll(r1.raw);
  blist.addAll(r2.raw);
  blist.addAll(r3.raw);

  byte [] raw = blist.toArray();
  ByteBuffer buf   = ByteBuffer.wrap(raw);

  Util.Settings settings = Util.settings(); 
  HTTPParser parser = new HTTPParser(r1.type);

  int read = parser.execute(settings, buf);
  if (has_upgrade && parser.upgrade) {
    raw = upgrade_message_fix(raw, read, r1,r2,r3);
    check(settings.numCalled == message_count); 
    return;
  }

  check(read == raw.length);

  buf = Util.empty();    
  read = parser.execute(settings, buf);
  if (has_upgrade && parser.upgrade) {
    check(settings.numCalled == message_count); 
    return;
  }

  check(0 == read);
  check(settings.numCalled == message_count);
}  

   Given a sequence of bytes and the number of these that we were able to
   parse, verify that upgrade bodies are correct.

static byte [] upgrade_message_fix(byte[] body, int nread, Message... msgs) {
  int off = 0;
  for (Message m : msgs) {
    off += m.raw.length;
    if (m.upgrade()) {
      off -= m.upgrade.length;
      // Original C:
      //     Check the portion of the response after its specified upgrade 
      //     if (!check_str_eq(m, "upgrade", body + off, body + nread)) {
      //       abort();
      //     }
      // to me, this seems to be equivalent to comparing off and nread ...
      check (off, nread);

      // Original C:
      //   Fix up the response so that message_eq() will verify the beginning
      //   of the upgrade   
      // 
      //   *(body + nread + strlen(m->upgrade)) = '\0';
      // This only shortens body so the strlen check passes.
      return new byte[off];

    }
  }
  return null;
}

//upgrade_message_fix(char *body, const size_t nread, const size_t nmsgs, …) { // va_list ap; // size_t i; // size_t off = 0; // // va_start(ap, nmsgs); // // for (i = 0; i < nmsgs; i++) { // struct message *m = va_arg(ap, struct message *); // // off += strlen(m->raw); // // if (m->upgrade) { // off -= strlen(m->upgrade); // // Check the portion of the response after its specified upgrade */ // if (!check_str_eq(m, “upgrade”, body + off, body + nread)) { // abort(); // } // // /* Fix up the response so that message_eq() will verify the beginning // * of the upgrade */ // *(body + nread + strlen(m->upgrade)) = '0'; // messages[num_messages -1 ].upgrade = body + nread; // // va_end(ap); // return; // } // } // // va_end(ap); // printf(“nn*** Error: expected a message with upgrade ***n”); // // abort(); //}

static void p (Object o) {
  System.out.println(o);
}

static Settings settings() {
  return new Settings();
}
static Message find(List<Message> list, String name) {
  for (Message m : list) {
    if (name.equals(m.name)) {
      return m;
    }
  }
  return null;
}

static class Settings extends ParserSettings {
  public int numCalled;
  public int bodyCount;
  Settings() {
    this.on_message_complete = new HTTPCallback() {
      public int cb (HTTPParser parser) {
        numCalled++;
        return 0;
      }
    };
    this.on_body = new HTTPDataCallback() {
      public int cb (HTTPParser p, ByteBuffer b, int pos, int len) {
        bodyCount += len;
        return 0;
      }
    }; 
  }

  int numCalled () {
    return this.numCalled;
  }
}

}