// Copyright 2025 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 (
	
)

type cell struct {
	currStr   string
	lastStr   string
	currStyle Style
	lastStyle Style
	width     int
	lock      bool
}

func ( *cell) ( bool) {
	if  {
		.lastStr = ""
	} else {
		if .currStr == "" {
			.currStr = " "
		}
		.lastStr = .currStr
		.lastStyle = .currStyle
	}
}

// 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.
//
// Deprecated: Use Put instead, which this is implemented in terms of.
func ( *CellBuffer) ( int,  int,  rune,  []rune,  Style) {
	.Put(, , string(append([]rune{}, ...)), )
}

// Put a single styled grapheme using the given string and style
// at the same location.  Note that only the first grapheme in the string
// will bre displayed, using only the 1 or 2 (depending on width) cells
// located at x, y. It returns the rest of the string, and the width used.
func ( *CellBuffer) ( int,  int,  string,  Style) (string, int) {
	var  int = 0
	if  >= 0 &&  >= 0 &&  < .w &&  < .h {
		var  string
		 := &.cells[(*.w)+]
		 := -1
		for  == 0 &&  != "" {
			var  string
			, , ,  = uniseg.FirstGraphemeClusterInString(, )
			 += 
			if  == "" {
				break
			}
		}

		// 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  > 0 &&  != .currStr {
			// Prevent unnecessary boundchecks for first cell, since we already
			// received that one.
			.setDirty(true)
			for  := 1;  < ; ++ {
				.SetDirty(+, , true)
			}
		}

		.currStr = 
		.width = 

		if .fg == ColorNone {
			.fg = .currStyle.fg
		}
		if .bg == ColorNone {
			.bg = .currStyle.bg
		}
		.currStyle = 
	}
	return , 
}

// Get the contents of a character cell (or two adjacent cells), including the
// the style and the display width in cells.  (The width can be either 1, normally,
// or 2 for East Asian full-width characters.  If the width is 0, then the cell is
// is empty.)
func ( *CellBuffer) (,  int) (string, Style, int) {
	var  Style
	var  int
	var  string
	if  >= 0 &&  >= 0 &&  < .w &&  < .h {
		 := &.cells[(*.w)+]
		,  = .currStr, .currStyle
		if  = .width;  == 0 ||  == "" {
			 = 1
			 = " "
		}
	}
	return , , 
}

// 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.)
//
// Deprecated: Use Get, which this implemented in terms of.
func ( *CellBuffer) (,  int) (rune, []rune, Style, int) {
	var  Style
	var  int
	var  rune
	var  []rune
	, ,  := .Get(, )
	for ,  := range  {
		if  == 0 {
			 = 
		} else {
			 = append(, )
		}
	}
	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[].lastStr = ""
	}
}

// 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 .lastStyle != .currStyle {
			return true
		}
		if .lastStr != .currStr {
			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)+]
		.setDirty()
	}
}

// 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)+]
			 := &[(*)+]
			.currStr = .currStr
			.currStyle = .currStyle
			.width = .width
			.lastStr = ""
		}
	}
	.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[]
		.currStr = string()
		 := 
		if .fg == ColorNone {
			.fg = .currStyle.fg
		}
		if .bg == ColorNone {
			.bg = .currStyle.bg
		}
		.currStyle = 
		.width = 1
	}
}