package eval

import (
	
	
	
	
	

	
	
)

// Port conveys data stream. It always consists of a byte band and a channel band.
type Port struct {
	File      *os.File
	Chan      chan interface{}
	closeFile bool
	closeChan bool
}

// Returns a copy of the Port with the Close* flags unset.
func ( *Port) () *Port {
	return &Port{.File, .Chan, false, false}
}

// Closes a Port.
func ( *Port) () {
	if  == nil {
		return
	}
	if .closeFile {
		.File.Close()
	}
	if .closeChan {
		close(.Chan)
	}
}

var (
	// ClosedChan is a closed channel, suitable as a placeholder input channel.
	ClosedChan = getClosedChan()
	// BlackholeChan is a channel that absorbs all values written to it,
	// suitable as a placeholder output channel.
	BlackholeChan = getBlackholeChan()
	// DevNull is /dev/null, suitable as a placeholder file for either input or
	// output.
	DevNull = getDevNull()

	// DummyInputPort is a port made up from DevNull and ClosedChan, suitable as
	// a placeholder input port.
	DummyInputPort = &Port{File: DevNull, Chan: ClosedChan}
	// DummyOutputPort is a port made up from DevNull and BlackholeChan,
	// suitable as a placeholder output port.
	DummyOutputPort = &Port{File: DevNull, Chan: BlackholeChan}
)

func () chan interface{} {
	 := make(chan interface{})
	close()
	return 
}

func () chan interface{} {
	 := make(chan interface{})
	go func() {
		for range  {
		}
	}()
	return 
}

func () *os.File {
	,  := os.Open(os.DevNull)
	if  != nil {
		fmt.Fprintf(os.Stderr,
			"cannot open %s, shell might not function normally\n", os.DevNull)
	}
	return 
}

// PipePort returns an output *Port whose value and byte components are both
// piped. The supplied functions are called on a separate goroutine with the
// read ends of the value and byte components of the port. It also returns a
// function to clean up the port and wait for the callbacks to finish.
func ( func(<-chan interface{}),  func(*os.File)) (*Port, func(), error) {
	, ,  := os.Pipe()
	if  != nil {
		return nil, nil, 
	}
	 := make(chan interface{}, outputCaptureBufferSize)

	var  sync.WaitGroup
	.Add(2)
	go func() {
		defer .Done()
		()
	}()
	go func() {
		defer .Done()
		defer .Close()
		()
	}()

	 := &Port{Chan: , closeChan: true, File: , closeFile: true}
	 := func() {
		.close()
		.Wait()
	}
	return , , nil
}

// CapturePort returns an output *Port whose value and byte components are
// both connected to an internal pipe that saves the output. It also returns a
// function to call to obtain the captured output.
func () (*Port, func() []interface{}, error) {
	 := []interface{}{}
	var  sync.Mutex
	, ,  := PipePort(
		func( <-chan interface{}) {
			for  := range  {
				.Lock()
				 = append(, )
				.Unlock()
			}
		},
		func( *os.File) {
			 := bufio.NewReader()
			for {
				,  := .ReadString('\n')
				if  != "" {
					 := strutil.ChopLineEnding()
					.Lock()
					 = append(, )
					.Unlock()
				}
				if  != nil {
					if  != io.EOF {
						logger.Println("error on reading:", )
					}
					break
				}
			}
		})
	if  != nil {
		return nil, nil, 
	}
	return , func() []interface{} {
		()
		return 
	}, nil
}

// StringCapturePort is like CapturePort, but processes value outputs by
// stringifying them and prepending an output marker.
func () (*Port, func() []string, error) {
	var  []string
	var  sync.Mutex
	 := func( string) {
		.Lock()
		defer .Unlock()
		 = append(, )
	}
	, ,  := PipePort(
		func( <-chan interface{}) {
			for  := range  {
				("▶ " + vals.ToString())
			}
		},
		func( *os.File) {
			 := bufio.NewReader()
			for {
				,  := .ReadString('\n')
				if  != nil {
					if  != io.EOF {
						("i/o error: " + .Error())
					}
					break
				}
				(strutil.ChopLineEnding())
			}
		})
	if  != nil {
		return nil, nil, 
	}
	return , func() []string {
		()
		return 
	}, nil
}

// Buffer size for the channel to use in FilePort. The value has been chosen
// arbitrarily.
const filePortChanSize = 32

// FilePort returns an output *Port where the byte component is the file itself,
// and the value component is converted to an internal channel that writes
// each value to the file, prepending with a prefix. It also returns a cleanup
// function, which should be called when the *Port is no longer needed.
func ( *os.File,  string) (*Port, func()) {
	 := make(chan interface{}, filePortChanSize)
	 := make(chan struct{})
	go func() {
		for  := range  {
			.WriteString()
			.WriteString(vals.Repr(, vals.NoPretty))
			.WriteString("\n")
		}
		close()
	}()
	return &Port{File: , Chan: }, func() {
		close()
		<-
	}
}

// PortsFromStdFiles is a shorthand for calling PortsFromFiles with os.Stdin,
// os.Stdout and os.Stderr.
func ( string) ([]*Port, func()) {
	return PortsFromFiles([3]*os.File{os.Stdin, os.Stdout, os.Stderr}, )
}

// PortsFromFiles builds 3 ports from 3 files. It also returns a function that
// should be called when the ports are no longer needed.
func ( [3]*os.File,  string) ([]*Port, func()) {
	,  := FilePort([1], )
	,  := FilePort([2], )
	return []*Port{{File: [0], Chan: ClosedChan}, , }, func() {
		()
		()
	}
}