// 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 (
	
	

	runewidth 
)

type cell struct {
	currMain  rune
	currComb  []rune
	currStyle Style
	lastMain  rune
	lastStyle Style
	lastComb  []rune
	width     int
	lock      bool
}

// CellBuffer represents a two-dimensional array of character cells.
// This is primarily intended for use by Screen implementors; it
// contains much of the common code they need.  To create one, just
// declare a variable of its type; no explicit initialization is necessary.
//
// CellBuffer is not thread safe.
type CellBuffer struct {
	w     int
	h     int
	cells []cell
}

// SetContent sets the contents (primary rune, combining runes,
// and style) for a cell at a given location.  If the background or
// foreground of the style is set to ColorNone, then the respective
// color is left un changed.
func ( *CellBuffer) ( int,  int,
	 rune,  []rune,  Style,
) {
	if  >= 0 &&  >= 0 &&  < .w &&  < .h {
		 := &.cells[(*.w)+]

		// Wide characters: we want to mark the "wide" cells
		// dirty as well as the base cell, to make sure we consider
		// both cells as dirty together.  We only need to do this
		// if we're changing content
		if (.width > 0) && ( != .currMain || len() != len(.currComb) || (len() > 0 && !reflect.DeepEqual(, .currComb))) {
			for  := 0;  < .width; ++ {
				.SetDirty(+, , true)
			}
		}

		.currComb = append([]rune{}, ...)

		if .currMain !=  {
			.width = runewidth.RuneWidth()
		}
		.currMain = 
		if .fg == ColorNone {
			.fg = .currStyle.fg
		}
		if .bg == ColorNone {
			.bg = .currStyle.bg
		}
		.currStyle = 
	}
}

// GetContent returns the contents of a character cell, including the
// primary rune, any combining character runes (which will usually be
// nil), the style, and the display width in cells.  (The width can be
// either 1, normally, or 2 for East Asian full-width characters.)
func ( *CellBuffer) (,  int) (rune, []rune, Style, int) {
	var  rune
	var  []rune
	var  Style
	var  int
	if  >= 0 &&  >= 0 &&  < .w &&  < .h {
		 := &.cells[(*.w)+]
		, ,  = .currMain, .currComb, .currStyle
		if  = .width;  == 0 ||  < ' ' {
			 = 1
			 = ' '
		}
	}
	return , , , 
}

// Size returns the (width, height) in cells of the buffer.
func ( *CellBuffer) () (int, int) {
	return .w, .h
}

// Invalidate marks all characters within the buffer as dirty.
func ( *CellBuffer) () {
	for  := range .cells {
		.cells[].lastMain = rune(0)
	}
}

// Dirty checks if a character at the given location needs to be
// refreshed on the physical display.  This returns true if the cell
// content is different since the last time it was marked clean.
func ( *CellBuffer) (,  int) bool {
	if  >= 0 &&  >= 0 &&  < .w &&  < .h {
		 := &.cells[(*.w)+]
		if .lock {
			return false
		}
		if .lastMain == rune(0) {
			return true
		}
		if .lastMain != .currMain {
			return true
		}
		if .lastStyle != .currStyle {
			return true
		}
		if len(.lastComb) != len(.currComb) {
			return true
		}
		for  := range .lastComb {
			if .lastComb[] != .currComb[] {
				return true
			}
		}
	}
	return false
}

// SetDirty is normally used to indicate that a cell has
// been displayed (in which case dirty is false), or to manually
// force a cell to be marked dirty.
func ( *CellBuffer) (,  int,  bool) {
	if  >= 0 &&  >= 0 &&  < .w &&  < .h {
		 := &.cells[(*.w)+]
		if  {
			.lastMain = rune(0)
		} else {
			if .currMain == rune(0) {
				.currMain = ' '
			}
			.lastMain = .currMain
			.lastComb = .currComb
			.lastStyle = .currStyle
		}
	}
}

// LockCell locks a cell from being drawn, effectively marking it "clean" until
// the lock is removed. This can be used to prevent tcell from drawing a given
// cell, even if the underlying content has changed. For example, when drawing a
// sixel graphic directly to a TTY screen an implementer must lock the region
// underneath the graphic to prevent tcell from drawing on top of the graphic.
func ( *CellBuffer) (,  int) {
	if  < 0 ||  < 0 {
		return
	}
	if  >= .w ||  >= .h {
		return
	}
	 := &.cells[(*.w)+]
	.lock = true
}

// UnlockCell removes a lock from the cell and marks it as dirty
func ( *CellBuffer) (,  int) {
	if  < 0 ||  < 0 {
		return
	}
	if  >= .w ||  >= .h {
		return
	}
	 := &.cells[(*.w)+]
	.lock = false
	.SetDirty(, , true)
}

// Resize is used to resize the cells array, with different dimensions,
// while preserving the original contents.  The cells will be invalidated
// so that they can be redrawn.
func ( *CellBuffer) (,  int) {
	if .h ==  && .w ==  {
		return
	}

	 := make([]cell, *)
	for  := 0;  <  &&  < .h; ++ {
		for  := 0;  <  &&  < .w; ++ {
			 := &.cells[(*.w)+]
			 := &[(*)+]
			.currMain = .currMain
			.currComb = .currComb
			.currStyle = .currStyle
			.width = .width
			.lastMain = rune(0)
		}
	}
	.cells = 
	.h = 
	.w = 
}

// Fill fills the entire cell buffer array with the specified character
// and style.  Normally choose ' ' to clear the screen.  This API doesn't
// support combining characters, or characters with a width larger than one.
// If either the foreground or background are ColorNone, then the respective
// color is unchanged.
func ( *CellBuffer) ( rune,  Style) {
	for  := range .cells {
		 := &.cells[]
		.currMain = 
		.currComb = nil
		 := 
		if .fg == ColorNone {
			.fg = .currStyle.fg
		}
		if .bg == ColorNone {
			.bg = .currStyle.bg
		}
		.currStyle = 
		.width = 1
	}
}

var runeConfig *runewidth.Condition

func init() {
	// The defaults for the runewidth package are poorly chosen for terminal
	// applications.  We however will honor the setting in the environment if
	// it is set.
	if os.Getenv("RUNEWIDTH_EASTASIAN") == "" {
		runewidth.DefaultCondition.EastAsianWidth = false
	}
}