package hodu

import "encoding/json"
import "fmt"
import "net/http"
import "net/url"
import "runtime"
import "strconv"
import "time"
import "unsafe"

/*
 *                       POST                 GET            PUT            DELETE
 * /servers -     create new server     list all servers    bulk update     delete all servers
 * /servers/1 -        X             get server 1 details  update server 1  delete server 1
 * /servers/1/xxx -
 *
 * /servers/1112123/peers
 *   POST add a new peer to a server
 *   GET list all peers
 *   PUT create/replace
 *   PATCH partial update
 * /servers/1112123/peers/1231344
 *   GET get info
 */

type json_errmsg struct {
	Text string `json:"error-text"`
}

type json_in_client_conn struct {
	ServerAddrs []string `json:"server-addrs"`
}

type json_in_client_route struct {
	Id RouteId `json:"id"` // 0 for auto-assignement.
	ClientPeerAddr string `json:"client-peer-addr"`
	ClientPeerName string `json:"client-peer-name"`
	ServerPeerOption string `json:"server-peer-option"`
	ServerPeerServiceAddr string `json:"server-peer-service-addr"` // desired listening address on the server side
	ServerPeerServiceNet string `json:"server-peer-service-net"` // permitted network in prefix notation
	Lifetime string `json:"lifetime"`
}

type json_in_client_route_update struct {
	Lifetime string `sjon:"lifetime"`
}

type json_out_client_conn_id struct {
	Id ConnId `json:"id"`
}

type json_out_client_conn struct {
	Id ConnId `json:"id"`
	ReqServerAddrs []string `json:"req-server-addrs"` // server addresses requested. may include a domain name
	CurrentServerIndex int `json:"current-server-index"`
	ServerAddr string `json:"server-addr"` // actual server address
	ClientAddr string `json:"client-addr"`
	Routes []json_out_client_route `json:"routes"`
}

type json_out_client_route_id struct {
	Id    RouteId `json:"id"`
	CtsId ConnId  `json:"conn-id"`
}

type json_out_client_route struct {
	Id RouteId `json:"id"`
	ClientPeerAddr string `json:"client-peer-addr"`
	ClientPeerName string `json:"client-peer-name"`
	ServerPeerOption string `json:"server-peer-option"`
	ServerPeerListenAddr string `json:"server-peer-service-addr"`
	ServerPeerNet string `json:"server-peer-service-net"`
	Lifetime string `json:"lifetime"`
	LifetimeStart int64 `json:"lifetime-start"`
}

type json_out_client_peer struct {
	Id PeerId `json:"id"`
	ClientPeerAddr string `json:"client-peer-addr"`
	ClientLocalAddr string `json:"client-local-addr"`
	ServerPeerAddr string `json:"server-peer-addr"`
	ServerLocalAddr string `json:"server-local-addr"`
}

type json_out_client_stats struct {
	CPUs int `json:"cpus"`
	Goroutines int `json:"goroutines"`

	NumGCs uint32 `json:"num-gcs"`
	HeapAllocBytes uint64 `json:"memory-alloc-bytes"`
	MemAllocs uint64 `json:"memory-num-allocs"`
	MemFrees uint64 `json:"memory-num-frees"`

	ClientConns int64 `json:"client-conns"`
	ClientRoutes int64 `json:"client-routes"`
	ClientPeers int64 `json:"client-peers"`
}
// ------------------------------------

type client_ctl struct {
	c *Client
	id string
}

type client_ctl_client_conns struct {
	client_ctl
	//c *Client
	//id string
}

type client_ctl_client_conns_id struct {
	client_ctl
}

type client_ctl_client_conns_id_routes struct {
	client_ctl
}

type client_ctl_client_conns_id_routes_id struct {
	client_ctl
}

type client_ctl_client_conns_id_routes_id_peers struct {
	client_ctl
}

type client_ctl_client_conns_id_routes_id_peers_id struct {
	client_ctl
}

type client_ctl_stats struct {
	client_ctl
}

// ------------------------------------

func (ctl *client_ctl) GetId() string {
	return ctl.id
}

// ------------------------------------

