// Copyright 2021 The TCell Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use file except in compliance with the License.
// You may obtain a copy of the license at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//go:build aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos

package tcell

import (
	
	
	
	
	
	
	
	

	
	
)

// stdIoTty is an implementation of the Tty API based upon stdin/stdout.
type stdIoTty struct {
	fd    int
	in    *os.File
	out   *os.File
	saved *term.State
	sig   chan os.Signal
	cb    func()
	stopQ chan struct{}
	dev   string
	wg    sync.WaitGroup
	l     sync.Mutex
}

func ( *stdIoTty) ( []byte) (int, error) {
	return .in.Read()
}

func ( *stdIoTty) ( []byte) (int, error) {
	return .out.Write()
}

func ( *stdIoTty) () error {
	return nil
}

func ( *stdIoTty) () error {
	.l.Lock()
	defer .l.Unlock()

	// We open another copy of /dev/tty.  This is a workaround for unusual behavior
	// observed in macOS, apparently caused when a sub-shell (for example) closes our
	// own tty device (when it exits for example).  Getting a fresh new one seems to
	// resolve the problem.  (We believe this is a bug in the macOS tty driver that
	// fails to account for dup() references to the same file before applying close()
	// related behaviors to the tty.)  We're also holding the original copy we opened
	// since closing that might have deleterious effects as well.  The upshot is that
	// we will have up to two separate file handles open on /dev/tty.  (Note that when
	// using stdin/stdout instead of /dev/tty this problem is not observed.)
	var  error
	.in = os.Stdin
	.out = os.Stdout
	.fd = int(.in.Fd())

	if !term.IsTerminal(.fd) {
		return errors.New("device is not a terminal")
	}

	_ = .in.SetReadDeadline(time.Time{})
	,  := term.MakeRaw(.fd) // also sets vMin and vTime
	if  != nil {
		return 
	}
	.saved = 

	.stopQ = make(chan struct{})
	.wg.Add(1)
	go func( chan struct{}) {
		defer .wg.Done()
		for {
			select {
			case <-.sig:
				.l.Lock()
				 := .cb
				.l.Unlock()
				if  != nil {
					()
				}
			case <-:
				return
			}
		}
	}(.stopQ)

	signal.Notify(.sig, syscall.SIGWINCH)
	return nil
}

func ( *stdIoTty) () error {
	_ = .in.SetReadDeadline(time.Now())
	if  := tcSetBufParams(.fd, 0, 0);  != nil {
		return 
	}
	return nil
}

func ( *stdIoTty) () error {
	.l.Lock()
	if  := term.Restore(.fd, .saved);  != nil {
		.l.Unlock()
		return 
	}
	_ = .in.SetReadDeadline(time.Now())

	signal.Stop(.sig)
	close(.stopQ)
	.l.Unlock()

	.wg.Wait()

	return nil
}

func ( *stdIoTty) () (WindowSize, error) {
	 := WindowSize{}
	,  := unix.IoctlGetWinsize(.fd, unix.TIOCGWINSZ)
	if  != nil {
		return , 
	}
	 := int(.Col)
	 := int(.Row)
	if  == 0 {
		, _ = strconv.Atoi(os.Getenv("COLUMNS"))
	}
	if  == 0 {
		 = 80 // default
	}
	if  == 0 {
		, _ = strconv.Atoi(os.Getenv("LINES"))
	}
	if  == 0 {
		 = 25 // default
	}
	.Width = 
	.Height = 
	.PixelWidth = int(.Xpixel)
	.PixelHeight = int(.Ypixel)
	return , nil
}

func ( *stdIoTty) ( func()) {
	.l.Lock()
	.cb = 
	.l.Unlock()
}

// NewStdioTty opens a tty using standard input/output.
func () (Tty, error) {
	 := &stdIoTty{
		sig: make(chan os.Signal),
		in:  os.Stdin,
		out: os.Stdout,
	}
	var  error
	.fd = int(.in.Fd())
	if !term.IsTerminal(.fd) {
		return nil, errors.New("not a terminal")
	}
	if .saved,  = term.GetState(.fd);  != nil {
		return nil, fmt.Errorf("failed to get state: %w", )
	}
	return , nil
}