package main

import (
	"bytes"
	"fmt"
	"strconv"
	"strings"
)

type BlockFont struct {
	characters []int
	data       [][][]byte
}

/*
func BytesHexed(output []byte) {
	fmt.Printf("BH: ")
	for _, ch := range output {
		fmt.Printf("%02x ", ch)
	}
	fmt.Println("")
}

func BytesArrayHexed(output *[][]byte) {
	for _, out := range *output {
		BytesHexed(out)
	}
}
*/

// Make []strings all the same length
func normalizeBlock(block *[][]byte) {
	var max int
	// StringO(block)
	for _, line := range *block {
		l := len(line)
		if max < l {
			max = l
		}
	}
	// fmt.Printf("max = %d\n", max)
	for idx, _ := range *block {
		// l := len(line)
		for len((*block)[idx]) < max {
			//(*block)[idx] += byte(0x20) //append((*block)[idx], byte{0x20})
			(*block)[idx] = append((*block)[idx], byte(0x20))
		}
	}
	// StringO(block)
	// fmt.Println("== normalized ==")
}

// Get Character information from BlockFont return Normalize
func (bf *BlockFont) GetCharacter(c int) [][]byte {
	var result [][]byte

	if c == 32 {
		// Space character
		result = append(result, []byte{0x20, 0x20})
		return result
	}

	// 33-126 are the possible characters.
	if c >= 33 && c <= 126 {
		c -= 33
		idx := bf.characters[c]
		if idx == -1 {
			// This character is not supported by this font.
			return result
		}
		result = bf.data[bf.characters[c]]
		// fmt.Println("normalizeCharBlock")
		normalizeBlock(&result)
		// fmt.Println("normalizeChar done")
		return result
	} else {
		return result
	}
}

// expandBlock the number of strings, and make it match the length of [0].
func __expandBlock(block *[]string) {
	// l := len((*block)[0])
	l := len([]rune((*block)[0]))
	*block = append(*block, strings.Repeat(" ", l))
}

func expandBlock(block *[][]byte) {
	// l := len((*block)[0])
	l := len((*block)[0])
	blank := []byte{0x20}
	*block = append(*block, bytes.Repeat(blank, l))
}

// Given text, translate to thedraw font.
func (bf *BlockFont) Output(input string) ([][]byte, int) {
	var out [][]byte
	var max int

	for _, ch := range input {
		// fmt.Printf("%d %x\n", ch, ch)
		block := bf.GetCharacter(int(ch))
		// fmt.Println("ok")
		l := len(block)
		max += l
		if l != 0 {
			if len(out) == 0 {
				// First time
				for _, b := range block {
					out = append(out, b)
				}
			} else {
				if len(out) != 0 {
					for l > len(out) {
						// We need to expand the out
						// fmt.Println("expandBlock")
						expandBlock(&out)
					}
				}
				// fmt.Println("Append block to out")

				// Ok, we have something!
				for idx, b := range block {
					out[idx] = append(out[idx], byte(0x20))
					// out[idx] = append(out[idx], b)

					for _, inner := range b {
						out[idx] = append(out[idx], inner)
					}
					// out[idx] += byte(0x20) + b
					// fmt.Printf("%s\n", CP437_to_Unicode(b))
				}
			}
		}
		// fmt.Println("normalizeBlock")
		normalizeBlock(&out)
		// fmt.Println("normalizeBlock done")
	}
	return out, len(out[0])
}

type ColorFont struct {
	characters []int
	data       [][][]byte
}

// Make []strings all the same length
func ___normalizeColor(block *[]string) int {
	var max int
	for _, line := range *block {
		l := len([]rune(line))
		if max < l {
			max = l
		}
	}
	for idx, line := range *block {
		l := len([]rune(line))
		if l < max {
			(*block)[idx] += strings.Repeat(" \x0f", max-l/2)
		}
	}
	return max
}

func normalizeColor(block *[][]byte) int {
	var max int
	for _, line := range *block {
		l := len(line)
		if max < l {
			max = l
		}
	}
	for idx, _ := range *block {
		// l := len(line)
		blank := []byte{0x20, 0x0f}
		for len((*block)[idx]) < max {
			(*block)[idx] = append((*block)[idx], blank...)

			/*
				for _, b := range blank {
					(*block)[idx] = append((*block)[idx], b)
				}
			*/
			// (*block)[idx] += strings.Repeat(" \x0f", max-l/2)
		}
	}
	return max
}

