package main

import (
	
	
	
	
	_ 
	
	
	

	
	ssrpc 
	ssam 
	amrelay 
	amrelayt 

	example 
	
	amhelp 
	am 
	arpc 
)

var ssF = states.FooStates
var ssB = states.BarStates
var PassRpc = example.PassRpc

type ARpc = example.ARpc

func init() {
	_ = godotenv.Load()
}

func main() {
	 := context.Background()

	// debug

	go func() {
		http.ListenAndServe("localhost:6060", nil)
	}()

	// net source machine

	 := &HandlersFoo{
		lastMsg: time.Now(),
	}
	,  := am.NewCommon(, "server-foo", states.FooSchema, ssF.Names(), , nil, nil)
	if  != nil {
		panic()
	}
	amhelp.MachDebugEnv()
	.SemLogger().SetArgsMapper(example.LogArgs)
	.machFoo = 
	arpc.MachRepl(, example.EnvFooReplAddr, &arpc.ReplOpts{
		// TODO config
		AddrDir:  "tmp",
		Args:     ARpc{},
		ParseRpc: example.ParseRpc,
	})
	.Add1(ssF.Start, nil)

	// RPC Server

	,  := arpc.NewServer(, example.EnvFooWsAddr, "server-foo", , &arpc.ServerOpts{
		// manual web socket, no tcp, not relayed
		WebSocket: true,
		Parent:    ,
	})
	if  != nil {
		panic()
	}
	.Start(nil)
	.s = 

	// websocket relay

	 := func( context.Context,  string,  net.Conn) (*arpc.Client, error) {
		// RPC Client (Net Machine)
		,  := arpc.NewClient(, "localhost:0", "server-bar", states.BarSchema, &arpc.ClientOpts{
			Parent: ,
		})
		if  != nil {
			panic()
		}

		// inject the connection, store and start TODO atomic
		.Conn.Store(&)
		.rpcBar.Store()
		.Start(nil)
		go func() {
			<-.Mach.When1(ssrpc.ClientStates.Ready, nil)
			fmt.Printf("aRPC connected via WebSocket\n")
		}()

		return , nil
	}
	,  := amrelay.New(, amrelayt.Args{
		Name:   "wasm-demo",
		Debug:  true,
		Parent: ,
		Wasm: &amrelayt.ArgsWasm{
			ListenAddr: example.EnvRelayHttpAddr,
			StaticDir:  "./client",
			// TODO config
			ReplAddrDir: "tmp",
			ClientMatchers: []amrelayt.ClientMatcher{{
				Id:        regexp.MustCompile("^browser-bar-"),
				NewClient: ,
			}},
		},
	})
	if  != nil {
		panic()
	}
	.Start(nil)

	// wait
	select {}
}

type HandlersFoo struct {
	machFoo *am.Machine
	// last connected browser machine
	rpcBar atomic.Pointer[arpc.Client]
	s      *arpc.Server

	lastMsg   time.Time
	lastHello time.Time
}

func ( *HandlersFoo) ( *am.Event) {
	 := .machFoo.NewStateCtx(ssF.Start)

	.machFoo.Fork(, , func() {
		 := time.NewTicker(time.Second)
		defer .Stop()

		for {
			select {
			case <-.Done():
				return

			case <-.C:
				// bored? 15sec after the last msg
				if time.Since(.lastMsg) > 15*time.Second && .machFoo.Not1(ssF.Bored) {
					// periodic sync without args
					.machFoo.Add1(ssF.Bored, nil)
				}

				// hello? try 10 times, only if browser-bar connected
				 := .rpcBar.Load()
				if  != nil && time.Since(.lastHello) > 10*time.Second &&
					.Mach.Is1(ssam.BasicStates.Ready) &&
					.NetMach.Tick(ssB.Msg) < 20 {

					// instant sync with args
					 := PassRpc(&ARpc{
						Msg: "hello",
					})
					.NetMach.EvAdd1(, ssF.Msg, )
					.machFoo.EvAdd1(, ssF.Msg, )
					.lastHello = time.Now()
				}
			}
		}
	})
}

func ( *HandlersFoo) ( *am.Event) bool {
	return example.ParseArgs(.Args).Msg != ""
}

func ( *HandlersFoo) ( *am.Event) {
	.machFoo.Remove1(ssF.Msg, nil)
	 := example.ParseArgs(.Args)

	// both foo and bar mutate this state
	 := "bar"
	if .Mutation().Source != nil {
		 = .Mutation().Source.MachId
	}

	 := fmt.Sprintf("[%s] %s\n", , .Msg)
	fmt.Print()
	.lastMsg = time.Now()
}

func ( *HandlersFoo) ( *am.Event) {
	fmt.Println("foo is bored...")
}