func (ctl *client_ctl_client_conns) ServeHTTP(w http.ResponseWriter, req *http.Request) (int, error) {
	var c *Client
	var status_code int
	var err error
	var je *json.Encoder

	defer func() {
		var err interface{} = recover()
		if err != nil { dump_call_frame_and_exit(ctl.c.log, req, err) }
	}()

	c = ctl.c
	je = json.NewEncoder(w)

	switch req.Method {
		case http.MethodGet:
			var cts *ClientConn
			var js []json_out_client_conn
			var q url.Values

			q = req.URL.Query()

// TODO: brief listing vs full listing
			if q.Get("brief") == "true" {
			}

			js = make([]json_out_client_conn, 0)
			c.cts_mtx.Lock()
			for _, cts = range c.cts_map {
				var r *ClientRoute
				var jsp []json_out_client_route

				jsp = make([]json_out_client_route, 0)
				cts.route_mtx.Lock()
				for _, r = range cts.route_map {
					jsp = append(jsp, json_out_client_route{
						Id: r.id,
						ClientPeerAddr: r.peer_addr,
						ClientPeerName: r.peer_name,
						ServerPeerListenAddr: r.server_peer_listen_addr.String(),
						ServerPeerNet: r.server_peer_net,
						ServerPeerOption: r.server_peer_option.string(),
						Lifetime: fmt.Sprintf("%.09f", r.lifetime.Seconds()),
						LifetimeStart: r.lifetime_start.Unix(),
					})
				}
				js = append(js, json_out_client_conn{
					Id: cts.id,
					ReqServerAddrs: cts.cfg.ServerAddrs,
					CurrentServerIndex: cts.cfg.Index,
					ServerAddr: cts.remote_addr,
					ClientAddr: cts.local_addr,
					Routes: jsp,
				})
				cts.route_mtx.Unlock()
			}
			c.cts_mtx.Unlock()

			status_code = http.StatusOK; w.WriteHeader(status_code)
			if err = je.Encode(js); err != nil { goto oops }

		case http.MethodPost:
			// add a new client connection to a server with no client-side peers
			// while the client code can accept one more more client-side peer address
			// with the server added, i don't intentially accept peer addresses
			// as it's tricky to handle erroneous cases in creating the client routes
			// after hacing connected to the server. therefore, the json_in_client_conn
			// type contains a server address field only.
			var s json_in_client_conn
			var cc ClientConfig
			var cts *ClientConn

			err = json.NewDecoder(req.Body).Decode(&s)
			if err != nil || len(s.ServerAddrs) <= 0 {
				status_code = http.StatusBadRequest; w.WriteHeader(status_code)
				goto done
			}

			cc.ServerAddrs = s.ServerAddrs
			//cc.PeerAddrs = s.PeerAddrs
			cts, err = c.start_service(&cc) // TODO: this can be blocking. do we have to resolve addresses before calling this? also not good because resolution succeed or fail at each attempt.  however ok as ServeHTTP itself is in a goroutine?
			if err != nil {
				status_code = http.StatusInternalServerError; w.WriteHeader(status_code)
				if err = je.Encode(json_errmsg{Text: err.Error()}); err != nil { goto oops }
			} else {
				status_code = http.StatusCreated; w.WriteHeader(status_code)
				if err = je.Encode(json_out_client_conn_id{Id: cts.id}); err != nil { goto oops }
			}

		case http.MethodDelete:
			// delete all client connections to servers. if we request to stop all
			// client connections, they will remove themselves from the client.
			// we do passive deletion rather than doing active deletion by calling
			// c.RemoveAllClientConns()
			c.ReqStopAllClientConns()
			status_code = http.StatusNoContent; w.WriteHeader(status_code)

		default:
			status_code = http.StatusBadRequest; w.WriteHeader(status_code)
	}

done:
	return status_code, nil

oops:
	return status_code, err
}

// ------------------------------------

