// vi:ts=4:sts=4:sw=4:noet:tw=72

package modules

import (
	"bufio"
	"flokatirc/util"
	"fmt"
	"math/rand"
	"os"
	"regexp"
	"strings"
	"time"

	"github.com/sorcix/irc"

	"code.dnix.de/an/xlog"
)

type quizQuestion struct {
	category, question, answer, regexp, level string
}

var (
	quizCtrlCh         = make(chan string, 1024)
	quizAnswerCh       = make(chan *irc.Message, 1024)
	quizQuestions      []quizQuestion
	quizQuestionValues = map[string]int{"extreme": 5, "hard": 4, "normal": 3, "easy": 2, "baby": 1}
)

func init() {
	MsgFuncs["quiz"] = quizHandleMessage
	RunFuncs["quiz"] = quiz
	rand.Seed(time.Now().Unix())
}

func quizHandleMessage(m *irc.Message) {
	tok := strings.Split(m.Trailing, " ")
	if len(tok) < 1 {
		return
	}
	switch tok[0] {
	case "!quiz", "!quizstart":
		quizCtrlCh <- "start"
		break
	case "!quizstop":
		quizCtrlCh <- "stop"
		break
	default:
		quizAnswerCh <- m
		break
	}
}

func quiz() {
	time.Sleep(5 * time.Second)
	SayCh <- fmt.Sprintf("%s\nquiz mod test. !quizstart to start, !quizstop to end.", "*")
	for {
		time.Sleep(1 * time.Millisecond)
		select {
		case s := <-quizCtrlCh:
			if s == "start" {
				quizRun()
			}
		default:
			break
		}
	}
}

func quizRun() {
	quizQuestions = quizLoadQuestions("questions.txt")
	ranklist := make(map[string]int)
	SayCh <- fmt.Sprintf("%s\nQuiz gestartet.", "*")
	for {
		solved, cont, solver, gain := quizAskQuestion()
		if !cont {
			SayCh <- fmt.Sprintf("%s\nQuiz beendet.", "*")
			return
		} else {
			if solved {
				if _, exists := ranklist[solver]; exists {
					ranklist[solver] += gain
				} else {
					ranklist[solver] = gain
				}
				if ranklist[solver] > 1000 {
					bold := byte(0x02)
					solver = string(bold) + solver + string(bold)
					SayCh <- fmt.Sprintf("%s\n%s gewinnt!", "*", solver)
					quizPrintRanklist(ranklist)
					SayCh <- fmt.Sprintf("%s\nQuiz beendet.", "*")
					return
				}
			}
		}
		quizPrintRanklist(ranklist)
	}
}

func quizPrintRanklist(ranklist map[string]int) {
	SayCh <- fmt.Sprintf("%s\nAktueller Punktestand:", "*")
	for k, v := range ranklist {
		SayCh <- fmt.Sprintf("%s\n%s: %d", "*", k, v)
	}
}

func quizAskQuestion() (bool, bool, string, int) {
	q := quizQuestions[rand.Intn(len(quizQuestions))]
	SayCh <- fmt.Sprintf("%s\nEine Frage aus der Kategorie \"%s\" (%s):", "*", q.category, q.level)
	SayCh <- fmt.Sprintf("%s\n>>> %s <<<", "*", q.question)
	solved, cont, solver, gain := quizWaitForAnswer(q)
	if !solved {
		SayCh <- fmt.Sprintf("%s\nDie richtige Antwort wäre gewesen:", "*")
		SayCh <- fmt.Sprintf("%s\n%s [%s]", "*", q.answer, q.regexp)
	}
	time.Sleep(5 * time.Second)
	return solved, cont, solver, gain
}

func quizWaitForAnswer(q quizQuestion) (bool, bool, string, int) {
	i := 0
	haveAnswer := false
	timer := time.Now().Unix()
	for {
		select {
		case m := <-quizAnswerCh:
			haveAnswer = true
			points := 10 - ((time.Now().Unix() - timer) / 10)
			if points < 1 {
				points = 1
			}
			re, err := regexp.Compile(q.regexp)
			if err != nil {
				xlog.Error(err.Error())
				return false, false, "", 0
			}
			if re.MatchString(strings.ToLower(util.ReplaceUmlauts(m.Trailing))) {
				bold := byte(0x02)
				solver := strings.Split(m.Prefix.String(), "!")[0]
				solver = string(bold) + solver + string(bold)
				value := quizQuestionValues[q.level]
				gain := int(points) * value
				SayCh <- fmt.Sprintf("%s\n%s hat die Frage korrekt beantwortet und erhält dafür %d (%d * %d) Punkte.", "*", solver, gain, points, value)
				return true, true, solver, gain
			}
			break
		case s := <-quizCtrlCh:
			if s == "stop" {
				return false, false, "", 0
			}
			break
		case <-time.After(15 * time.Second):
			i++
			if i > 3 {
				if haveAnswer {
					SayCh <- fmt.Sprintf("%s\nNiemand konnte die Frage korrekt beantworten.", "*")
					return false, true, "", 0
				} else {
					SayCh <- fmt.Sprintf("%s\nNiemand hat auf die Frage geantwortet.", "*")
					return false, false, "", 0
				}
			} else {
				quizGiveHint(q, i)
			}
		}
	}
}

func quizGiveHint(q quizQuestion, n int) {
	var hint string
	haveHint := false
	for {
		hint = ""
		for i := 0; i < len(q.answer); i++ {
			if string(q.answer[i]) == " " {
				hint += " "
			} else {
				if rand.Intn(10) < 2 {
					haveHint = true
					hint += string(q.answer[i])
				} else {
					hint += "-"
				}
			}
		}
		if haveHint {
			break
		}
	}
	SayCh <- fmt.Sprintf("%s\nTipp: %s", "*", hint)
}

func quizLoadQuestions(path string) []quizQuestion {
	file, err := os.Open(path)
	if err != nil {
		xlog.Fatal(err.Error())
	}
	defer file.Close()

	questions := make([]quizQuestion, 0)
	q := quizQuestion{"", "", "", "", ""}

	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		line := scanner.Text()
		if len(line) == 0 || line[0] == '#' || line[0] == '\n' {
			if q.category != "" {
				questions = append(questions, q)
				q = quizQuestion{"", "", "", "", "normal"}
			}
		} else {
			tok := strings.Split(line, ":: ")
			switch tok[0] {
			case "Category":
				q.category = tok[1]
				break
			case "Question":
				q.question = tok[1]
				break
			case "Answer":
				q.answer = strings.Replace(util.ReplaceUmlauts(tok[1]), "#", "", -1)
				if q.regexp == "" {
					regexp := strings.Split(strings.ToLower(util.ReplaceUmlauts(tok[1])), "#")
					if len(regexp) > 1 {
						q.regexp = regexp[1]
					} else {
						q.regexp = regexp[0]
					}
				}
				break
			case "Regexp":
				q.regexp = util.ReplaceUmlauts(strings.ToLower(tok[1]))
				break
			case "Level":
				q.level = tok[1]
				break
			}
		}
	}

	if err := scanner.Err(); err != nil {
		xlog.Fatal(err.Error())
	}

	return questions
}