package cleanimage

import (
	"fmt"
	"strings"
)

// KittyImageCleaner provides methods to generate Kitty graphics protocol
// commands for deleting images.
type KittyImageCleaner struct{}

// NewKittyImageCleaner creates a new instance of KittyImageCleaner.
func NewKittyImageCleaner() *KittyImageCleaner {
	return &KittyImageCleaner{}
}

// buildCommand constructs the base Kitty graphics protocol command.
func (kic *KittyImageCleaner) buildCommand(params map[string]string) string {
	var sb strings.Builder
	sb.WriteString("\033_Ga=d") // Start with the delete action

	if len(params) > 0 {
		var paramStrings []string
		for key, value := range params {
			paramStrings = append(paramStrings, fmt.Sprintf("%s=%s", key, value))
		}
		sb.WriteString(",")
		sb.WriteString(strings.Join(paramStrings, ","))
	}

	sb.WriteString("\033\\") // End the command
	return sb.String()
}

// DeleteAllVisiblePlacements deletes all images visible on screen.
// 'freeData' determines if the underlying image data should also be freed.
func (kic *KittyImageCleaner) DeleteAllVisiblePlacements(freeData bool) string {
	if freeData {
		return kic.buildCommand(map[string]string{"d": "A"})
	}
	return kic.buildCommand(map[string]string{"d": "a"})
}

// DeleteByID deletes images with a specific ID.
// 'imageID' is the ID of the image to delete.
// 'placementID' is an optional placement ID. If 0, all placements with the imageID are deleted.
// 'freeData' determines if the underlying image data should also be freed.
func (kic *KittyImageCleaner) DeleteByID(imageID int, placementID int, freeData bool) string {
	params := make(map[string]string)
	if freeData {
		params["d"] = "I"
	} else {
		params["d"] = "i"
	}
	params["i"] = fmt.Sprintf("%d", imageID)
	if placementID != 0 {
		params["p"] = fmt.Sprintf("%d", placementID)
	}
	return kic.buildCommand(params)
}

// DeleteNewestByID deletes the newest image with a specified number (ID).
// 'imageNumber' is the number (ID) of the newest image to delete.
// 'placementID' is an optional placement ID. If 0, all placements with the imageNumber are deleted.
// 'freeData' determines if the underlying image data should also be freed.
func (kic *KittyImageCleaner) DeleteNewestByID(imageNumber int, placementID int, freeData bool) string {
	params := make(map[string]string)
	if freeData {
		params["d"] = "N"
	} else {
		params["d"] = "n"
	}
	params["I"] = fmt.Sprintf("%d", imageNumber) // Note: Kitty uses 'I' for number here
	if placementID != 0 {
		params["p"] = fmt.Sprintf("%d", placementID)
	}
	return kic.buildCommand(params)
}

// DeleteByCursorPosition deletes all placements that intersect with the current cursor position.
// 'freeData' determines if the underlying image data should also be freed.
func (kic *KittyImageCleaner) DeleteByCursorPosition(freeData bool) string {
	if freeData {
		return kic.buildCommand(map[string]string{"d": "C"})
	}
	return kic.buildCommand(map[string]string{"d": "c"})
}

// DeleteAnimationFrames deletes animation frames.
// 'freeData' determines if the underlying image data should also be freed.
func (kic *KittyImageCleaner) DeleteAnimationFrames(freeData bool) string {
	if freeData {
		return kic.buildCommand(map[string]string{"d": "F"})
	}
	return kic.buildCommand(map[string]string{"d": "f"})
}

// DeleteByCellPosition deletes all placements that intersect a specific cell.
// 'x', 'y' are the coordinates of the cell (1-indexed).
// 'freeData' determines if the underlying image data should also be freed.
func (kic *KittyImageCleaner) DeleteByCellPosition(x, y int, freeData bool) string {
	params := map[string]string{
		"x": fmt.Sprintf("%d", x),
		"y": fmt.Sprintf("%d", y),
	}
	if freeData {
		params["d"] = "P"
	} else {
		params["d"] = "p"
	}
	return kic.buildCommand(params)
}

// DeleteByCellAndZIndex deletes all placements that intersect a specific cell
// and have a specific z-index.
// 'x', 'y' are the coordinates of the cell (1-indexed).
// 'zIndex' is the z-index of the placements to delete.
// 'freeData' determines if the underlying image data should also be freed.
func (kic *KittyImageCleaner) DeleteByCellAndZIndex(x, y, zIndex int, freeData bool) string {
	params := map[string]string{
		"x": fmt.Sprintf("%d", x),
		"y": fmt.Sprintf("%d", y),
		"z": fmt.Sprintf("%d", zIndex),
	}
	if freeData {
		params["d"] = "Q"
	} else {
		params["d"] = "q"
	}
	return kic.buildCommand(params)
}

