package readline

import (
	"fmt"

	ansi "github.com/acarl005/stripansi"
	"github.com/rivo/uniseg"
)

// SetPrompt will define the readline prompt string.
// It also calculates the runes in the string as well as any non-printable escape codes.
func (rl *Instance) SetPrompt(s string) {
	rl.mainPrompt = s
	rl.computePrompt()
}

// SetRightPrompt sets the right prompt.
func (rl *Instance) SetRightPrompt(s string) {
	rl.rightPrompt = s + " "
	rl.computePrompt()
}

// RefreshPromptLog - A simple function to print a string message (a log, or more broadly,
// an asynchronous event) without bothering the user, and by "pushing" the prompt below the message.
func (rl *Instance) RefreshPromptLog(log string) (err error) {

	// We adjust cursor movement, depending on which mode we're currently in.
	if !rl.modeTabCompletion {
		rl.tcUsedY = 1
		// Account for the info line
	} else if rl.modeTabCompletion && rl.modeAutoFind {
		rl.tcUsedY = 0
	} else {
		rl.tcUsedY = 1
	}

	// Prompt offset
	if rl.Multiline {
		rl.tcUsedY += 1
	} else {
		rl.tcUsedY += 0
	}

	// Clear the current prompt and everything below
	print(seqClearLine)
	if rl.stillOnRefresh {
		moveCursorUp(1)
	}
	rl.stillOnRefresh = true
	moveCursorUp(rl.infoY + rl.tcUsedY)
	moveCursorBackwards(GetTermWidth())
	print("\r\n" + seqClearScreenBelow)

	// Print the log
	fmt.Printf(log)

	// Add a new line between the message and the prompt, so not overloading the UI
	print("\n")

	// Print the prompt
	if rl.Multiline {
		rl.tcUsedY += 3
		fmt.Println(rl.mainPrompt)

	} else {
		rl.tcUsedY += 2
		fmt.Print(rl.mainPrompt)
	}

	// Refresh the line
	rl.updateHelpers()

	return
}

// RefreshPromptInPlace - Refreshes the prompt in the very same place he is.
func (rl *Instance) RefreshPromptInPlace(prompt string) (err error) {
	// We adjust cursor movement, depending on which mode we're currently in.
	// Prompt data intependent
	if !rl.modeTabCompletion {
		rl.tcUsedY = 1
		// Account for the info line
	} else if rl.modeTabCompletion && rl.modeAutoFind {
		rl.tcUsedY = 0
	} else {
		rl.tcUsedY = 1
	}

	// Update the prompt if a special has been passed.
	if prompt != "" {
		rl.SetPrompt(prompt)
	}

	if rl.Multiline {
		rl.tcUsedY += 1
	}

	// Clear the input line and everything below
	print(seqClearLine)
	moveCursorUp(rl.infoY + rl.tcUsedY)
	moveCursorBackwards(GetTermWidth())
	print("\r\n" + seqClearScreenBelow)

	// Add a new line if needed
	if rl.Multiline {
		fmt.Println(rl.mainPrompt)

	} else {
		fmt.Print(rl.mainPrompt)
	}

	// Refresh the line
	rl.updateHelpers()

	return
}

// RefreshPromptCustom - Refresh the console prompt with custom values.
// @prompt      => If not nil (""), will use this prompt instead of the currently set prompt.
// @offset      => Used to set the number of lines to go upward, before reprinting. Set to 0 if not used.
// @clearLine   => If true, will clean the current input line on the next refresh.
func (rl *Instance) RefreshPromptCustom(prompt string, offset int, clearLine bool) (err error) {

	// We adjust cursor movement, depending on which mode we're currently in.
	if !rl.modeTabCompletion {
		rl.tcUsedY = 1
	} else if rl.modeTabCompletion && rl.modeAutoFind { // Account for the info line
		rl.tcUsedY = 0
	} else {
		rl.tcUsedY = 1
	}

	// Add user-provided offset
	rl.tcUsedY += offset

	// Go back to prompt position, then up to the user provided offset.
	moveCursorBackwards(GetTermWidth())
	moveCursorUp(rl.posY)
	moveCursorUp(offset)

	// Then clear everything below our new position
	print(seqClearScreenBelow)

	// Update the prompt if a special has been passed.
	if prompt != "" {
		rl.SetPrompt(prompt)
	}

	// Add a new line if needed
	if rl.Multiline && prompt == "" {
	} else if rl.Multiline {
		fmt.Println(rl.mainPrompt)
	} else {
		fmt.Print(rl.mainPrompt)
	}

	// Refresh the line
	rl.updateHelpers()

	// If input line was empty, check that we clear it from detritus
	// The three lines are borrowed from clearLine(), we don't need more.
	if clearLine {
		rl.clearLine()
	}

	return
}

// computePrompt - At any moment, returns an (1st or 2nd line) actualized prompt,
// considering all input mode parameters and prompt string values.
func (rl *Instance) computePrompt() (prompt []rune) {
	if rl.Multiline {
		if rl.MultilinePrompt != "" {
			rl.realPrompt = []rune(rl.MultilinePrompt)
		} else {
			rl.realPrompt = []rune{} //rl.defaultPrompt
		}
	}
	if !rl.Multiline {
		if rl.mainPrompt != "" {
			rl.realPrompt = []rune(rl.mainPrompt)
		}
		// We add the multiline prompt anyway, because it might be empty and thus have
		// no effect on our user interface, or be specified and thus needed.
		// if rl.MultilinePrompt != "" {
		rl.realPrompt = append(rl.realPrompt, []rune(rl.MultilinePrompt)...)
		// } else {
		//         rl.realPrompt = append(rl.realPrompt, rl.defaultPrompt...)
		// }
	}

	// Strip color escapes
	rl.promptLen = getRealLength(string(rl.realPrompt))
	rl.rightPromptLen = getRealLength(string(rl.rightPrompt))
	
	return
}

func (rl *Instance) colorizeVimPrompt(p []rune) (cp []rune) {
	if rl.VimModeColorize {
		return []rune(fmt.Sprintf("%s%s%s", BOLD, string(p), RESET))
	}

	return p
}

// getRealLength - Some strings will have ANSI escape codes, which might be wrongly
// interpreted as legitimate parts of the strings. This will bother if some prompt
// components depend on other's length, so we always pass the string in this for
// getting its real-printed length.
func getRealLength(s string) (l int) {
	stripped := ansi.Strip(s)
	return uniseg.GraphemeClusterCount(stripped)
}

func (rl *Instance) echoRightPrompt() {
	if rl.fullX < GetTermWidth() - rl.rightPromptLen - 1 {
		moveCursorForwards(GetTermWidth())
		moveCursorBackwards(rl.rightPromptLen)
		print(rl.rightPrompt)
	}
}