// client-conns/{conn_id}
func (ctl *client_ctl_client_conns_id) ServeHTTP(w http.ResponseWriter, req *http.Request) (int, error) {
	var c *Client
	var status_code int
	var err error
	var conn_id string
	var conn_nid uint64
	var je *json.Encoder
	var cts *ClientConn

	defer func() {
		var err interface{} = recover()
		if err != nil { dump_call_frame_and_exit(ctl.c.log, req, err) }
	}()

	c = ctl.c
	je = json.NewEncoder(w)

	conn_id = req.PathValue("conn_id")

	conn_nid, err = strconv.ParseUint(conn_id, 10, int(unsafe.Sizeof(ConnId(0)) * 8))
	if err != nil {
		status_code = http.StatusBadRequest; w.WriteHeader(status_code)
		if err = je.Encode(json_errmsg{Text: "wrong connection id - " + conn_id}); err != nil { goto oops }
		goto done
	}

	cts = c.FindClientConnById(ConnId(conn_nid))
	if cts == nil {
		status_code = http.StatusNotFound; w.WriteHeader(status_code)
		if err = je.Encode(json_errmsg{Text: "non-existent connection id - " + conn_id}); err != nil { goto oops }
		goto done
	}

	switch req.Method {
		case http.MethodGet:
			var r *ClientRoute
			var jsp []json_out_client_route
			var js *json_out_client_conn

			jsp = make([]json_out_client_route, 0)
			cts.route_mtx.Lock()
			for _, r = range cts.route_map {
				jsp = append(jsp, json_out_client_route{
					Id: r.id,
					ClientPeerAddr: r.peer_addr,
					ClientPeerName: r.peer_name,
					ServerPeerListenAddr: r.server_peer_listen_addr.String(),
					ServerPeerNet: r.server_peer_net,
					ServerPeerOption: r.server_peer_option.string(),
					Lifetime: fmt.Sprintf("%.09f", r.lifetime.Seconds()),
					LifetimeStart: r.lifetime_start.Unix(),
				})
			}
			js = &json_out_client_conn{
				Id: cts.id,
				ReqServerAddrs: cts.cfg.ServerAddrs,
				CurrentServerIndex: cts.cfg.Index,
				ServerAddr: cts.local_addr,
				ClientAddr: cts.remote_addr,
				Routes: jsp,
			}
			cts.route_mtx.Unlock()

			status_code = http.StatusOK; w.WriteHeader(status_code)
			if err = je.Encode(js); err != nil { goto oops }

		case http.MethodDelete:
			//c.RemoveClientConn(cts)
			cts.ReqStop()
			status_code = http.StatusNoContent; w.WriteHeader(status_code)

		default:
			status_code = http.StatusBadRequest; w.WriteHeader(status_code)
	}

done:
	return status_code, nil

oops:
	return status_code, err
}

// ------------------------------------

