package door

type SpinRite struct {
	Width     uint8   // Width of the animation (must be odd)
	Length    uint8   // Width of the worm (must be odd)
	Color     string  // Color (default CYAN ON BLUE)
	OutputB   []byte  // Output CP437/bytes (Width)
	OutputR   []rune  // Output Unicode (Width)
	Buffer    []uint8 // Output calculate buffer (Width)
	Runes     []rune  // blank, center, top, bottom, both
	Bytes     []byte  // blank, center, top, bottom, both
	CenterPos uint8   // Center Position (Width - 1) / 2
	StartPos  uint8   // Starting Position (Length -1) / 2
	Index     uint8   // Index of current iteration
}

func SpinRiteInit(width uint8, length uint8, color string) SpinRite {
	if color == "" {
		color = ColorText("CYAN ON BLUE")
	}
	var center uint8 = ((width - 1) / 2)
	var start uint8 = ((length - 1) / 2)
	var result SpinRite = SpinRite{Width: width,
		Length:    length,
		Color:     color,
		CenterPos: center,
		StartPos:  start,
		Buffer:    make([]uint8, width),
		Index:     0}
	if Unicode {
		result.OutputR = make([]rune, width)
		result.Runes = []rune{' ', '\u221e', '\u2580', '\u2584', '\u2588'}
	} else {
		result.OutputB = make([]byte, width)
		result.Bytes = []byte{' ', 0xec, 0xdf, 0xdc, 0xdb}
	}
	return result
}

func (sr *SpinRite) PosToIndex(pos int8, top bool) (index uint8, level bool) {
	if top {
		if pos >= int8(sr.Width) {
			// wrap around
			level = !top
			idx := int8(sr.Width) + (int8(sr.Width) - (pos + 1))
			index = uint8(idx)
		} else {
			level = top
			index = uint8(pos)
		}
	} else {
		// bottom
		if pos < 0 {
			level = !top
			pos++
			index = uint8(-pos)
		} else {
			level = top
			index = uint8(pos)
		}
	}
	return
}

func (sr *SpinRite) Calculate() {
	for i := range sr.Buffer {
		sr.Buffer[i] = 0
	}
	sr.Buffer[sr.CenterPos] = 1

	// [    S C s    ]
	var top int8 = int8(sr.CenterPos) - int8(sr.StartPos) + int8(sr.Index)
	var bottom int8 = int8(sr.CenterPos) + int8(sr.StartPos) - int8(sr.Index)

	var l int8
	for l = 0; l < int8(sr.Length); l++ {
		var index uint8
		var level bool
		index, level = sr.PosToIndex(top+l, true)
		// log.Println("TOP", l, top+l, index, level)
		if level {
			sr.Buffer[index] |= 0x02
		} else {
			sr.Buffer[index] |= 0x04
		}
		index, level = sr.PosToIndex(bottom-l, false)
		// log.Println("BOT", l, bottom-l, index, level)
		if level {
			sr.Buffer[index] |= 0x02
		} else {
			sr.Buffer[index] |= 0x04
		}
	}

	// log.Println("Index", sr.Index, "Buffer", sr.Buffer)

	var reset bool = true

	// Convert from buffer to output
	if Unicode {
		for l = 0; l < int8(sr.Width); l++ {
			switch sr.Buffer[l] {
			case 0:
				sr.OutputR[l] = sr.Runes[0]
			case 1:
				sr.OutputR[l] = sr.Runes[1]
				reset = false
			case 2, 3:
				sr.OutputR[l] = sr.Runes[2]
				reset = false
			case 4, 5:
				sr.OutputR[l] = sr.Runes[3]
				reset = false
			case 6, 7:
				sr.OutputR[l] = sr.Runes[4]
			}
		}
	} else {
		for l = 0; l < int8(sr.Width); l++ {
			switch sr.Buffer[l] {
			case 0:
				sr.OutputB[l] = sr.Bytes[0]
			case 1:
				sr.OutputB[l] = sr.Bytes[1]
				reset = false
			case 2, 3:
				sr.OutputB[l] = sr.Bytes[2]
				reset = false
			case 4, 5:
				sr.OutputB[l] = sr.Bytes[3]
				reset = false
			case 6, 7:
				sr.OutputB[l] = sr.Bytes[4]
			}
		}
	}

	if reset {
		// log.Println("RESET")
		sr.Index = 0
	}
}

func (sr *SpinRite) Output() string {
	var result string

	sr.Calculate()
	if Unicode {
		result = string(sr.OutputR)
	} else {
		result = string(sr.OutputB)
	}
	sr.Index++

	return sr.Color + result
}

type SpinRiteMsg struct {
	SpinRite
	Messages []string
	MsgIndex int
	Next     bool
}

func SpinRiteMsgInit(width uint8, length uint8, color string, messages []string) SpinRiteMsg {
	var result SpinRiteMsg = SpinRiteMsg{SpinRite: SpinRiteInit(width, length, color)}
	if Unicode {
		result.Runes[1] = result.Runes[0]
	} else {
		result.SpinRite.Bytes[1] = result.SpinRite.Bytes[0]
	}
	result.Messages = messages
	result.MsgIndex = 0
	result.Next = false
	return result
}

func (sr *SpinRiteMsg) Output() string {
	var result string

	sr.Calculate()
	if sr.Next && sr.Index == 0 {
		sr.MsgIndex++
		if sr.MsgIndex == len(sr.Messages) {
			sr.MsgIndex = 0
		}
	}
	if sr.Index != 0 {
		sr.Next = true
	}
	// Place message
	var msg string = sr.Messages[sr.MsgIndex]
	var pos int = int(sr.CenterPos) - (len(msg) / 2)

	for i := 0; i < len(msg); i++ {
		if Unicode {
			if sr.OutputR[pos+i] == ' ' {
				sr.OutputR[pos+i] = []rune(msg)[i]
			}
		} else {
			if sr.OutputB[pos+i] == ' ' {
				sr.OutputB[pos+i] = byte(msg[i])
			}
		}
	}

	if Unicode {
		result = string(sr.OutputR)
	} else {
		result = string(sr.OutputB)
	}
	sr.Index++

	return sr.Color + result
}

/*
Or possibly: · ∞
[    █████    ]
[   ▄▄███▀▀   ]
[  ▄▄▄▄█▀▀▀▀  ]
[ ▄▄▄▄▄•▀▀▀▀▀ ]
[▄▄▄▄▄ • ▀▀▀▀▀]
[█▄▄▄  •  ▀▀▀█]
[██▄   •   ▀██]
[██▀   •   ▄██]
[█▀▀▀  •  ▄▄▄█]
[█▀▀▀▀ • ▄▄▄▄█]
[ ▀▀▀▀▀•▄▄▄▄▄ ]
[  ▀▀▀▀█▄▄▄▄  ]
[   ▀▀███▄▄   ]
*/