package main

import (
	"flag"
	"log"
	"os"
	"os/signal"
	"strconv"
	"strings"
	"time"
	"unicode/utf8"

	"git.saintnet.tech/tomecraft/tome_lib"
	"github.com/google/uuid"
	"github.com/gorilla/websocket"
)

var done chan interface{}
var interrupt chan os.Signal
var pid int
var matchID uuid.UUID

func receiveHandler(connection *websocket.Conn, container *UIContainer) {
	defer close(done)
	for {
		var resp tome_lib.SessionCommandResult
		err := connection.ReadJSON(&resp)
		if err != nil {
			log.Println("Error in receive:", err)
			break
		}
		switch resp.Result {
		case tome_lib.SessionRespJoined1:
			pid = tome_lib.SentinalID
			container.Output <- "joined as sentinal"
		case tome_lib.SessionRespJoined2:
			pid = tome_lib.ScourgeID
			container.Output <- "joined as scourge"
		case tome_lib.SessionRespFound:
			matchID = resp.MatchID
			container.Output <- "game found"
		case tome_lib.SessionRespPlayed:
			if resp.GameResult != nil {
				switch resp.GameResult.ResultType {
				case tome_lib.ActCmd:
					container.Output <- resp.GameResult.ActionResult.String()
				case tome_lib.StateCmd:
					container.State = resp.GameResult.StateResult
					container.Updated <- true
				case tome_lib.DebugCmd:
					container.Output <- "debug response"
				}
			} else {
				container.Output <- "error playing"
			}
		case tome_lib.SessionRespLeft:
			container.Output <- "game left"
			matchID = uuid.Nil
			return
		case tome_lib.SessionRespBroadcastSenTurn:
			container.Output <- "Sentinal may take their turn"
		case tome_lib.SessionRespBroadcastScoTrun:
			container.Output <- "Scourge may take their turn"
		case tome_lib.SessionRespBroadcastSenWin:
			container.Output <- "Sentinal wins!"
		case tome_lib.SessionRespBroadcastScoWin:
			container.Output <- "Scourge wins!"
		case tome_lib.SessionRespBroadcastUpdate:
			container.GetUpdate <- true
		case tome_lib.SessionRespBroadcastScoJoin:
			container.Output <- "Scourge has joined the game"
		case tome_lib.SessionRespBroadcastSenJoin:
			container.Output <- "Sentinal has joined the game"
		case tome_lib.SessionRespBroadcastScoLeft:
			container.Output <- "Scourge has left the game"
		case tome_lib.SessionRespBroadcastSenLeft:
			container.Output <- "Sentinal has left the game"
		case tome_lib.SessionRespBroadcastSenReady:
			container.Output <- "Sentinal is ready"
		case tome_lib.SessionRespBroadcastScoReady:
			container.Output <- "scourge is ready"
		case tome_lib.SessionRespBroadcastNone:

		case tome_lib.SessionRespError:
			container.Output <- "generic error"

		default:
			container.Output <- "Received a server response we don't know how to handle"
		}
	}
}