func (cf *ColorFont) GetCharacter(c int) ([][]byte, int) {
	var result [][]byte

	if c == 32 {
		blank := []byte{0x20, 0x0f, 0x20, 0x0f}
		result = append(result, blank) // " \x0f \x0f")
		return result, len(result[0]) / 2
	}

	if c >= 33 && c <= 126 {
		c -= 33
		idx := cf.characters[c]
		if idx == -1 {
			return result, 0
		}
		result = cf.data[cf.characters[c]]
		// fmt.Println("Character:")
		// BytesArrayHexed(&result)
		// fmt.Println("normalizing...")
		max := normalizeColor(&result)
		// BytesArrayHexed(&result)
		// StringHexO(&result)
		return result, max
	} else {
		return result, 0
	}
}

/*
func expandColor(block *[]string) {
	l := len((*block)[0])
	*block = append(*block, strings.Repeat(" \x0f", l/2))
}
*/

func thedraw_to_ansi(c int) int {
	trans := []int{0, 4, 2, 6, 1, 5, 3, 7}
	return trans[c]
}

func colorout(c int) string {
	bg := thedraw_to_ansi(c / 16)
	fg := c % 16
	bold := (fg & 0x8) == 0x8
	fg = thedraw_to_ansi(fg & 0x7)
	if bold {
		return fmt.Sprintf("\x1b[0;1;%d;%dm", fg+30, bg+40)
	} else {
		return fmt.Sprintf("\x1b[0;%d;%dm", fg+30, bg+40)
	}
}

type ColorParts struct {
	Background int
	Foreground int
	Bold       bool
}

func ColorSplit(color int) ColorParts {
	return ColorParts{Background: thedraw_to_ansi(color / 16), Foreground: thedraw_to_ansi(color & 7), Bold: (color%16)&0x08 == 0x08}
}

func ColorOutput(previous int, color int) string {
	prev := ColorSplit(previous)
	c := ColorSplit(color)
	var codes []int8

	if c.Bold {
		if !prev.Bold {
			codes = append(codes, 1)
		}
	} else {
		if prev.Bold {
			// bold was set previously, there's only one way to reset it
			codes = append(codes, 0)
			prev.Background = 0
			prev.Foreground = 7
		}
	}

	if prev.Background == 0 && prev.Foreground == 0 {
		// output everything
		codes = append(codes, int8(c.Foreground)+30)
		codes = append(codes, int8(c.Background)+40)
	} else {
		if c.Foreground != prev.Foreground {
			codes = append(codes, int8(c.Foreground)+30)
		}
		if c.Background != prev.Background {
			codes = append(codes, int8(c.Background)+40)
		}
	}

	if len(codes) == 0 {
		// Everything matched
		return ""
	}
	var text []string
	for _, code := range codes {
		text = append(text, strconv.Itoa(int(code)))
	}

	return "\x1b[" + strings.Join(text, ";") + "m"
}

func Colorize(input []byte) []byte {
	var result []byte
	// runes := []rune(input)
	var previous int

	// BytesHexed(input)

	for pos := 0; pos < len(input); pos += 2 {
		ch := input[pos]
		color := int(input[pos+1])
		// fmt.Printf("%d : CH %d / %x, Color %d / %x\n", pos, ch, ch, color, color)
		colorstring := ColorOutput(previous, color)
		for _, c := range []byte(colorstring) {
			result = append(result, c)
		}

		// result = append(result, []byte(colorstring))

		// result += []byte(ColorOutput(previous, color))
		// result += ColorOutput(previous, color) + string(ch)
		result = append(result, ch)
		// result += string(ch)
		previous = color
		// result += colorout(color) + string(ch)
	}
	return result
}

func __expandColor(block *[]string, need int) {
	*block = append(*block, strings.Repeat(" \x0f", need))
}

func expandColor(block *[][]byte, need int) {
	blank := []byte{0x20, 0x0f}
	// *block = append(*block, strings.Repeat(" \x0f", need))
	*block = append(*block, bytes.Repeat(blank, need))
}