func (ctl *client_ctl_client_conns_id_routes) ServeHTTP(w http.ResponseWriter, req *http.Request) (int, error) {
	var c *Client
	var status_code int
	var err error
	var conn_id string
	var conn_nid uint64
	var je *json.Encoder
	var cts *ClientConn

	defer func() {
		var err interface{} = recover()
		if err != nil { dump_call_frame_and_exit(ctl.c.log, req, err) }
	}()

	c = ctl.c
	je = json.NewEncoder(w)

	conn_id = req.PathValue("conn_id")

	conn_nid, err = strconv.ParseUint(conn_id, 10, int(unsafe.Sizeof(ConnId(0)) * 8))
	if err != nil {
		status_code = http.StatusBadRequest; w.WriteHeader(status_code)
		if err = je.Encode(json_errmsg{Text: "wrong connection id - " + conn_id }); err != nil { goto oops }
		goto done
	}

	cts = c.FindClientConnById(ConnId(conn_nid))
	if cts == nil {
		status_code = http.StatusNotFound; w.WriteHeader(status_code)
		if err = je.Encode(json_errmsg{Text: "non-existent connection id - " + conn_id}); err != nil { goto oops }
		goto done
	}

	switch req.Method {
		case http.MethodGet:
			var r *ClientRoute
			var jsp []json_out_client_route

			jsp = make([]json_out_client_route, 0)
			cts.route_mtx.Lock()
			for _, r = range cts.route_map {
				jsp = append(jsp, json_out_client_route{
					Id: r.id,
					ClientPeerAddr: r.peer_addr,
					ClientPeerName: r.peer_name,
					ServerPeerListenAddr: r.server_peer_listen_addr.String(),
					ServerPeerNet: r.server_peer_net,
					ServerPeerOption: r.server_peer_option.string(),
					Lifetime: fmt.Sprintf("%.09f", r.lifetime.Seconds()),
					LifetimeStart: r.lifetime_start.Unix(),
				})
			}
			cts.route_mtx.Unlock()

			status_code = http.StatusOK; w.WriteHeader(status_code)
			if err = je.Encode(jsp); err != nil { goto oops }

		case http.MethodPost:
			var jcr json_in_client_route
			var r *ClientRoute
			var rc *ClientRouteConfig
			var server_peer_option RouteOption
			var lifetime time.Duration

			err = json.NewDecoder(req.Body).Decode(&jcr)
			if err != nil {
				status_code = http.StatusBadRequest; w.WriteHeader(status_code)
				goto oops
			}

			if jcr.ClientPeerAddr == "" {
				status_code = http.StatusBadRequest; w.WriteHeader(status_code)
				err = fmt.Errorf("blank client-peer-addr")
				goto oops
			}

			server_peer_option = string_to_route_option(jcr.ServerPeerOption)
			if server_peer_option == RouteOption(ROUTE_OPTION_UNSPEC) {
				status_code = http.StatusBadRequest; w.WriteHeader(status_code)
				err = fmt.Errorf("wrong server-peer-option value - %s", server_peer_option)
				goto oops
			}

			lifetime, err = parse_duration_string(jcr.Lifetime)
			if err != nil {
				status_code = http.StatusBadRequest; w.WriteHeader(status_code)
				err = fmt.Errorf("wrong lifetime value %s - %s", jcr.Lifetime, err.Error())
				goto oops
			}

			rc = &ClientRouteConfig{
				Id: jcr.Id,
				PeerAddr: jcr.ClientPeerAddr,
				PeerName: jcr.ClientPeerName,
				Option: server_peer_option,
				ServiceAddr: jcr.ServerPeerServiceAddr,
				ServiceNet: jcr.ServerPeerServiceNet,
				Lifetime: lifetime,
			}

			//cts.AddClientRouteConfig(rc) // TODO: this is to remember... but how to delete it?
			r, err = cts.AddNewClientRoute(rc)
			if err != nil {
				status_code = http.StatusInternalServerError; w.WriteHeader(status_code)
				if err = je.Encode(json_errmsg{Text: err.Error()}); err != nil { goto oops }
			} else {
				status_code = http.StatusCreated; w.WriteHeader(status_code)
				if err = je.Encode(json_out_client_route_id{Id: r.id, CtsId: r.cts.id}); err != nil { goto oops }
			}

		case http.MethodDelete:
			//cts.RemoveAllClientRoutes()
			cts.ReqStopAllClientRoutes()
			status_code = http.StatusNoContent; w.WriteHeader(status_code)

		default:
			status_code = http.StatusBadRequest; w.WriteHeader(status_code)
	}

done:
	return status_code, nil

oops:
	return status_code, err
}

// ------------------------------------

func (ctl *client_ctl_client_conns_id_routes_id) ServeHTTP(w http.ResponseWriter, req *http.Request) (int, error) {
	var c *Client
	var status_code int
	var err error
	var conn_id string
	var route_id string
	var conn_nid uint64
	var route_nid uint64
	var je *json.Encoder
	var cts *ClientConn
	var r *ClientRoute

	defer func() {
		var err interface{} = recover()
		if err != nil { dump_call_frame_and_exit(ctl.c.log, req, err) }
	}()

	c = ctl.c
	je = json.NewEncoder(w)

	conn_id = req.PathValue("conn_id")
	route_id = req.PathValue("route_id")

	conn_nid, err = strconv.ParseUint(conn_id, 10, int(unsafe.Sizeof(ConnId(0)) * 8))
	if err != nil {
		status_code = http.StatusBadRequest; w.WriteHeader(status_code)
		if err = je.Encode(json_errmsg{Text: "wrong connection id - " + conn_id}); err != nil { goto oops }
		goto done
	}
	route_nid, err = strconv.ParseUint(route_id, 10, int(unsafe.Sizeof(RouteId(0)) * 8))
	if err != nil {
		status_code = http.StatusBadRequest; w.WriteHeader(status_code)
		if err = je.Encode(json_errmsg{Text: "wrong route id - " + route_id}); err != nil { goto oops }
		goto done
	}

	cts = c.FindClientConnById(ConnId(conn_nid))
	if cts == nil {
		status_code = http.StatusNotFound; w.WriteHeader(status_code)
		if err = je.Encode(json_errmsg{Text: "non-existent connection id - " + conn_id}); err != nil { goto oops }
		goto done
	}

	r = cts.FindClientRouteById(RouteId(route_nid))
	if r == nil {
		status_code = http.StatusNotFound; w.WriteHeader(status_code)
		if err = je.Encode(json_errmsg{Text: "non-existent route id - " + conn_id}); err != nil { goto oops }
		goto done
	}

	switch req.Method {
		case http.MethodGet:
			status_code = http.StatusOK; w.WriteHeader(status_code)
			err = je.Encode(json_out_client_route{
				Id: r.id,
				ClientPeerAddr: r.peer_addr,
				ClientPeerName: r.peer_name,
				ServerPeerListenAddr: r.server_peer_listen_addr.String(),
				ServerPeerNet: r.server_peer_net,
				ServerPeerOption: r.server_peer_option.string(),
				Lifetime: r.lifetime.String(),
			})
			if err != nil { goto oops }

		case http.MethodPut:
			var jcr json_in_client_route_update
			var lifetime time.Duration

			err = json.NewDecoder(req.Body).Decode(&jcr)
			if err != nil {
				status_code = http.StatusBadRequest; w.WriteHeader(status_code)
				goto oops
			}

			lifetime, err = parse_duration_string(jcr.Lifetime)
			if err != nil {
				status_code = http.StatusBadRequest; w.WriteHeader(status_code)
				err = fmt.Errorf("wrong lifetime value %s - %s", jcr.Lifetime, err.Error())
				goto oops
			}


			err = r.ResetLifetime(lifetime)
			if err != nil { goto oops }

		case http.MethodDelete:
			r.ReqStop()
			status_code = http.StatusNoContent; w.WriteHeader(status_code)

		default:
			status_code = http.StatusBadRequest; w.WriteHeader(status_code)
	}

done:
	return status_code, nil

oops:
	return status_code, err
}

