package main

import (
	"strconv"
)

const (
	GameStateInProgress  int = 0
	GameStateWaitingUser     = 1
	GameStateEnded           = 2
	GameStateDraw            = 3
	GameStateAIWon           = 4
	GameStateAILost          = 5
)

const (
	ImageCross  string = "cross"
	ImageNought        = "nought"
)

const (
	WinnerNone int = 0
	WinnerAI       = 1
	WinnerUser     = 2
)

var field2point = map[int][2]int{
	1: {0, 0},
	2: {0, 1},
	3: {0, 2},
	4: {1, 0},
	5: {1, 1},
	6: {1, 2},
	7: {2, 0},
	8: {2, 1},
	9: {2, 2},
}

var FieldClass = map[int]string{
	1: "tt-item br bb",
	2: "tt-item bb",
	3: "tt-item bl bb",
	4: "tt-item br",
	5: "tt-item",
	6: "tt-item bl",
	7: "tt-item bt br",
	8: "tt-item bt",
	9: "tt-item bl bt",
}

var winner = [][3]int{
	{1, 2, 3},
	{4, 5, 6},
	{7, 8, 9},
	{1, 4, 7},
	{2, 5, 8},
	{3, 6, 9},
	{1, 5, 9},
	{7, 5, 3},
}

type Field struct {
	disabled bool
	image    string
	line     bool
}

type Game struct {
	gameFlow     string
	aiSecond     bool
	aiFields     []int
	userFields   []int
	fields       map[int]Field
	state        int
	winnerFields [3]int
	winner       int
}

func newGame(gameFlow string, aiSecond bool) *Game {
	fields := make(map[int]Field)
	for i := 1; i < 10; i++ {
		field := &Field{
			disabled: false,
		}
		fields[i] = *field
	}

	game := &Game{
		gameFlow: gameFlow,
		aiSecond: aiSecond,
		fields:   fields,
		winner:   WinnerNone,
	}

	for i, f := range str2array(game.gameFlow) {
		fieldNum, _ := strconv.ParseInt(f, 0, 16)

		if field, ok := game.fields[int(fieldNum)]; ok {
			field.disabled = true

			if i%2 == 0 {
				field.image = ImageCross
			} else {
				field.image = ImageNought
			}

			game.fields[int(fieldNum)] = field
		}

		if (i%2 == 0) == game.aiSecond {
			game.userFields = append(game.userFields, int(fieldNum))
		} else {
			game.aiFields = append(game.aiFields, int(fieldNum))
		}
	}

	game.calculateState()

	if game.isFinished() && game.winner != WinnerNone {
		for _, f := range game.winnerFields {
			if field, ok := game.fields[f]; ok {
				field.line = true
				game.fields[f] = field
			}
		}

		for i := 1; i < 10; i++ {
			if field, ok := game.fields[i]; ok {
				field.disabled = true
				game.fields[i] = field
			}
		}
	}

	return game
}

func (game *Game) isFinished() bool {
	return game.state > GameStateWaitingUser
}

func (game *Game) calculateState() {
	game.state = GameStateInProgress

	if "0" == game.gameFlow {
		game.state = GameStateWaitingUser
		return
	}

	for _, w := range winner {
		if len(game.aiFields) >= 3 && isSubArray(w[:], game.aiFields) {
			game.state = GameStateAIWon
			game.winnerFields = w
			game.winner = WinnerAI
			return
		}
		if len(game.userFields) >= 3 && isSubArray(w[:], game.userFields) {
			game.state = GameStateAILost
			game.winnerFields = w
			game.winner = WinnerUser
			return
		}
	}

	if len(game.gameFlow) == 9 {
		game.state = GameStateDraw
	}
}

func (game *Game) hasWinner() bool {
	return game.winner != WinnerNone
}

func (game *Game) isAiWon() bool {
	return game.winner == WinnerAI
}

func (game *Game) isFirstWon() bool {
	if game.winner == WinnerNone {
		return false
	}

	if game.aiSecond && game.winner == WinnerUser {
		return true
	}

	return !game.aiSecond && game.winner == WinnerAI
}

func isSubArray(a, b []int) bool {
	mb := make(map[int]struct{}, len(b))
	for _, x := range b {
		mb[x] = struct{}{}
	}
	var diff []int
	for _, x := range a {
		if _, found := mb[x]; !found {
			diff = append(diff, x)
		}
	}

	return len(diff) == 0
}