package main

import (
	"math/rand"
	"red-green/door"
	"strings"
)

type Deck struct {
	Cards []door.Panel
	Backs []door.Panel
	Mark  []door.Panel
}

func (d *Deck) SetBackColor(color string) {
	for x := 0; x < 5; x++ {
		for idx := range d.Backs[x].Lines {
			d.Backs[x].Lines[idx].DefaultColor = color
		}
	}
}

func (d *Deck) Init() {
	d.Cards = make([]door.Panel, 52)
	for x := 0; x < 52; x++ {
		d.Cards[x] = CardOf(x)
	}
	d.Backs = make([]door.Panel, 5)
	for x := 0; x < 5; x++ {
		d.Backs[x] = BackOf(x)
	}
	d.Mark = make([]door.Panel, 2)
	d.Mark[0] = MarkOf(0)
	d.Mark[1] = MarkOf(1)
}

func CardOf(c int) door.Panel {
	suit := GetSuit(c)
	rank := GetRank(c)
	var is_red bool = suit < 2
	var color string

	if is_red {
		color = door.ColorText("RED ON WHITE")
	} else {
		color = door.ColorText("BLACK ON WHITE")
	}

	p := door.Panel{
		X:     0,
		Y:     0,
		Width: 5}
	r := RankSymbol(rank)
	s := SuitSymbol(suit)

	p.Lines = append(p.Lines,
		door.Line{Text: r + s + "   ",
			DefaultColor: color})
	p.Lines = append(p.Lines,
		door.Line{Text: "  " + s + "  ",
			DefaultColor: color})
	p.Lines = append(p.Lines,
		door.Line{Text: "   " + s + r,
			DefaultColor: color})
	return p
}

func BackOf(level int) door.Panel {
	p := door.Panel{
		X:     0,
		Y:     0,
		Width: 5,
	}

	var back string = strings.Repeat(BackSymbol(level), 5)

	for x := 0; x < 3; x++ {
		p.Lines = append(p.Lines,
			door.Line{Text: back})
	}
	return p
}

func MarkOf(c int) door.Panel {
	p := door.Panel{Width: 1}
	color := door.ColorText("BLUE ON WHITE")
	// var m rune
	if c == 0 {
		p.Lines = append(p.Lines,
			door.Line{Text: " ",
				DefaultColor: color,
			})
	} else {
		if door.Unicode {
			p.Lines = append(p.Lines,
				door.Line{Text: "\u25a0",
					DefaultColor: color,
				})
			// m = '\u25a0'
		} else {
			// Safely convert from byte to string
			var b [1]byte
			b[0] = 0xfe
			p.Lines = append(p.Lines,
				door.Line{Text: string(b[:]),
					DefaultColor: color})
		}
	}
	return p
}

func RankSymbol(c int) string {
	const symbols = "A23456789TJQK"
	return symbols[c : c+1]
}

func SuitSymbol(c int) string {
	if door.Unicode {
		switch c {
		case 0:
			return "\u2665"
		case 1:
			return "\u2666"
		case 2:
			return "\u2663"
		case 3:
			return "\u2660"
		}
	} else {
		if door.Full_CP437 {
			switch c {
			case 0:
				return "\x03"
			case 1:
				return "\x04"
			case 2:
				return "\x05"
			case 3:
				return "\x06"
			}
		} else {
			switch c {
			case 0:
				return "*"
			case 1:
				return "^"
			case 2:
				return "%"
			case 3:
				return "$"
			}
		}
	}
	return "?"
}

func BackSymbol(level int) string {
	if level == 0 {
		return " "
	}
	if door.Unicode {
		switch level {
		case 1:
			return "\u2591"
		case 2:
			return "\u2592"
		case 3:
			return "\u2593"
		case 4:
			return "\u2588"
		}
	} else {
		switch level {
		case 1:
			return "\xb0"
		case 2:
			return "\xb1"
		case 3:
			return "\xb2"
		case 4:
			return "\xdb"
		}
	}
	return "?"
}

