package relay

import (
	
	
	

	

	

	amhelp 
	am 
	ssam 
	
)

type WsTcpTun struct {
	*ssam.DisposedHandlers

	Mach *am.Machine
	// Incoming WebSocket connection
	WsConn *websocket.Conn
	// TCP addr to listen on
	TcpAddr    string
	TcpLn      net.Listener
	RemoteAddr string
	RemoteId   string
}

func (
	 context.Context,  *websocket.Conn, , , ,
	 string,  am.Api,  bool,
) (*WsTcpTun, error) {
	 := &WsTcpTun{
		DisposedHandlers: &ssam.DisposedHandlers{},

		WsConn:     ,
		TcpAddr:    ,
		RemoteAddr: ,
		RemoteId:   ,
	}

	,  := am.NewCommon(, , states.WsTcpTunSchema,
		ssT.Names(), , , &am.Opts{
			DontPanicToException: ,
		})
	if  != nil {
		return nil, 
	}
	.Mach = 
	if  {
		_ = amhelp.MachDebugEnv()
	}

	// register disposal
	var  am.HandlerDispose = func( string,  context.Context) {
		_ = .WsConn.Close(websocket.StatusNormalClosure, "")
	}
	.Mach.Add1(ssT.RegisterDisposal, am.A{
		ssam.DisposedArgHandler: ,
	})

	return , nil
}

func ( *WsTcpTun) ( *am.Event) bool {
	return .WsConn != nil && .TcpAddr != ""
}

func ( *WsTcpTun) ( *am.Event) {
	.Mach.Log("WS tun for %s (%s) at %s", .RemoteId, .RemoteAddr, .TcpAddr)
}

func ( *WsTcpTun) ( *am.Event) {
	if .TcpLn != nil {
		_ = .TcpLn.Close()
	}
}

func ( *WsTcpTun) ( *am.Event) {
	// dispose on WS disconn
	.Mach.EvAdd1(, ssT.Disposing, nil)
}

func ( *WsTcpTun) ( *am.Event) {
	 := .Mach.NewStateCtx(ssR.Start)

	.Mach.Fork(, , func() {
		var  net.ListenConfig
		,  := .Listen(, "tcp", .TcpAddr)
		if  != nil {
			.Mach.EvAddErr(, , nil)
			return
		}
		if .Err() != nil {
			// close early
			.Close()
			return // expired
		}
		.TcpLn = 
		.Mach.EvAdd1(, ssT.TcpListening, nil)
	})
}

func ( *WsTcpTun) ( *am.Event) {
	 := .Mach.NewStateCtx(ssR.Start)
	 := .TcpLn

	.Mach.Fork(, , func() {
		for .Err() == nil {
			,  := .Accept()
			if  != nil {
				.Mach.Log("TCP accept err (%s)", .Addr())
				continue
			}

			// accept
			 := amhelp.EvAdd1Sync(, , .Mach, ssT.TcpAccepted,
				types.Pass(&types.A{
					RemoteAddr: .RemoteAddr().String(),
					Conn:       ,
				}))
			if  == am.Executed {
				<-.Mach.WhenNot1(ssT.TcpAccepted, )
			}
		}
	})
}

func ( *WsTcpTun) ( *am.Event) {
	 := .Mach.NewStateCtx(ssT.TcpAccepted)
	 := types.ParseArgs(.Args)

	.Mach.Fork(, , func() {
		// start
		 := websocket.NetConn(, .WsConn, websocket.MessageBinary)

		// TCP -> WebSocket (WASM)
		go func() {
			,  := io.Copy(, .Conn)
			.Mach.EvAddErrState(, ssT.ErrServer, , Pass(&A{
				RemoteAddr: .RemoteAddr,
			}))
		}()

		// WebSocket (WASM) -> TCP
		go func() {
			,  := io.Copy(.Conn, )
			.Mach.EvAddErrState(, ssT.ErrClient, , Pass(&A{
				RemoteAddr: .RemoteAddr,
			}))
		}()

		// wait
		<-.Done()
	})
}