package main

// WayfarerQuestState — прогресс игрока в активном квесте (какой
// квест, на каком шаге). Сами тексты и ветвления хранятся отдельно,
// в WayfarerQuestDefs — так, чтобы состояние партии было маленьким и
// не дублировало неизменный контент квеста.
type WayfarerQuestState struct {
	Key  string
	Step int
}

// WayfarerQuestChoice — один вариант выбора на шаге квеста.
type WayfarerQuestChoice struct {
	LabelKey string // ключ перевода текста варианта выбора

	// NextStep — индекс следующего шага квеста, если выбор не
	// завершает квест. Игнорируется, если Terminal == true.
	NextStep int

	// Terminal — завершает ли этот выбор квест немедленно.
	Terminal bool
	Success  bool // при Terminal: считается ли это успехом или провалом квеста

	ResultKey string // ключ перевода текста-результата этого выбора

	CreditsDelta float64 // изменение кредитов (может быть отрицательным)
	HullDelta    int     // изменение прочности корпуса (может быть отрицательным)
	CargoIdx     int     // индекс товара в WayfarerCommodities, которого касается CargoDelta (-1, если не используется)
	CargoDelta   int     // изменение количества этого товара в трюме
	KillsDelta   int     // изменение числа побед (для пилотского рейтинга)
}

// WayfarerQuestStep — один узел ветвления квеста: текст ситуации и
// доступные варианты выбора.
type WayfarerQuestStep struct {
	TextKey string
	Choices []WayfarerQuestChoice
}

// WayfarerQuestDef — полное описание одного квеста: заголовок и все
// его шаги (шаг 0 — стартовый).
type WayfarerQuestDef struct {
	Key      string
	TitleKey string
	BriefKey string // короткое описание для списка доступных квестов
	Steps    []WayfarerQuestStep
}

// FindWayfarerQuest ищет определение квеста по ключу среди
// WayfarerQuestDefs.
func FindWayfarerQuest(key string) (WayfarerQuestDef, bool) {
	for _, q := range WayfarerQuestDefs {
		if q.Key == key {
			return q, true
		}
	}
	return WayfarerQuestDef{}, false
}

// StartQuest запускает квест key с самого начала (шаг 0), если он
// ещё не завершён и не провален ранее и сейчас нет другого
// активного квеста.
func (s *WayfarerShip) StartQuest(key string) error {
	if s.ActiveQuest != nil {
		return errWayfarerQuestAlreadyActive
	}
	if s.CompletedQuests[key] || s.FailedQuests[key] {
		return errWayfarerQuestAlreadyDone
	}
	if _, ok := FindWayfarerQuest(key); !ok {
		return errWayfarerQuestNotFound
	}
	s.ActiveQuest = &WayfarerQuestState{Key: key, Step: 0}
	return nil
}

// CurrentQuestStep возвращает текущий шаг активного квеста (или
// (_, false), если квеста нет).
func (s *WayfarerShip) CurrentQuestStep() (WayfarerQuestStep, bool) {
	if s.ActiveQuest == nil {
		return WayfarerQuestStep{}, false
	}
	def, ok := FindWayfarerQuest(s.ActiveQuest.Key)
	if !ok || s.ActiveQuest.Step >= len(def.Steps) {
		return WayfarerQuestStep{}, false
	}
	return def.Steps[s.ActiveQuest.Step], true
}

// ChooseQuestOption применяет выбранный вариант choiceIdx текущего
// шага активного квеста: списывает/начисляет ресурсы и либо
// переходит на следующий шаг, либо завершает квест (успехом или
// провалом).
func (s *WayfarerShip) ChooseQuestOption(choiceIdx int) (WayfarerQuestChoice, error) {
	step, ok := s.CurrentQuestStep()
	if !ok {
		return WayfarerQuestChoice{}, errWayfarerNoActiveQuest
	}
	if choiceIdx < 0 || choiceIdx >= len(step.Choices) {
		return WayfarerQuestChoice{}, errWayfarerInvalidChoice
	}
	choice := step.Choices[choiceIdx]

	s.Credits += choice.CreditsDelta
	if s.Credits < 0 {
		s.Credits = 0
	}
	s.Hull += choice.HullDelta
	if s.Hull < 0 {
		s.Hull = 0
	}
	if s.Hull > s.MaxHull {
		s.Hull = s.MaxHull
	}
	if choice.CargoIdx >= 0 && choice.CargoIdx < len(s.Cargo) {
		s.Cargo[choice.CargoIdx] += choice.CargoDelta
		if s.Cargo[choice.CargoIdx] < 0 {
			s.Cargo[choice.CargoIdx] = 0
		}
	}
	s.Kills += choice.KillsDelta

	key := s.ActiveQuest.Key
	if choice.Terminal {
		if choice.Success {
			s.CompletedQuests[key] = true
		} else {
			s.FailedQuests[key] = true
		}
		s.ActiveQuest = nil
	} else {
		s.ActiveQuest.Step = choice.NextStep
	}
	return choice, nil
}

// AbandonQuest прерывает текущий активный квест без последствий,
// как если бы он никогда не начинался (не считается ни успехом, ни
// провалом — его можно будет начать заново позже).
func (s *WayfarerShip) AbandonQuest() {
	s.ActiveQuest = nil
}

var (
	errWayfarerQuestAlreadyActive = wayfarerErr("сейчас уже выполняется другой квест")
	errWayfarerQuestAlreadyDone   = wayfarerErr("этот квест уже завершён или провален ранее")
	errWayfarerQuestNotFound      = wayfarerErr("квест с таким ключом не найден")
	errWayfarerNoActiveQuest      = wayfarerErr("сейчас нет активного квеста")
	errWayfarerInvalidChoice      = wayfarerErr("недопустимый номер варианта")
)