func backend(container *UIContainer) {
	done = make(chan interface{})    // Channel to indicate that the receiverHandler is done
	interrupt = make(chan os.Signal) // Channel to listen for interrupt signal to terminate gracefully
	cmd := make(chan tome_lib.SessionCommand)

	signal.Notify(interrupt, os.Interrupt) // Notify the interrupt channel for SIGINT
	hostname := flag.String("host", "localhost", "server hostname to connect to")
	port := flag.Int("port", 7636, "port to connect to")

	flag.Parse()
	port_s := strconv.Itoa(*port)
	socketUrl := "ws://" + *hostname + ":" + port_s + "/ws"
	id := uuid.New()
	conn, _, err := websocket.DefaultDialer.Dial(socketUrl, nil)

	if err != nil {
		log.Fatal("Error connecting to Websocket Server:", err)
	}
	defer conn.Close()
	go receiveHandler(conn, container)
	go GetCommand(id, cmd, container)
	ticker := time.NewTicker(2 * time.Second)
	// Our main loop for the client
	// We send our relevant packets here
	for {
		var c tome_lib.SessionCommand
		select {
		case c = <-cmd:
			err := conn.WriteJSON(c)
			if err != nil {
				log.Println("Error during writing to websocket:", err)
				return
			}
			if c.Command == tome_lib.SessionCmdLeave {
				break
			}
		case <-container.GetUpdate:
			if container.State != nil {
				err = conn.WriteJSON(&tome_lib.SessionCommand{
					ID:      id,
					MatchID: matchID,
					Command: tome_lib.SessionCmdPlay,
					GameCommand: &tome_lib.Command{
						PlayerID: pid,
						Type:     tome_lib.StateCmd,
						Cmd:      "g",
					},
				})
				if err != nil {
					log.Println("Error during writing to websocker:", err)
					return
				}
				container.Updated <- true
			}

		case <-interrupt:
			// We received a SIGINT (Ctrl + C). Terminate gracefully...
			log.Println("Received SIGINT interrupt signal. Closing all pending connections")

			// Close our websocket connection
			err := conn.WriteJSON(tome_lib.SessionCommand{
				ID:      id,
				Command: tome_lib.SessionCmdLeave,
			})
			if err != nil {
				log.Println("Error during closing websocket:", err)
				return
			}

			select {
			case <-done:
				log.Println("Receiver Channel Closed! Exiting....")
			case <-time.After(time.Duration(1) * time.Second):
				log.Println("Timeout in closing receiving channel. Exiting....")
			}
			return
		case <-ticker.C:
			select {
			case <-done:
				return
			default:
				if matchID != uuid.Nil {
					err := conn.WriteJSON(tome_lib.SessionCommand{
						ID:      id,
						MatchID: matchID,
						Command: tome_lib.SessionCmdPoll,
					})
					if err != nil {
						log.Println("Error writing to websocket:", err)
						return
					}
				}
			}
		}
	}
}

func GetCommand(uid uuid.UUID, resp chan tome_lib.SessionCommand, container *UIContainer) {
	for {
		var cmd string
		var t int
		input := <-container.Input
		input_s := strings.Split(input, " ")
		t, err := strconv.Atoi(input_s[0])
		if err != nil {
			continue
		}
		cmd = trimFirstRune(input)

		cmd = strings.TrimSpace(cmd)
		switch t {
		case 0:
			//session
			switch tome_lib.SessionCmd(cmd) {
			case tome_lib.SessionCmdQuery:
				resp <- tome_lib.SessionCommand{
					ID:      uid,
					Command: tome_lib.SessionCmdQuery,
				}
			case tome_lib.SessionCmdJoin:
				resp <- tome_lib.SessionCommand{
					ID:      uid,
					MatchID: matchID,
					Command: tome_lib.SessionCmdJoin,
				}
			case tome_lib.SessionCmdLeave:
				resp <- tome_lib.SessionCommand{
					ID:      uid,
					MatchID: matchID,
					Command: tome_lib.SessionCmdLeave,
				}
			default:
				break
			}
		case 1:
			//state
			resp <- tome_lib.SessionCommand{
				ID:      uid,
				MatchID: matchID,
				Command: tome_lib.SessionCmdPlay,
				GameCommand: &tome_lib.Command{
					PlayerID: pid,
					Type:     tome_lib.StateCmd,
					Cmd:      cmd,
				},
			}
		case 2:
			//action
			resp <- tome_lib.SessionCommand{
				ID:      uid,
				MatchID: matchID,
				Command: tome_lib.SessionCmdPlay,
				GameCommand: &tome_lib.Command{
					PlayerID: pid,
					Type:     tome_lib.ActCmd,
					Cmd:      cmd,
				},
			}
		}

	}
}

func trimFirstRune(s string) string {
	_, i := utf8.DecodeRuneInString(s)
	return s[i:]
}
