package daemon

import (
	
	

	
	
	
)

const retriesOnShutdown = 3

var (
	// ErrDaemonUnreachable is returned when the daemon cannot be reached after
	// several retries.
	ErrDaemonUnreachable = errors.New("daemon offline")
)

// Client represents a daemon client.
type Client interface {
	store.Store

	ResetConn() error
	Close() error

	Pid() (int, error)
	SockPath() string
	Version() (int, error)
}

// Implementation of the Client interface.
type client struct {
	sockPath  string
	rpcClient *rpc.Client
	waits     sync.WaitGroup
}

// NewClient creates a new Client instance that talks to the socket. Connection
// creation is deferred to the first request.
func ( string) Client {
	return &client{, nil, sync.WaitGroup{}}
}

// SockPath returns the socket path that the Client talks to. If the client is
// nil, it returns an empty string.
func ( *client) () string {
	return .sockPath
}

// ResetConn resets the current connection. A new connection will be established
// the next time a request is made. If the client is nil, it does nothing.
func ( *client) () error {
	if .rpcClient == nil {
		return nil
	}
	 := .rpcClient
	.rpcClient = nil
	return .Close()
}

// Close waits for all outstanding requests to finish and close the connection.
// If the client is nil, it does nothing and returns nil.
func ( *client) () error {
	.waits.Wait()
	return .ResetConn()
}

func ( *client) ( string, ,  interface{}) error {
	.waits.Add(1)
	defer .waits.Done()

	for  := 0;  < retriesOnShutdown; ++ {
		if .rpcClient == nil {
			,  := dial(.sockPath)
			if  != nil {
				return 
			}
			.rpcClient = rpc.NewClient()
		}

		 := .rpcClient.Call(api.ServiceName+"."+, , )
		if  == rpc.ErrShutdown {
			// Clear rpcClient so as to reconnect next time
			.rpcClient = nil
			continue
		} else {
			return 
		}
	}
	return ErrDaemonUnreachable
}

// Convenience methods for RPC methods. These are quite repetitive; when the
// number of RPC calls grow above some threshold, a code generator should be
// written to generate them.

func ( *client) () (int, error) {
	 := &api.VersionRequest{}
	 := &api.VersionResponse{}
	 := .call("Version", , )
	return .Version, 
}

func ( *client) () (int, error) {
	 := &api.PidRequest{}
	 := &api.PidResponse{}
	 := .call("Pid", , )
	return .Pid, 
}

func ( *client) () (int, error) {
	 := &api.NextCmdRequest{}
	 := &api.NextCmdSeqResponse{}
	 := .call("NextCmdSeq", , )
	return .Seq, 
}

func ( *client) ( string) (int, error) {
	 := &api.AddCmdRequest{Text: }
	 := &api.AddCmdResponse{}
	 := .call("AddCmd", , )
	return .Seq, 
}

func ( *client) ( int) error {
	 := &api.DelCmdRequest{Seq: }
	 := &api.DelCmdResponse{}
	 := .call("DelCmd", , )
	return 
}

func ( *client) ( int) (string, error) {
	 := &api.CmdRequest{Seq: }
	 := &api.CmdResponse{}
	 := .call("Cmd", , )
	return .Text, 
}

func ( *client) (,  int) ([]string, error) {
	 := &api.CmdsRequest{From: , Upto: }
	 := &api.CmdsResponse{}
	 := .call("Cmds", , )
	return .Cmds, 
}

func ( *client) (,  int) ([]store.Cmd, error) {
	 := &api.CmdsWithSeqRequest{From: , Upto: }
	 := &api.CmdsWithSeqResponse{}
	 := .call("CmdsWithSeq", , )
	return .Cmds, 
}

func ( *client) ( int,  string) (store.Cmd, error) {
	 := &api.NextCmdRequest{From: , Prefix: }
	 := &api.NextCmdResponse{}
	 := .call("NextCmd", , )
	return store.Cmd{Text: .Text, Seq: .Seq}, 
}

func ( *client) ( int,  string) (store.Cmd, error) {
	 := &api.PrevCmdRequest{Upto: , Prefix: }
	 := &api.PrevCmdResponse{}
	 := .call("PrevCmd", , )
	return store.Cmd{Text: .Text, Seq: .Seq}, 
}

func ( *client) ( string,  float64) error {
	 := &api.AddDirRequest{Dir: , IncFactor: }
	 := &api.AddDirResponse{}
	 := .call("AddDir", , )
	return 
}

func ( *client) ( string) error {
	 := &api.DelDirRequest{Dir: }
	 := &api.DelDirResponse{}
	 := .call("DelDir", , )
	return 
}

func ( *client) ( map[string]struct{}) ([]store.Dir, error) {
	 := &api.DirsRequest{Blacklist: }
	 := &api.DirsResponse{}
	 := .call("Dirs", , )
	return .Dirs, 
}

func ( *client) ( string) (string, error) {
	 := &api.SharedVarRequest{Name: }
	 := &api.SharedVarResponse{}
	 := .call("SharedVar", , )
	return .Value, 
}

func ( *client) (,  string) error {
	 := &api.SetSharedVarRequest{Name: , Value: }
	 := &api.SetSharedVarResponse{}
	return .call("SetSharedVar", , )
}

func ( *client) ( string) error {
	 := &api.DelSharedVarRequest{Name: }
	 := &api.DelSharedVarResponse{}
	return .call("DelSharedVar", , )
}