package main

import (
	"AoC2020/helper"
	"fmt"
	"os"
	"sort"
	"strconv"
	"strings"
)

type Player struct {
	deck          []int
	previousDecks [][]int
}

type Game struct {
	players [2]Player
	winner  int
}

var lineBreak = "\n"

func main() {
	game := Game{}
	args := os.Args[1:]
	input, err := helper.GetFile(args[0])
	if err != nil {
		fmt.Println(err)
	}
	playerInput := strings.Split(input, lineBreak+lineBreak)
	player1 := parsePlayer(playerInput[0])
	player2 := parsePlayer(playerInput[1])
	game.players[0] = player1
	game.players[1] = player2
	for game.winner == 0 {
		game = game.runRoundRecursive()
		fmt.Println(game.players[0])
		fmt.Println(game.players[1])
	}
	fmt.Println(game.winner)
	fmt.Println(game.players[game.winner-1].deck)
	fmt.Println(game.caculateWinnerScore())
}

func parsePlayer(input string) Player {
	var player = Player{}
	playerInput := strings.Split(input, lineBreak)
	for _, val := range playerInput[1:] {
		number, _ := strconv.Atoi(val)
		player.deck = append(player.deck, number)
	}
	return player
}

func (g Game) runRound() Game {
	g = g.saveCurrentDeck()
	var cardP0 int
	var cardP1 int
	g.players[0], cardP0 = g.players[0].drawCard()
	g.players[1], cardP1 = g.players[1].drawCard()
	winnerCards := []int{cardP0, cardP1}
	sort.Sort(sort.Reverse(sort.IntSlice(winnerCards)))
	if cardP0 > cardP1 {
		g.players[0] = g.players[0].addToDeck(winnerCards)
	}
	if cardP1 > cardP0 {
		g.players[1] = g.players[1].addToDeck(winnerCards)
	}
	if len(g.players[0].deck) == 0 {
		g.winner = 2
	}
	if len(g.players[1].deck) == 0 {
		g.winner = 1
	}
	return g
}

func (g Game) caculateWinnerScore() int {
	winnerDeck := g.players[g.winner-1].deck
	return calculateScoreForDeck(winnerDeck)
}

func calculateScoreForDeck(deck []int) int {
	deckLen := len(deck)
	sum := 0
	for i := 0; i < deckLen; i++ {
		sum += deck[i] * (deckLen - i)
	}
	return sum
}

func (g Game) saveCurrentDeck() Game {
	for i, _ := range g.players {
		g.players[i] = g.players[i].saveCurrentDeck()
	}
	return g
}

func (g Game) runRoundRecursive() Game {
	p1 := g.players[0]
	p2 := g.players[1]
	for i, _ := range p1.previousDecks {
		if helper.Equal(p1.previousDecks[i], p1.deck) && helper.Equal(p2.previousDecks[i], p2.deck) {
			g.winner = 1
			return g
		}
	}
	var roundWinner int
	p1 = p1.saveCurrentDeck()
	p2 = p2.saveCurrentDeck()
	var cardP1 int
	var cardP2 int
	p1, cardP1 = p1.drawCard()
	p2, cardP2 = p2.drawCard()
	if len(p1.deck) >= cardP1 && len(p2.deck) >= cardP2 {
		var newGame Game
		var newPlayer1 Player
		var newPlayer2 Player
		newPlayer1.deck = make([]int, cardP1)
		newPlayer2.deck = make([]int, cardP2)
		copy(newPlayer1.deck, p1.deck[:cardP1])
		copy(newPlayer2.deck, p2.deck[:cardP2])
		newGame.players = [2]Player{newPlayer1, newPlayer2}
		roundWinner = newGame.runRecursive().winner
	} else {
		if cardP1 > cardP2 {
			roundWinner = 1
		}
		if cardP2 > cardP1 {
			roundWinner = 2
		}
	}
	if roundWinner == 1 {
		p1 = p1.addToDeck([]int{cardP1, cardP2})
	}
	if roundWinner == 2 {
		p2 = p2.addToDeck([]int{cardP2, cardP1})
	}
	if len(p1.deck) == 0 {
		g.winner = 2
	}
	if len(p2.deck) == 0 {
		g.winner = 1
	}
	g.players = [2]Player{p1, p2}
	return g
}

func (g Game) run() Game {
	for g.winner == 0 {
		g = g.runRound()
	}
	return g
}

func (g Game) runRecursive() Game {
	for g.winner == 0 {
		g = g.runRoundRecursive()
	}
	return g
}

func (p Player) drawCard() (Player, int) {
	card := p.deck[0]
	p.deck = helper.DeleteAt(p.deck, 0)
	return p, card
}

func (p Player) saveCurrentDeck() Player {
	copyOfDeck := make([]int, len(p.deck))
	copy(copyOfDeck, p.deck)
	p.previousDecks = append(p.previousDecks, copyOfDeck)
	return p
}

func (p Player) addToDeck(cards []int) Player {
	p.deck = append(p.deck, cards...)
	return p
}
