package winrm

import (
	
	
	
	
	
	
	
	

	
)

// Client struct
type Client struct {
	Parameters
	username string
	password string
	useHTTPS bool
	url      string
	http     Transporter
}

// Transporter does different transporters
// and init a Post request based on them
type Transporter interface {
	// init request baset on the transport configurations
	Post(*Client, *soap.SoapMessage) (string, error)
	Transport(*Endpoint) error
}

// NewClient will create a new remote client on url, connecting with user and password
// This function doesn't connect (connection happens only when CreateShell is called)
func ( *Endpoint, ,  string) (*Client, error) {
	return NewClientWithParameters(, , , DefaultParameters)
}

// NewClientWithParameters will create a new remote client on url, connecting with user and password
// This function doesn't connect (connection happens only when CreateShell is called)
func ( *Endpoint, ,  string,  *Parameters) (*Client, error) {
	// alloc a new client
	 := &Client{
		Parameters: *,
		username:   ,
		password:   ,
		url:        .url(),
		useHTTPS:   .HTTPS,
		// default transport
		http: &clientRequest{dial: .Dial},
	}

	// switch to other transport if provided
	if .TransportDecorator != nil {
		.http = .TransportDecorator()
	}

	// set the transport to some endpoint configuration
	if  := .http.Transport();  != nil {
		return nil, fmt.Errorf("can't parse this key and certs: %w", )
	}

	return , nil
}

func readCACerts( []byte) (*x509.CertPool, error) {
	 := x509.NewCertPool()

	if !.AppendCertsFromPEM() {
		return nil, errors.New("unable to read certificates")
	}

	return , nil
}

// CreateShell will create a WinRM Shell,
// which is the prealable for running commands.
func ( *Client) () (*Shell, error) {
	 := NewOpenShellRequest(.url, &.Parameters)
	defer .Free()

	,  := .sendRequest()
	if  != nil {
		return nil, 
	}

	,  := ParseOpenShellResponse()
	if  != nil {
		return nil, 
	}

	return .NewShell(), nil
}

// NewShell will create a new WinRM Shell for the given shellID
func ( *Client) ( string) *Shell {
	return &Shell{client: , id: }
}

// sendRequest exec the custom http func from the client
func ( *Client) ( *soap.SoapMessage) (string, error) {
	return .http.Post(, )
}

// Run will run command on the the remote host, writing the process stdout and stderr to
// the given writers. Note with this method it isn't possible to inject stdin.
//
// Deprecated: use RunWithContext()
func ( *Client) ( string,  io.Writer,  io.Writer) (int, error) {
	return .RunWithContext(context.Background(), , , )
}

// RunWithContext will run command on the the remote host, writing the process stdout and stderr to
// the given writers. Note with this method it isn't possible to inject stdin.
// If the context is canceled, the remote command is canceled.
func ( *Client) ( context.Context,  string,  io.Writer,  io.Writer) (int, error) {
	return .RunWithContextWithInput(, , , , nil)
}

// RunWithString will run command on the the remote host, returning the process stdout and stderr
// as strings, and using the input stdin string as the process input
//
// Deprecated: use RunWithContextWithString()
func ( *Client) ( string,  string) (string, string, int, error) {
	return .RunWithContextWithString(context.Background(), , )
}

// RunWithContextWithString will run command on the the remote host, returning the process stdout and stderr
// as strings, and using the input stdin string as the process input
// If the context is canceled, the remote command is canceled.
func ( *Client) ( context.Context,  string,  string) (string, string, int, error) {
	var ,  bytes.Buffer
	,  := .RunWithContextWithInput(, , &, &, strings.NewReader())
	return .String(), .String(), , 
}

// RunPSWithString will basically wrap your code to execute commands in powershell.exe. Default RunWithString
// runs commands in cmd.exe
//
// Deprecated: use RunPSWithContextWithString()
func ( *Client) ( string,  string) (string, string, int, error) {
	return .RunPSWithContextWithString(context.Background(), , )
}

// RunPSWithContextWithString will basically wrap your code to execute commands in powershell.exe. Default RunWithString
// runs commands in cmd.exe
func ( *Client) ( context.Context,  string,  string) (string, string, int, error) {
	 = Powershell()

	// Let's check if we actually created a command
	if  == "" {
		return "", "", 1, errors.New("cannot encode the given command")
	}

	// Specify powershell.exe to run encoded command
	return .RunWithContextWithString(, , )
}

// RunWithInput will run command on the the remote host, writing the process stdout and stderr to
// the given writers, and injecting the process stdin with the stdin reader.
// Warning stdin (not stdout/stderr) are bufferized, which means reading only one byte in stdin will
// send a winrm http packet to the remote host. If stdin is a pipe, it might be better for
// performance reasons to buffer it.
// If stdin is nil, this is equivalent to c.Run()
//
// Deprecated: use RunWithContextWithInput()
func ( *Client) ( string, ,  io.Writer,  io.Reader) (int, error) {
	return .RunWithContextWithInput(context.Background(), , , , )
}

// RunWithContextWithInput will run command on the the remote host, writing the process stdout and stderr to
// the given writers, and injecting the process stdin with the stdin reader.
// If the context is canceled, the command on the remote machine is canceled.
// Warning stdin (not stdout/stderr) are bufferized, which means reading only one byte in stdin will
// send a winrm http packet to the remote host. If stdin is a pipe, it might be better for
// performance reasons to buffer it.
// If stdin is nil, this is equivalent to c.RunWithContext()
func ( *Client) ( context.Context,  string, ,  io.Writer,  io.Reader) (int, error) {
	,  := .CreateShell()
	if  != nil {
		return 1, 
	}
	defer .Close()
	,  := .ExecuteWithContext(, )
	if  != nil {
		return 1, 
	}

	var  sync.WaitGroup
	.Add(3)

	go func() {
		defer func() {
			.Done()
		}()
		if  == nil {
			return
		}
		defer func() {
			.Stdin.Close()
		}()
		_, _ = io.Copy(.Stdin, )
	}()
	go func() {
		defer .Done()
		_, _ = io.Copy(, .Stdout)
	}()
	go func() {
		defer .Done()
		_, _ = io.Copy(, .Stderr)
	}()

	.Wait()
	.Wait()
	.Close()

	return .ExitCode(), .err
}