package rpc2

import (
	
	
	
	
	
	
	

	
)

// Precompute the reflect type for error.  Can't use error directly
// because Typeof takes an empty interface value.  This is annoying.
var typeOfError = reflect.TypeOf((*error)(nil)).Elem()
var typeOfClient = reflect.TypeOf((*Client)(nil))

const (
	clientConnected hub.Kind = iota
	clientDisconnected
)

// Server responds to RPC requests made by Client.
type Server struct {
	handlers map[string]*handler
	eventHub *hub.Hub
}

type handler struct {
	fn        reflect.Value
	argType   reflect.Type
	replyType reflect.Type
}

type connectionEvent struct {
	Client *Client
}

type disconnectionEvent struct {
	Client *Client
}

func (connectionEvent) () hub.Kind    { return clientConnected }
func (disconnectionEvent) () hub.Kind { return clientDisconnected }

// NewServer returns a new Server.
func () *Server {
	return &Server{
		handlers: make(map[string]*handler),
		eventHub: &hub.Hub{},
	}
}

// Handle registers the handler function for the given method. If a handler already exists for method, Handle panics.
func ( *Server) ( string,  interface{}) {
	addHandler(.handlers, , )
}

func addHandler( map[string]*handler,  string,  interface{}) {
	if ,  := [];  {
		panic("rpc2: multiple registrations for " + )
	}

	 := reflect.ValueOf()
	 := .Type()
	// Method needs three ins: *client, *args, *reply.
	if .NumIn() != 3 {
		log.Panicln("method", , "has wrong number of ins:", .NumIn())
	}
	// First arg must be a pointer to rpc2.Client.
	 := .In(0)
	if .Kind() != reflect.Ptr {
		log.Panicln("method", , "client type not a pointer:", )
	}
	if  != typeOfClient {
		log.Panicln("method", , "first argument", .String(), "not *rpc2.Client")
	}
	// Second arg need not be a pointer.
	 := .In(1)
	if !isExportedOrBuiltinType() {
		log.Panicln(, "argument type not exported:", )
	}
	// Third arg must be a pointer.
	 := .In(2)
	if .Kind() != reflect.Ptr {
		log.Panicln("method", , "reply type not a pointer:", )
	}
	// Reply type must be exported.
	if !isExportedOrBuiltinType() {
		log.Panicln("method", , "reply type not exported:", )
	}
	// Method needs one out.
	if .NumOut() != 1 {
		log.Panicln("method", , "has wrong number of outs:", .NumOut())
	}
	// The return type of the method must be error.
	if  := .Out(0);  != typeOfError {
		log.Panicln("method", , "returns", .String(), "not error")
	}
	[] = &handler{
		fn:        ,
		argType:   ,
		replyType: ,
	}
}

// Is this type exported or a builtin?
func isExportedOrBuiltinType( reflect.Type) bool {
	for .Kind() == reflect.Ptr {
		 = .Elem()
	}
	// PkgPath will be non-empty even for an exported type,
	// so we need to check the type name as well.
	return isExported(.Name()) || .PkgPath() == ""
}

// Is this an exported - upper case - name?
func isExported( string) bool {
	,  := utf8.DecodeRuneInString()
	return unicode.IsUpper()
}

// OnConnect registers a function to run when a client connects.
func ( *Server) ( func(*Client)) {
	.eventHub.Subscribe(clientConnected, func( hub.Event) {
		go (.(connectionEvent).Client)
	})
}

// OnDisconnect registers a function to run when a client disconnects.
func ( *Server) ( func(*Client)) {
	.eventHub.Subscribe(clientDisconnected, func( hub.Event) {
		go (.(disconnectionEvent).Client)
	})
}

// Accept accepts connections on the listener and serves requests
// for each incoming connection.  Accept blocks; the caller typically
// invokes it in a go statement.
func ( *Server) ( net.Listener) {
	for {
		,  := .Accept()
		if  != nil {
			if !errors.Is(, net.ErrClosed) {
				log.Print("rpc.Serve: accept:", .Error())
			}
			return
		}
		go .ServeConn()
	}
}

// ServeConn runs the server on a single connection.
// ServeConn blocks, serving the connection until the client hangs up.
// The caller typically invokes ServeConn in a go statement.
// ServeConn uses the gob wire format (see package gob) on the
// connection.  To use an alternate codec, use ServeCodec.
func ( *Server) ( io.ReadWriteCloser) {
	.ServeCodec(NewGobCodec())
}

// ServeCodec is like ServeConn but uses the specified codec to
// decode requests and encode responses.
func ( *Server) ( Codec) {
	.ServeCodecWithState(, NewState())
}

// ServeCodecWithState is like ServeCodec but also gives the ability to
// associate a state variable with the client that persists across RPC calls.
func ( *Server) ( Codec,  *State) {
	defer .Close()

	// Client also handles the incoming connections.
	 := NewClientWithCodec()
	.server = true
	.handlers = .handlers
	.State = 

	.eventHub.Publish(connectionEvent{})
	.Run()
	.eventHub.Publish(disconnectionEvent{})
}