// This file is part of mongoose web server project,
// https://github.com/cesanta/mongoose

using System;
using System.Runtime.InteropServices;

[StructLayout(LayoutKind.Sequential)] public struct MongooseHeader {
  [MarshalAs(UnmanagedType.LPTStr)] public IntPtr name;
  [MarshalAs(UnmanagedType.LPTStr)] public IntPtr value;
};

// mongoose.h :: struct mg_request_info
[StructLayout(LayoutKind.Sequential)] public struct MongooseRequestInfo {
  [MarshalAs(UnmanagedType.LPTStr)] public string request_method;
  [MarshalAs(UnmanagedType.LPTStr)] public string uri;
  [MarshalAs(UnmanagedType.LPTStr)] public string http_version;
  [MarshalAs(UnmanagedType.LPTStr)] public string query_string;
  [MarshalAs(UnmanagedType.LPTStr)] public string remote_user;
  public int remote_ip;
  public int remote_port;
  public int is_ssl;
  [MarshalAs(UnmanagedType.ByValArray,SizeConst=64)]
    public MongooseHeader[] http_headers;
};

[StructLayout(LayoutKind.Sequential)] public struct MongooseEvent {
  public int type;
  public IntPtr user_data;
  public IntPtr conn_data;
  public IntPtr event_param;
  public IntPtr conn;
  public IntPtr request_info;
};

public delegate int MongooseEventHandlerN(ref MongooseEvent ev);
public delegate int MongooseEventHandler(MongooseEvent ev);

public class Mongoose {
  public const string dll_name_ = "mongoose";
  public string version_ = "??";

  // These are here to store a ref to the callbacks
  // while they are over in unmanaged code, to prevent garbage collection.
  private event MongooseEventHandlerN delegates;

  private IntPtr ctx_;

  [DllImport(dll_name_)] private static extern
    IntPtr mg_start([MarshalAs(UnmanagedType.LPArray,
                     ArraySubType=UnmanagedType.LPTStr)] string[] options,
                    MongooseEventHandlerN callback,
                    IntPtr user_data);
  [DllImport(dll_name_)] private static extern void mg_stop(IntPtr ctx);
  [DllImport(dll_name_)] private static extern IntPtr mg_version();
  [DllImport(dll_name_)] public static extern int mg_write(IntPtr conn,
      string data, int length);

  public Mongoose(string document_root,
                  string listening_ports,
                  MongooseEventHandler event_handler) {
    version_ = Marshal.PtrToStringAnsi(mg_version());

    string[] options = {
      "document_root", document_root,
      "listening_ports", listening_ports,
      null
    };

    MongooseEventHandlerN cb = delegate(ref MongooseEvent ev) {
      return event_handler(ev);
    };

    // Prevent garbage collection
    delegates += cb;

    ctx_ = mg_start(options, cb, IntPtr.Zero);
  }

  public static int write(IntPtr conn, string data) {
    return mg_write(conn, data, data.Length);
  }

  public void stop() {
    if (this.ctx_ != IntPtr.Zero) {
      mg_stop(this.ctx_);
    }
    this.ctx_ = IntPtr.Zero;
  }

  ~Mongoose() {
    stop();
  }
}
