package main

import (
	"bufio"
	"flag"
	"fmt"
	"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, output chan string) {
	defer close(done)
	for {
		var resp tome_lib.SessionCommandResult
		err := connection.ReadJSON(&resp)
		if err != nil {
			log.Println("Error in receive:", err)
		}
		switch resp.Result {
		case tome_lib.SessionRespJoined1:
			pid = tome_lib.SentinalID
			output <- "joined as sentinal"
		case tome_lib.SessionRespJoined2:
			pid = tome_lib.ScourgeID
			output <- "joined as scourge"
		case tome_lib.SessionRespFound:
			matchID = resp.MatchID
			output <- "tome_lib found"
		case tome_lib.SessionRespJoinError:
			output <- "error joining game"
		case tome_lib.SessionRespPlayed:
			if resp.GameResult != nil {
				output <- "played succesfully"
				switch resp.GameResult.ResultType {
				case tome_lib.ActCmd:
					output <- resp.GameResult.ActionResult.String()
				case tome_lib.StateCmd:
					output <- resp.GameResult.StateResult.String()
				case tome_lib.DebugCmd:
					output <- "debug response" //resp.GameResult.DebugResult
				}
			} else {
				output <- "error playing"
			}
		case tome_lib.SessionRespLeft:
			output <- "tome_lib left"
			break
		case tome_lib.SessionRespBroadcastSenTurn:
			output <- "Sentinal may take their turn"
		case tome_lib.SessionRespBroadcastScoTrun:
			output <- "Scourge may take their turn"
		case tome_lib.SessionRespBroadcastSenWin:
			output <- "Sentinal wins!"
		case tome_lib.SessionRespBroadcastScoWin:
			output <- "Scourge wins!"
		case tome_lib.SessionRespBroadcastUpdate:
			//we don't handle updates
		case tome_lib.SessionRespBroadcastScoJoin:
			output <- "Scourge has joined the game"
		case tome_lib.SessionRespBroadcastSenJoin:
			output <- "Sentinal has joined the game"
		case tome_lib.SessionRespBroadcastScoLeft:
			output <- "Scourge has left the game"
		case tome_lib.SessionRespBroadcastSenLeft:
			output <- "Sentinal has left the game"
		case tome_lib.SessionRespBroadcastSenReady:
			output <- "Sentinal is ready"
		case tome_lib.SessionRespBroadcastScoReady:
			output <- "scourge is ready"
		case tome_lib.SessionRespBroadcastNone:

		case tome_lib.SessionRespError:
			output <- "generic error"

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

func main() {
	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)
	output := make(chan string)

	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"
	conn, _, err := websocket.DefaultDialer.Dial(socketUrl, nil)
	id := uuid.New()

	if err != nil {
		log.Fatal("Error connecting to Websocket Server:", err)
	}
	defer conn.Close()
	go receiveHandler(conn, output)
	go GetCommand(id, cmd)
	ticker := time.NewTicker(1 * time.Second)
	// Our main loop for the client
	// We send our relevant packets here
	for {
		var c tome_lib.SessionCommand
		var o string
		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 o = <-output:
			fmt.Println(o)
		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:
			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) {
	for {
		var cmd string
		var t int
		fmt.Print("> ")
		_, err := fmt.Scanf("%d", &t)
		if err != nil {
			log.Println(err)
		}
		if t == -1 {
			panic("quitting")
		}
		cmd, err = bufio.NewReader(os.Stdin).ReadString('\n')
		if err != nil {
			log.Println(err)
		}
		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:]
}
