package http_parser;

import primitive.collection.ByteList;

public class ParserSettings extends http_parser.lolevel.ParserSettings {

public HTTPCallback       on_message_begin;
public HTTPDataCallback   on_path;
public HTTPDataCallback   on_query_string;
public HTTPDataCallback   on_url;
public HTTPDataCallback   on_fragment;
public HTTPCallback       on_status_complete;
public HTTPDataCallback   on_header_field;
public HTTPDataCallback   on_header_value;

public HTTPCallback       on_headers_complete;
public HTTPDataCallback   on_body;
public HTTPCallback       on_message_complete;

public HTTPErrorCallback  on_error;

private HTTPCallback      _on_message_begin;
private HTTPDataCallback  _on_path;
private HTTPDataCallback  _on_query_string;
private HTTPDataCallback  _on_url;
private HTTPDataCallback  _on_fragment;
private HTTPCallback      _on_status_complete;
private HTTPDataCallback  _on_header_field;
private HTTPDataCallback  _on_header_value;
private HTTPCallback      _on_headers_complete;
private HTTPDataCallback  _on_body;
private HTTPCallback      _on_message_complete;
private HTTPErrorCallback _on_error;

private http_parser.lolevel.ParserSettings settings;

protected ByteList field = new ByteList();
protected ByteList value = new ByteList();
protected ByteList body  = new ByteList();

public ParserSettings() {
  this.settings = new http_parser.lolevel.ParserSettings();
  createMirrorCallbacks();
  attachCallbacks();
}

protected http_parser.lolevel.ParserSettings getLoLevelSettings() {
  return this.settings;
}

private void createMirrorCallbacks() {
  this._on_message_begin = new HTTPCallback() {
    public int cb(HTTPParser p) {
      if (null != ParserSettings.this.on_message_begin) {
        return ParserSettings.this.on_message_begin.cb(p);
      }
      return 0;
    }
  };
  this._on_path = new HTTPDataCallback() {
    @Override
    public int cb(HTTPParser p, byte[] by, int pos, int len) {
      if (null != ParserSettings.this.on_path) {
        return ParserSettings.this.on_path.cb(p, by, pos, len);
      }
      return 0;
    }
  };
  this._on_query_string = new HTTPDataCallback() {
    @Override
    public int cb(HTTPParser p, byte[] by, int pos, int len) {
      if (null != ParserSettings.this.on_query_string) {
        return ParserSettings.this.on_query_string.cb(p, by, pos, len);
      }
      return 0;
    }
  };
  this._on_url = new HTTPDataCallback() {
    @Override
    public int cb(HTTPParser p, byte[] by, int pos, int len) {
      if (null != ParserSettings.this.on_url) {
        return ParserSettings.this.on_url.cb(p, by, pos, len);
      }
      return 0;
    }
  };
  this._on_fragment = new HTTPDataCallback() {
    @Override
    public int cb(HTTPParser p, byte[] by, int pos, int len) {
      if (null != ParserSettings.this.on_fragment) {
        return ParserSettings.this.on_fragment.cb(p, by, pos, len);
      }
      return 0;
    }
  };
  this._on_status_complete = new HTTPCallback() {
    @Override
    public int cb(HTTPParser p) {
      if (null != ParserSettings.this.on_status_complete) {
        return ParserSettings.this.on_status_complete.cb(p);
      }
      return 0;
    }
  };
  this._on_error = new HTTPErrorCallback() {
    @Override
    public void cb(HTTPParser parser, String error) {
      if (null != ParserSettings.this.on_error) {
        ParserSettings.this.on_error.cb(parser, error);
      } else {
        throw new HTTPException(error);
      }

    }
  };

// (on_header_field and on_header_value shortened to on_h_*) // ———————— ———— ——————————————– // | State (prev. callback) | Callback | Description/action | // ———————— ———— ——————————————– // | nothing (first call) | on_h_field | Allocate new buffer and copy callback data | // | | | into it | // ———————— ———— ——————————————– // | value | on_h_field | New header started. | // | | | Copy current name,value buffers to headers | // | | | list and allocate new buffer for new name | // ———————— ———— ——————————————– // | field | on_h_field | Previous name continues. Reallocate name | // | | | buffer and append callback data to it | // ———————— ———— ——————————————– // | field | on_h_value | Value for current header started. Allocate | // | | | new buffer and copy callback data to it | // ———————— ———— ——————————————– // | value | on_h_value | Value continues. Reallocate value buffer | // | | | and append callback data to it | // ———————— ———— ——————————————–

  this._on_header_field = new HTTPDataCallback() {
    @Override
    public int cb(HTTPParser p, byte[] by, int pos, int len) {
      // previous value complete, call on_value with full value, reset value.
      if (0 != ParserSettings.this.value.size()) {
        // check we're even interested...
        if (null != ParserSettings.this.on_header_value) {
          byte [] valueArr = ParserSettings.this.value.toArray();
          int ret = ParserSettings.this.on_header_value.cb(p, valueArr, 0, valueArr.length);
          if (0 != ret) {
            return ret;
          }
          ParserSettings.this.value.clear();
        }
      }

      if (null == ParserSettings.this.on_header_field) {
        return 0;
      }

      ParserSettings.this.field.addAll(by);
      return 0;
    }
  };
  this._on_header_value = new HTTPDataCallback() {
    @Override
    public int cb(HTTPParser p, byte[] by, int pos, int len) {

      // previous field complete, call on_field with full field value, reset field.
      if (0 != ParserSettings.this.field.size()) {
        // check we're even interested...
        if (null != ParserSettings.this.on_header_field) {
          byte [] fieldArr = ParserSettings.this.field.toArray();
          int ret = ParserSettings.this.on_header_field.cb(p, fieldArr, 0, fieldArr.length);
          if (0 != ret) {
            return ret;
          }
          ParserSettings.this.field.clear();
        }
      }

      if (null == ParserSettings.this.on_header_value) {
        return 0;
      }
      ParserSettings.this.value.addAll(by);
      return 0;
    }
  };
  this._on_headers_complete = new HTTPCallback() {
    @Override
    public int cb(HTTPParser parser) {
      // is there an uncompleted value ... ?
      if (0 != ParserSettings.this.value.size()) {
        // check we're even interested...
        if (null != ParserSettings.this.on_header_value) {
          byte [] valueArr = ParserSettings.this.value.toArray();
          int ret = ParserSettings.this.on_header_value.cb(parser, valueArr, 0, valueArr.length);
          if (0 != ret) {
            return ret;
          }
          ParserSettings.this.value.clear();
        }
      }
      if (null != ParserSettings.this.on_headers_complete) {
        return ParserSettings.this.on_headers_complete.cb(parser);
      }
      return 0;
    }

  };
  this._on_body = new HTTPDataCallback() {
    @Override
    public int cb(HTTPParser p, byte[] by, int pos, int len) {
      if (null != ParserSettings.this.on_body) {
        ParserSettings.this.body.addAll(by, pos, len);
      }
      return 0;
    }
  };

  this._on_message_complete = new HTTPCallback() {
    @Override
    public int cb(HTTPParser parser) {
      if (null != ParserSettings.this.on_body) {
        byte [] body = ParserSettings.this.body.toArray();
        int ret = ParserSettings.this.on_body.cb(parser, body, 0, body.length);
        if (0!=ret) {
          return ret;
        }
        ParserSettings.this.body.clear();
      }
      if (null != ParserSettings.this.on_message_complete) {
        return ParserSettings.this.on_message_complete.cb(parser);
      }
      return 0;
    }
  };

}

private void attachCallbacks() {
  // these are certainly set, because we mirror them ...
  this.settings.on_message_begin    = this._on_message_begin;
  this.settings.on_path             = this._on_path;
  this.settings.on_query_string     = this._on_query_string;
  this.settings.on_url              = this._on_url;
  this.settings.on_fragment         = this._on_fragment;
  this.settings.on_status_complete  = this._on_status_complete;
  this.settings.on_header_field     = this._on_header_field;
  this.settings.on_header_value     = this._on_header_value;
  this.settings.on_headers_complete = this._on_headers_complete;
  this.settings.on_body             = this._on_body;
  this.settings.on_message_complete = this._on_message_complete;
  this.settings.on_error            = this._on_error;
}

}