func (bf *ColorFont) Output(input string) ([][]byte, int) {
	var out [][]byte
	var max int

	for _, ch := range input {
		// fmt.Printf("%d %x\n", ch, ch)
		block, blklen := bf.GetCharacter(int(ch))
		if blklen == 0 {
			continue
		}

		l := len(block)
		max += blklen
		if l != 0 {
			if len(out) == 0 {
				// First time
				for _, b := range block {
					out = append(out, b)
				}
			} else {
				if len(out) != 0 {
					for l > len(out) {
						// We need to expand the out
						expandColor(&out, len(out[0])/2)
						// ExpandColor(&out)
					}
				}

				for len(out) > len(block) {
					// We need to expand the block out
					expandColor(&block, len(block)/2)
				}

				// Normalizing the character blocks
				normalizeColor(&block)

				if len(out) != len(block) {
					panic(fmt.Sprintf("len(out) %d != len(block) %d", len(out), len(block)))
				}

				// Ok, we have something!
				for idx, b := range block {
					// blank := []byte{0x20, 0x0f}
					out[idx] = append(out[idx], byte(0x20))
					out[idx] = append(out[idx], byte(0x0f))
					for _, inner := range b {
						out[idx] = append(out[idx], inner)
					}
					//out[idx] += " \x0f" + b
					// fmt.Printf("%s\n", CP437_to_Unicode(b))
				}
			}
			// NormalizeColor(&out)
		}

	}

	if len(out) == 0 {
		return out, 0
	}

	// StringHexO(&out)
	max = len(out[0]) / 2

	for idx, _ := range out {
		out[idx] = Colorize(out[idx])
	}
	return out, max
}

