// Copyright 2024 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.

package tcell

import (
	
	

	
)

// NewSimulationScreen returns a SimulationScreen.  Note that
// SimulationScreen is also a Screen.
func ( string) SimulationScreen {
	if  == "" {
		 = "UTF-8"
	}
	 := &simscreen{charset: }
	.Screen = &baseScreen{screenImpl: }
	return 
}

// SimulationScreen represents a screen simulation.  This is intended to
// be a superset of normal Screens, but also adds some important interfaces
// for testing.
type SimulationScreen interface {
	Screen

	// InjectKeyBytes injects a stream of bytes corresponding to
	// the native encoding (see charset).  It turns true if the entire
	// set of bytes were processed and delivered as KeyEvents, false
	// if any bytes were not fully understood.  Any bytes that are not
	// fully converted are discarded.
	InjectKeyBytes(buf []byte) bool

	// InjectKey injects a key event.  The rune is a UTF-8 rune, post
	// any translation.
	InjectKey(key Key, r rune, mod ModMask)

	// InjectMouse injects a mouse event.
	InjectMouse(x, y int, buttons ButtonMask, mod ModMask)

	// GetContents returns screen contents as an array of
	// cells, along with the physical width & height.   Note that the
	// physical contents will be used until the next time SetSize()
	// is called.
	GetContents() (cells []SimCell, width int, height int)

	// GetCursor returns the cursor details.
	GetCursor() (x int, y int, visible bool)

	// GetTitle gets the previously set title.
	GetTitle() string

	// GetClipboardData gets the actual data for the clipboard.
	GetClipboardData() []byte
}

// SimCell represents a simulated screen cell.  The purpose of this
// is to track on screen content.
type SimCell struct {
	// Bytes is the actual character bytes.  Normally this is
	// rune data, but it could be be data in another encoding system.
	Bytes []byte

	// Style is the style used to display the data.
	Style Style

	// Runes is the list of runes, unadulterated, in UTF-8.
	Runes []rune
}

type simscreen struct {
	physw int
	physh int
	fini  bool
	style Style
	evch  chan Event
	quit  chan struct{}

	front     []SimCell
	back      CellBuffer
	clear     bool
	cursorx   int
	cursory   int
	cursorvis bool
	mouse     bool
	paste     bool
	charset   string
	encoder   transform.Transformer
	decoder   transform.Transformer
	fillchar  rune
	fillstyle Style
	fallback  map[rune]string
	title     string
	clipboard []byte

	Screen
	sync.Mutex
}

func ( *simscreen) () error {
	.evch = make(chan Event, 10)
	.quit = make(chan struct{})
	.fillchar = 'X'
	.fillstyle = StyleDefault
	.mouse = false
	.physw = 80
	.physh = 25
	.cursorx = -1
	.cursory = -1
	.style = StyleDefault

	if  := GetEncoding(.charset);  != nil {
		.encoder = .NewEncoder()
		.decoder = .NewDecoder()
	} else {
		return ErrNoCharset
	}

	.front = make([]SimCell, .physw*.physh)
	.back.Resize(80, 25)

	// default fallbacks
	.fallback = make(map[rune]string)
	for ,  := range RuneFallbacks {
		.fallback[] = 
	}
	return nil
}

func ( *simscreen) () {
	.Lock()
	.fini = true
	.back.Resize(0, 0)
	.Unlock()
	if .quit != nil {
		close(.quit)
	}
	.physw = 0
	.physh = 0
	.front = nil
}

func ( *simscreen) ( Style) {
	.Lock()
	.style = 
	.Unlock()
}

func ( *simscreen) (,  int) int {

	, , ,  := .back.GetContent(, )
	if !.back.Dirty(, ) {
		return 
	}
	if  >= .physw ||  >= .physh ||  < 0 ||  < 0 {
		return 
	}
	 := &.front[(*.physw)+]

	if  == StyleDefault {
		 = .style
	}
	.Style = 
	.Runes = append([]rune{}, ...)

	// now emit runes - taking care to not overrun width with a
	// wide character, and to ensure that we emit exactly one regular
	// character followed up by any residual combing characters

	.Bytes = nil

	if  > .physw- {
		.Runes = []rune{' '}
		.Bytes = []byte{' '}
		return 
	}

	 := make([]byte, 12)
	 := make([]byte, 12)
	 := 0

	for ,  := range .Runes {

		 := utf8.EncodeRune(, )

		, _, _ = .encoder.Transform(, [:], true)

		if  == 0 || [0] == '\x1a' {

			// skip combining

			if ,  := .fallback[];  {
				.Bytes = append(.Bytes,
					[]byte()...)

			} else if  >= ' ' &&  <= '~' {
				.Bytes = append(.Bytes, byte())

			} else if .Bytes == nil {
				.Bytes = append(.Bytes, '?')
			}
		} else {
			.Bytes = append(.Bytes, [:]...)
		}
	}
	.back.SetDirty(, , false)
	return 
}

func ( *simscreen) (,  int) {
	.Lock()
	.cursorx, .cursory = , 
	.showCursor()
	.Unlock()
}

func ( *simscreen) () {
	.ShowCursor(-1, -1)
}

func ( *simscreen) () {

	,  := .cursorx, .cursory
	if  < 0 ||  < 0 ||  >= .physw ||  >= .physh {
		.cursorvis = false
	} else {
		.cursorvis = true
	}
}

func ( *simscreen) () {
	// does not update cursor position
	.cursorvis = false
}