// ------------------------------------

func (ctl *client_ctl_client_conns_id_routes_id_peers) ServeHTTP(w http.ResponseWriter, req *http.Request) (int, error) {
	var c *Client
	var status_code int
	var err error
	var conn_id string
	var route_id string
	var conn_nid uint64
	var route_nid uint64
	var je *json.Encoder
	var r *ClientRoute

	defer func() {
		var err interface{} = recover()
		if err != nil { dump_call_frame_and_exit(ctl.c.log, req, err) }
	}()

	c = ctl.c
	je = json.NewEncoder(w)

	conn_id = req.PathValue("conn_id")
	route_id = req.PathValue("route_id")

	conn_nid, err = strconv.ParseUint(conn_id, 10, int(unsafe.Sizeof(ConnId(0)) * 8))
	if err != nil {
		status_code = http.StatusBadRequest; w.WriteHeader(status_code)
		if err = je.Encode(json_errmsg{Text: "wrong connection id - " + conn_id}); err != nil { goto oops }
		goto done
	}
	route_nid, err = strconv.ParseUint(route_id, 10, int(unsafe.Sizeof(RouteId(0)) * 8))
	if err != nil {
		status_code = http.StatusBadRequest; w.WriteHeader(status_code)
		if err = je.Encode(json_errmsg{Text: "wrong route id - " + route_id}); err != nil { goto oops }
		goto done
	}

	r = c.FindClientRouteById(ConnId(conn_nid), RouteId(route_nid))
	if r == nil {
		status_code = http.StatusNotFound; w.WriteHeader(status_code)
		if err = je.Encode(json_errmsg{Text: "non-existent connection/route id - " + conn_id + "/" + route_id}); err != nil { goto oops }
		goto done
	}

	switch req.Method {
		case http.MethodGet:
			var p *ClientPeerConn
			var jcp []json_out_client_peer

			jcp = make([]json_out_client_peer, 0)
			r.ptc_mtx.Lock()
			for _, p = range r.ptc_map {
				jcp = append(jcp, json_out_client_peer{
					Id: p.conn_id,
					ClientPeerAddr: p.conn.RemoteAddr().String(),
					ClientLocalAddr: p.conn.LocalAddr().String(),
					ServerPeerAddr: p.pts_raddr,
					ServerLocalAddr: p.pts_laddr,
				})
			}
			r.ptc_mtx.Unlock()

			status_code = http.StatusOK; w.WriteHeader(status_code)
			if err = je.Encode(jcp); err != nil { goto oops }

		case http.MethodDelete:
			r.ReqStopAllClientPeerConns()
			status_code = http.StatusNoContent; w.WriteHeader(status_code)

		default:
			status_code = http.StatusBadRequest; w.WriteHeader(status_code)
	}

done:
	return status_code, nil

oops:
	return status_code, err
}

// ------------------------------------

