package game

import (
	"battlecamp-go-server/board"
	"battlecamp-go-server/events"
	"battlecamp-go-server/player"
	"battlecamp-go-server/stomp"
	"log"
	"math/rand"
	"sync"
	"time"
)

type Game struct {
	Id        int64            `json:"id"`
	StartTime int64            `json:"startTime"`
	EndTime   int64            `json:"endTime"`
	Board     *board.Board     `json:"board"`
	Players   []*player.Player `json:"-"`
	mutex     sync.Mutex
	Winner    *player.Player `json:"winner,omitempty"`
}

func NewGame(cols, rows int) *Game {
	createTime := time.Now().Unix()

	game := &Game{
		StartTime: createTime,
		Board:     board.NewRemote(cols, rows),
		Players:   make([]*player.Player, 0),
	}

	return game
}

func (g *Game) Join(p *player.Player) {
	g.mutex.Lock()
	g.placePlayer(p)
	g.Players = append(g.Players, p)
	g.mutex.Unlock()
}

func (g *Game) placePlayer(p *player.Player) {
	xRange := g.Board.Width / 5
	yRange := g.Board.Height

	for {
		x := rand.Intn(xRange)
		y := rand.Intn(yRange)

		if g.isValidPlayerPos(x, y) {
			p.Pos.X = x
			p.Pos.Y = y
			return
		}
	}

}

func (g *Game) Move(p *player.Player, direction string) bool {
	if !(direction == "N" || direction == "W" || direction == "S" || direction == "E") {
		log.Printf("Illigal direction %v", direction)
		return false
	}
	newX := p.Pos.X
	newY := p.Pos.Y

	switch direction {
	case "N":
		newY--
	case "W":
		newX--
	case "S":
		newY++
	case "E":
		newX++
	}

	//TODO make thread safe
	if !g.isValidPlayerPos(newX, newY) {
		log.Printf("Illigal player pos oldX %v oldY %v newX %v newY %v dir %v ", p.Pos.X, p.Pos.Y, newX, newY, direction)
		return false
	}

	p.Pos.X = newX
	p.Pos.Y = newY
	// END TODO make tread safe
	up := events.PlayerUpdate{
		GameId:  g.Id,
		Players: g.Players,
	}

	stomp.SendJson("update", up)

	if g.isWinner(p) {
		g.EndTime = time.Now().Unix()
		g.Winner = p
		ge := events.GameEnd{
			Type:    "GAME_END",
			GameId:  g.Id,
			Payload: g.Winner.Id,
		}

		stomp.SendJson("game", ge)
	}

	return true
}

func (g *Game) isWinner(p *player.Player) bool {
	return g.Board.Finish.X == p.Pos.X && g.Board.Finish.Y == p.Pos.Y
}

func (g *Game) isValidPlayerPos(x, y int) bool {
	if x < 0 || y < 0 || x >= g.Board.Width || y >= g.Board.Height {
		return false
	}

	t := g.Board.Get(x, y)

	if t == board.Rock {
		return false
	}

	return g.getPlayerAt(x, y) == nil
}

func (g *Game) getPlayerAt(x, y int) *player.Player {

	for _, p := range g.Players {
		if p.Pos.X == x && p.Pos.Y == y {
			return p
		}
	}

	return nil
}

func (g *Game) GetPlayer(playerId string) *player.Player {

	for _, p := range g.Players {
		if p.Id == playerId {
			return p
		}
	}

	return nil
}