// DeleteByIDRange deletes all images whose ID is within a specified range.
// 'minID' is the minimum ID (inclusive).
// 'maxID' is the maximum ID (inclusive).
// 'freeData' determines if the underlying image data should also be freed.
// (Requires Kitty version 0.33.0 or later)
func (kic *KittyImageCleaner) DeleteByIDRange(minID, maxID int, freeData bool) string {
	params := map[string]string{
		"x": fmt.Sprintf("%d", minID),
		"y": fmt.Sprintf("%d", maxID),
	}
	if freeData {
		params["d"] = "R"
	} else {
		params["d"] = "r"
	}
	return kic.buildCommand(params)
}

// DeleteByColumn deletes all placements that intersect the specified column.
// 'column' is the column number (1-indexed).
// 'freeData' determines if the underlying image data should also be freed.
func (kic *KittyImageCleaner) DeleteByColumn(column int, freeData bool) string {
	params := map[string]string{
		"x": fmt.Sprintf("%d", column),
	}
	if freeData {
		params["d"] = "X"
	} else {
		params["d"] = "x"
	}
	return kic.buildCommand(params)
}

// DeleteByRow deletes all placements that intersect the specified row.
// 'row' is the row number (1-indexed).
// 'freeData' determines if the underlying image data should also be freed.
func (kic *KittyImageCleaner) DeleteByRow(row int, freeData bool) string {
	params := map[string]string{
		"y": fmt.Sprintf("%d", row),
	}
	if freeData {
		params["d"] = "Y"
	} else {
		params["d"] = "y"
	}
	return kic.buildCommand(params)
}

// DeleteByZIndex deletes all placements that have the specified z-index.
// 'zIndex' is the z-index of the placements to delete.
// 'freeData' determines if the underlying image data should also be freed.
func (kic *KittyImageCleaner) DeleteByZIndex(zIndex int, freeData bool) string {
	params := map[string]string{
		"z": fmt.Sprintf("%d", zIndex),
	}
	if freeData {
		params["d"] = "Z"
	} else {
		params["d"] = "z"
	}
	return kic.buildCommand(params)
}

func main() {
	cleaner := NewKittyImageCleaner()

	// Example usage:
	fmt.Println("Kitty Image Cleaning Commands:")
	fmt.Println("-------------------------------")

	// <ESC>_Ga=d<ESC>\              # delete all visible placements
	fmt.Println("Delete all visible placements (no data free):",
		cleaner.DeleteAllVisiblePlacements(false))

	// <ESC>_Ga=d,d=A<ESC>\          # delete all visible placements, freeing data
	fmt.Println("Delete all visible placements (with data free):",
		cleaner.DeleteAllVisiblePlacements(true))

	// <ESC>_Ga=d,d=i,i=10<ESC>\     # delete the image with id=10, without freeing data
	fmt.Println("Delete image with ID 10 (no data free):",
		cleaner.DeleteByID(10, 0, false))

	// <ESC>_Ga=d,d=I,i=10<ESC>\     # delete the image with id=10, freeing data
	fmt.Println("Delete image with ID 10 (with data free):",
		cleaner.DeleteByID(10, 0, true))

	// <ESC>_Ga=d,d=i,i=10,p=7<ESC>\ # delete the image with id=10 and placement id=7, without freeing data
	fmt.Println("Delete placement 7 of image ID 10 (no data free):",
		cleaner.DeleteByID(10, 7, false))

	// <ESC>_Ga=d,d=I,i=10,p=7<ESC>\ # delete the image with id=10 and placement id=7, freeing data
	fmt.Println("Delete placement 7 of image ID 10 (with data free):",
		cleaner.DeleteByID(10, 7, true))

	// <ESC>_Ga=d,d=Z,z=-1<ESC>\     # delete the placements with z-index -1, also freeing up image data
	fmt.Println("Delete placements with z-index -1 (with data free):",
		cleaner.DeleteByZIndex(-1, true))

	// <ESC>_Ga=d,d=z,z=0<ESC>\      # delete the placements with z-index 0, without freeing data
	fmt.Println("Delete placements with z-index 0 (no data free):",
		cleaner.DeleteByZIndex(0, false))

	// <ESC>_Ga=d,d=p,x=3,y=4<ESC>\  # delete all placements that intersect the cell at (3, 4), without freeing data
	fmt.Println("Delete placements at cell (3,4) (no data free):",
		cleaner.DeleteByCellPosition(3, 4, false))

	// <ESC>_Ga=d,d=P,x=5,y=6<ESC>\  # delete all placements that intersect the cell at (5, 6), freeing data
	fmt.Println("Delete placements at cell (5,6) (with data free):",
		cleaner.DeleteByCellPosition(5, 6, true))

	fmt.Println("Delete placements intersecting cursor (no data free):",
		cleaner.DeleteByCursorPosition(false))

	fmt.Println("Delete placements intersecting column 10 (with data free):",
		cleaner.DeleteByColumn(10, true))

	fmt.Println("Delete placements intersecting row 5 (no data free):",
		cleaner.DeleteByRow(5, false))

	fmt.Println("Delete images with ID range 100-200 (with data free):",
		cleaner.DeleteByIDRange(100, 200, true))

	fmt.Println("Delete placements at cell (1,1) with z-index 10 (no data free):",
		cleaner.DeleteByCellAndZIndex(1, 1, 10, false))
}