func GetRank(c int) int {
	return (c % 52) % 13
}

func GetSuit(c int) int {
	return (c % 52) / 13
}

func GetDeck(c int) int {
	return c / 52
}

type Pos struct {
	X     int
	Y     int
	Level int
}

func CalcCardPos(pos int) Pos {
	var result Pos
	const space = 3
	const height = 3

	if pos == 28 {
		result = CalcCardPos(23)
		result.Y += height + 1
		result.Level--
		return result
	} else {
		if pos == 29 {
			result = CalcCardPos(22)
			result.Y += height + 1
			result.Level--
			return result
		}
	}

	const CARD_WIDTH = 5
	var HALF_WIDTH int = 3
	HALF_WIDTH += space / 2

	const between = CARD_WIDTH + space

	if pos < 3 {
		result.Level = 1
		result.Y = (result.Level-1)*(height-1) + 1
		result.X = pos*(between*3) + between + HALF_WIDTH + space
		return result
	} else {
		pos -= 3
	}

	if pos < 6 {
		result.Level = 2
		result.Y = (result.Level-1)*(height-1) + 1
		group := pos / 2
		result.X = pos*between + (group * between) + CARD_WIDTH + space*2
		return result
	} else {
		pos -= 6
	}

	if pos < 9 {
		result.Level = 3
		result.Y = (result.Level-1)*(height-1) + 1
		result.X = pos*between + HALF_WIDTH + space
		return result
	} else {
		pos -= 9
	}

	if pos < 10 {
		result.Level = 4
		result.Y = (result.Level-1)*(height-1) + 1
		result.X = pos*between + space
		return result
	}
	// failure
	result.Level = -1
	result.X = -1
	result.Y = -1
	return result
}

// 0-29
var CardPos []Pos

var Blocks [][]int

func init() {
	CardPos = make([]Pos, 30)
	for x := 0; x < 30; x++ {
		CardPos[x] = CalcCardPos(x)
	}

	Blocks = [][]int{
		{3, 4},
		{5, 6},
		{7, 8}, // end row 1
		{9, 10},
		{10, 11},
		{12, 13},
		{13, 14},
		{15, 16},
		{16, 17},
		{18, 19}, // end row 2
		{19, 20},
		{20, 21},
		{21, 22},
		{22, 23},
		{23, 24},
		{24, 25},
		{25, 26},
		{26, 27},
	}
}

// Which card(s) are unblocked by this card?
func Unblocks(card int) []int {
	var result []int
	for idx := range Blocks {
		if Blocks[idx][0] == card || Blocks[idx][1] == card {
			result = append(result, idx)
		}
	}
	return result
}

func CanPlay(card1 DeckType, card2 DeckType) bool {
	rank1 := GetRank(int(card1))
	rank2 := GetRank(int(card2))

	if (rank1+1)%13 == rank2 {
		return true
	}

	if rank1 == 0 {
		rank1 += 13
	}
	if rank1-1 == rank2 {
		return true
	}
	return false
}

type DeckType int8

/*
This shuffles the deck(s) of cards.

The RNG must be seeded before calling.
*/
func ShuffleCards(RNG *rand.Rand, decks int) []DeckType {
	var size int = decks * 52
	var result []DeckType = make([]DeckType, size)
	for x := 0; x < size; x++ {
		result[x] = DeckType(x)
	}
	RNG.Shuffle(size, func(i, j int) {
		result[i], result[j] = result[j], result[i]
	})
	return result
}

func MakeCardStates(decks int) []DeckType {
	var size int = decks * 52
	var result []DeckType = make([]DeckType, size)
	// defaults to 0
	return result
}

func RemoveCard(c int, off_x int, off_y int, left bool, right bool) string {
	Pos := &CardPos[c]
	if Pos.Level > 1 {
		Pos.Level--
	}
	return ""
}