func (ctl *client_ctl_client_conns_id_routes_id_peers_id) ServeHTTP(w http.ResponseWriter, req *http.Request) (int, error) {
	var c *Client
	var status_code int
	var err error
	var conn_id string
	var route_id string
	var peer_id string
	var conn_nid uint64
	var route_nid uint64
	var peer_nid uint64
	var je *json.Encoder
	var p *ClientPeerConn

	defer func() {
		var err interface{} = recover()
		if err != nil { dump_call_frame_and_exit(ctl.c.log, req, err) }
	}()

	c = ctl.c
	je = json.NewEncoder(w)

	conn_id = req.PathValue("conn_id")
	route_id = req.PathValue("route_id")
	peer_id = req.PathValue("peer_id")

	conn_nid, err = strconv.ParseUint(conn_id, 10, int(unsafe.Sizeof(ConnId(0)) * 8))
	if err != nil {
		status_code = http.StatusBadRequest; w.WriteHeader(status_code)
		if err = je.Encode(json_errmsg{Text: "wrong connection id - " + conn_id}); err != nil { goto oops }
		goto done
	}
	route_nid, err = strconv.ParseUint(route_id, 10, int(unsafe.Sizeof(RouteId(0)) * 8))
	if err != nil {
		status_code = http.StatusBadRequest; w.WriteHeader(status_code)
		if err = je.Encode(json_errmsg{Text: "wrong route id - " + route_id}); err != nil { goto oops }
		goto done
	}
	peer_nid, err = strconv.ParseUint(peer_id, 10, int(unsafe.Sizeof(ConnId(0)) * 8))
	if err != nil {
		status_code = http.StatusBadRequest; w.WriteHeader(status_code)
		if err = je.Encode(json_errmsg{Text: "wrong peer id - " + peer_id}); err != nil { goto oops }
		goto done
	}

	p = c.FindClientPeerConnById(ConnId(conn_nid), RouteId(route_nid), PeerId(peer_nid))
	if p == nil {
		status_code = http.StatusNotFound; w.WriteHeader(status_code)
		if err = je.Encode(json_errmsg{Text: "non-existent connection/route/peer id - " + conn_id + "/" + route_id + "/" + peer_id}); err != nil { goto oops }
		goto done
	}

	switch req.Method {
		case http.MethodGet:
			var jcp *json_out_client_peer

			jcp = &json_out_client_peer{
				Id: p.conn_id,
				ClientPeerAddr: p.conn.RemoteAddr().String(),
				ClientLocalAddr: p.conn.LocalAddr().String(),
				ServerPeerAddr: p.pts_raddr,
				ServerLocalAddr: p.pts_laddr,
			}

			status_code = http.StatusOK; w.WriteHeader(status_code)
			if err = je.Encode(jcp); err != nil { goto oops }

		case http.MethodDelete:
			p.ReqStop()
			status_code = http.StatusNoContent; w.WriteHeader(status_code)

		default:
			status_code = http.StatusBadRequest; w.WriteHeader(status_code)
	}

done:
	return status_code, nil

oops:
	return status_code, err
}

// ------------------------------------

func (ctl *client_ctl_stats) ServeHTTP(w http.ResponseWriter, req *http.Request) (int, error) {
	var c *Client
	var status_code int
	var err error
	var je *json.Encoder

	defer func() {
		var err interface{} = recover()
		if err != nil { dump_call_frame_and_exit(ctl.c.log, req, err) }
	}()

	c = ctl.c
	je = json.NewEncoder(w)

	switch req.Method {
		case http.MethodGet:
			var stats json_out_client_stats
			var mstat runtime.MemStats
			runtime.ReadMemStats(&mstat)
			stats.CPUs = runtime.NumCPU()
			stats.Goroutines = runtime.NumGoroutine()
			stats.NumGCs = mstat.NumGC
			stats.HeapAllocBytes = mstat.HeapAlloc
			stats.MemAllocs = mstat.Mallocs
			stats.MemFrees = mstat.Frees
			stats.ClientConns = c.stats.conns.Load()
			stats.ClientRoutes = c.stats.routes.Load()
			stats.ClientPeers = c.stats.peers.Load()
			status_code = http.StatusOK; w.WriteHeader(status_code)
			if err = je.Encode(stats); err != nil { goto oops }

		default:
			status_code = http.StatusBadRequest; w.WriteHeader(status_code)
	}

//done:
	return status_code, nil

oops:
	return status_code, err
}