func CP437Bytes_to_Unicode(cp437 []byte) string {
	var result string

	for _, char := range cp437 {
		// fmt.Printf("%d, %x  ", char, char)
		switch int(char) {
		case 0x01:
			result += "\u263A"
		case 0x02:
			result += "\u263B"
		case 0x03:
			result += "\u2665"
		case 0x04:
			result += "\u2666"
		case 0x05:
			result += "\u2663"
		case 0x06:
			result += "\u2660"
		case 0x09:
			result += "\u25CB"
		case 0x0b:
			result += "\u2642"
		case 0x0c:
			result += "\u2640"
		case 0x0e:
			result += "\u266B"
		case 0x0f:
			result += "\u263C"
		case 0x10:
			result += "\u25BA"
		case 0x11:
			result += "\u25C4"
		case 0x12:
			result += "\u2195"
		case 0x13:
			result += "\u203C"
		case 0x14:
			result += "\xc2\xb6"
		case 0x15:
			result += "\xc2\xa7"
		case 0x16:
			result += "\u25AC"
		case 0x17:
			result += "\u21A8"
		case 0x18:
			result += "\u2191"
		case 0x19:
			result += "\u2193"
		case 0x1a:
			result += "\u2192"
		case 0x1c:
			result += "\u221F"
		case 0x1d:
			result += "\u2194"
		case 0x1e:
			result += "\u25B2"
		case 0x1f:
			result += "\u25BC"
		case 0x7f:
			result += "\u2302"
		case 0x80:
			result += "\xc3\x87"
		case 0x81:
			result += "\xc3\xbc"
		case 0x82:
			result += "\xc3\xa9"
		case 0x83:
			result += "\xc3\xa2"
		case 0x84:
			result += "\xc3\xa4"
		case 0x85:
			result += "\xc3\xa0"
		case 0x86:
			result += "\xc3\xa5"
		case 0x87:
			result += "\xc3\xa7"
		case 0x88:
			result += "\xc3\xaa"
		case 0x89:
			result += "\xc3\xab"
		case 0x8a:
			result += "\xc3\xa8"
		case 0x8b:
			result += "\xc3\xaf"
		case 0x8c:
			result += "\xc3\xae"
		case 0x8d:
			result += "\xc3\xac"
		case 0x8e:
			result += "\xc3\x84"
		case 0x8f:
			result += "\xc3\x85"
		case 0x90:
			result += "\xc3\x89"
		case 0x91:
			result += "\xc3\xa6"
		case 0x92:
			result += "\xc3\x86"
		case 0x93:
			result += "\xc3\xb4"
		case 0x94:
			result += "\xc3\xb6"
		case 0x95:
			result += "\xc3\xb2"
		case 0x96:
			result += "\xc3\xbb"
		case 0x97:
			result += "\xc3\xb9"
		case 0x98:
			result += "\xc3\xbf"
		case 0x99:
			result += "\xc3\x96"
		case 0x9a:
			result += "\xc3\x9c"
		case 0x9b:
			result += "\xc2\xa2"
		case 0x9c:
			result += "\xc2\xa3"
		case 0x9d:
			result += "\xc2\xa5"
		case 0x9e:
			result += "\u20A7"
		case 0x9f:
			result += "\u0192"
		case 0xa0:
			result += "\xc3\xa1"
		case 0xa1:
			result += "\xc3\xad"
		case 0xa2:
			result += "\xc3\xb3"
		case 0xa3:
			result += "\xc3\xba"
		case 0xa4:
			result += "\xc3\xb1"
		case 0xa5:
			result += "\xc3\x91"
		case 0xa6:
			result += "\xc2\xaa"
		case 0xa7:
			result += "\xc2\xba"
		case 0xa8:
			result += "\xc2\xbf"
		case 0xa9:
			result += "\u2310"
		case 0xaa:
			result += "\xc2\xac"
		case 0xab:
			result += "\xc2\xbd"
		case 0xac:
			result += "\xc2\xbc"
		case 0xad:
			result += "\xc2\xa1"
		case 0xae:
			result += "\xc2\xab"
		case 0xaf:
			result += "\xc2\xbb"
		case 0xb0:
			result += "\u2591"
		case 0xb1:
			result += "\u2592"
		case 0xb2:
			result += "\u2593"
		case 0xb3:
			result += "\u2502"
		case 0xb4:
			result += "\u2524"
		case 0xb5:
			result += "\u2561"
		case 0xb6:
			result += "\u2562"
		case 0xb7:
			result += "\u2556"
		case 0xb8:
			result += "\u2555"
		case 0xb9:
			result += "\u2563"
		case 0xba:
			result += "\u2551"
		case 0xbb:
			result += "\u2557"
		case 0xbc:
			result += "\u255D"
		case 0xbd:
			result += "\u255C"
		case 0xbe:
			result += "\u255B"
		case 0xbf:
			result += "\u2510"
		case 0xc0:
			result += "\u2514"
		case 0xc1:
			result += "\u2534"
		case 0xc2:
			result += "\u252C"
		case 0xc3:
			result += "\u251C"
		case 0xc4:
			result += "\u2500"
		case 0xc5:
			result += "\u253C"
		case 0xc6:
			result += "\u255E"
		case 0xc7:
			result += "\u255F"
		case 0xc8:
			result += "\u255A"
		case 0xc9:
			result += "\u2554"
		case 0xca:
			result += "\u2569"
		case 0xcb:
			result += "\u2566"
		case 0xcc:
			result += "\u2560"
		case 0xcd:
			result += "\u2550"
		case 0xce:
			result += "\u256C"
		case 0xcf:
			result += "\u2567"
		case 0xd0:
			result += "\u2568"
		case 0xd1:
			result += "\u2564"
		case 0xd2:
			result += "\u2565"
		case 0xd3:
			result += "\u2559"
		case 0xd4:
			result += "\u2558"
		case 0xd5:
			result += "\u2552"
		case 0xd6:
			result += "\u2553"
		case 0xd7:
			result += "\u256B"
		case 0xd8:
			result += "\u256A"
		case 0xd9:
			result += "\u2518"
		case 0xda:
			result += "\u250C"
		case 0xdb:
			result += "\u2588"
		case 0xdc:
			result += "\u2584"
		case 0xdd:
			result += "\u258C"
		case 0xde:
			result += "\u2590"
		case 0xdf:
			result += "\u2580"
		case 0xe0:
			result += "\u03B1"
		case 0xe1:
			result += "\xc3\x9f"
		case 0xe2:
			result += "\u0393"
		case 0xe3:
			result += "\u03C0"
		case 0xe4:
			result += "\u03A3"
		case 0xe5:
			result += "\u03C3"
		case 0xe6:
			result += "\xc2\xb5"
		case 0xe7:
			result += "\u03C4"
		case 0xe8:
			result += "\u03A6"
		case 0xe9:
			result += "\u0398"
		case 0xea:
			result += "\u03A9"
		case 0xeb:
			result += "\u03B4"
		case 0xec:
			result += "\u221E"
		case 0xed:
			result += "\u03C6"
		case 0xee:
			result += "\u03B5"
		case 0xef:
			result += "\u2229"
		case 0xf0:
			result += "\u2261"
		case 0xf1:
			result += "\xc2\xb1"
		case 0xf2:
			result += "\u2265"
		case 0xf3:
			result += "\u2264"
		case 0xf4:
			result += "\u2320"
		case 0xf5:
			result += "\u2321"
		case 0xf6:
			result += "\xc3\xb7"
		case 0xf7:
			result += "\u2248"
		case 0xf8:
			result += "\xc2\xb0"
		case 0xf9:
			result += "\u2219"
		case 0xfa:
			result += "\xc2\xb7"
		case 0xfb:
			result += "\u221A"
		case 0xfc:
			result += "\u207F"
		case 0xfd:
			result += "\xc2\xb2"
		case 0xfe:
			result += "\u25A0"
		case 0xff:
			result += "\xc2\xa0"
		default:
			result += string(char)
		}
	}
	// fmt.Printf("\n")
	return result
}