func ( *simscreen) (CursorStyle, Color) {}

func ( *simscreen) () {
	.Lock()
	.resize()
	.draw()
	.Unlock()
}

func ( *simscreen) () {
	// We emulate a hardware clear by filling with a specific pattern
	for  := range .front {
		.front[].Style = .fillstyle
		.front[].Runes = []rune{.fillchar}
		.front[].Bytes = []byte{byte(.fillchar)}
	}
	.clear = false
}

func ( *simscreen) () {
	.hideCursor()
	if .clear {
		.clearScreen()
	}

	,  := .back.Size()
	for  := 0;  < ; ++ {
		for  := 0;  < ; ++ {
			 := .drawCell(, )
			 +=  - 1
		}
	}
	.showCursor()
}

func ( *simscreen) (...MouseFlags) {
	.mouse = true
}

func ( *simscreen) () {
	.mouse = false
}

func ( *simscreen) () {
	.paste = true
}

func ( *simscreen) () {
	.paste = false
}

func ( *simscreen) () {
}

func ( *simscreen) () {
}

func ( *simscreen) () (int, int) {
	.Lock()
	,  := .back.Size()
	.Unlock()
	return , 
}

func ( *simscreen) () {
	,  := .physw, .physh
	,  := .back.Size()
	if  !=  ||  !=  {
		.back.Resize(, )
		 := NewEventResize(, )
		.postEvent()
	}
}

func ( *simscreen) () int {
	return 256
}

func ( *simscreen) ( Event) {
	select {
	case .evch <- :
	case <-.quit:
	}
}

func ( *simscreen) (,  int,  ButtonMask,  ModMask) {
	 := NewEventMouse(, , , )
	.postEvent()
}

func ( *simscreen) ( Key,  rune,  ModMask) {
	 := NewEventKey(, , )
	.postEvent()
}

func ( *simscreen) ( []byte) bool {
	 := false

:
	for len() > 0 {
		if [0] >= ' ' && [0] <= 0x7F {
			// printable ASCII easy to deal with -- no encodings
			 := NewEventKey(KeyRune, rune([0]), ModNone)
			.postEvent()
			 = [1:]
			continue
		}

		if [0] < 0x80 {
			 := ModNone
			// No encodings start with low numbered values
			if Key([0]) >= KeyCtrlA && Key([0]) <= KeyCtrlZ {
				 = ModCtrl
			}
			 := NewEventKey(Key([0]), 0, )
			.postEvent()
			 = [1:]
			continue
		}

		 := make([]byte, len()*4) // worst case
		for  := 1;  < len(); ++ {
			.decoder.Reset()
			, ,  := .decoder.Transform(, [:], true)

			if  != 0 {
				,  := utf8.DecodeRune([:])
				if  != utf8.RuneError {
					 := NewEventKey(KeyRune, , ModNone)
					.postEvent()
				}
				 = [:]
				continue 
			}
		}
		 = true
		 = [1:]
		continue
	}

	return !
}

func ( *simscreen) () {
	.Lock()
	.clear = true
	.resize()
	.back.Invalidate()
	.draw()
	.Unlock()
}

func ( *simscreen) () string {
	return .charset
}

func ( *simscreen) (,  int) {
	.Lock()
	 := make([]SimCell, *)
	for  := 0;  <  &&  < .physh; ++ {
		for  := 0;  <  &&  < .physw; ++ {
			[(*)+] = .front[(*.physw)+]
		}
	}
	.cursorx, .cursory = -1, -1
	.physw, .physh = , 
	.front = 
	.back.Resize(, )
	.Unlock()
}

func ( *simscreen) () ([]SimCell, int, int) {
	.Lock()
	, ,  := .front, .physw, .physh
	.Unlock()
	return , , 
}

func ( *simscreen) () (int, int, bool) {
	.Lock()
	, ,  := .cursorx, .cursory, .cursorvis
	.Unlock()
	return , , 
}

func ( *simscreen) ( rune,  string) {
	.Lock()
	.fallback[] = 
	.Unlock()
}

func ( *simscreen) ( rune) {
	.Lock()
	delete(.fallback, )
	.Unlock()
}

func ( *simscreen) ( rune,  bool) bool {

	if  := .encoder;  != nil {
		 := make([]byte, 6)
		 := make([]byte, 6)
		 := utf8.EncodeRune(, )

		.Reset()
		, ,  := .Transform(, [:], true)
		if  != 0 &&  == nil && [0] != '\x1A' {
			return true
		}
	}
	if ! {
		return false
	}
	if ,  := .fallback[];  {
		return true
	}
	return false
}

func ( *simscreen) () bool {
	return false
}

func ( *simscreen) (int, int, int, int) {}

func ( *simscreen) (Key) bool {
	return true
}

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

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

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

func ( *simscreen) () (Tty, bool) {
	return nil, false
}

func ( *simscreen) () *CellBuffer {
	return &.back
}

func ( *simscreen) () chan Event {
	return .evch
}

func ( *simscreen) () <-chan struct{} {
	return .quit
}

func ( *simscreen) ( string) {
	.title = 
}

func ( *simscreen) () string {
	return .title
}

func ( *simscreen) ( []byte) {
	.clipboard = 
}

func ( *simscreen) () {
	if .clipboard != nil {
		 := NewEventClipboard(.clipboard)
		.postEvent()
	}
}

func ( *simscreen) () []byte {
	return .clipboard
}