package parser

import (
	"math"
	"regexp"
	"strconv"
	"strings"

	//"fmt"

	//"github.com/davecgh/go-spew/spew"
	"github.com/ghodss/yaml"
)

type Warband struct {
	Warband        *WarbandName     `json:"warband"`
	Rating         int              `json:"rating"`
	CampaignPoints int              `json:"campaign"`
	GoldCrowns     int              `json:"gc"`
	Shards         int              `json:"shards"`
	Equipment      *ItemList        `json:"equipment"`
	Heros          []*Hero          `json:"heros"`
	HenchmenGroups []*HenchmenGroup `json:"henchmen"`
	Notes          string           `json:"notes"`
	Objective      string           `json:"objective"`

	Henchmen_sum_xp     int
	Henchmen_cnt        int
	Hero_sum_xp         int
	Hero_cnt            int
	Hiredsword_sum_xp   int
	Hiredsword_cnt      int
	Large_cnt           int
	Routtest            int
	WarbandAddition_sum int
}

type WarbandName struct {
	Name string
	Race string
}

type ItemList struct {
	List []string
}

type Hero struct {
	Header          string `json:"hero"`
	Name            string
	Type            string
	Experience      int
	WarbandAddition int       `json:"warbandaddition,omitempty"`
	Stats           *Stats    `json:"stats,omitempty"`
	Large           bool      `json:"large"`
	HiredSword      bool      `json:"hiredsword"`
	Weapons         *ItemList `json:"weapons,omitempty"`
	Armour          *ItemList `json:"armour,omitempty"`
	Rules           *ItemList `json:"rules,omitempty"`
	SkillLists      *ItemList `json:"skilllists,omitempty"`
	BSkillLists     Skilllist
}

type HenchmenGroup struct {
	Header     string `json:"group"`
	Name       string
	Number     int
	Type       string
	Experience int
	Large      bool      `json:"large"`
	Stats      *Stats    `json:"stats"`
	Weapons    *ItemList `json:"weapons"`
	Armour     *ItemList `json:"armour"`
	Rules      *ItemList `json:"rules"`
}

type Weapons struct {
	MainHand string
	OffHand  string
}

type Stats struct {
	Movement       string
	WeaponSkill    int
	BallisticSkill int
	Strength       int
	Toughness      int
	Wounds         int
	Initiative     int
	Attacks        int
	Leadership     int
	Save           string
}

type Skilllist struct {
	Strength bool
	Academic bool
	Combat   bool
	Shooting bool
	Speed    bool
	Special  bool
}

func (stats *Stats) UnmarshalJSON(b []byte) error {
	regex := regexp.MustCompile(`"\s*M([0-9]+[dD]*[6]*)\s*,\s*WS([0-9]+)\s*,\s*BS([0-9]+)\s*,\s*S([0-9]+)\s*,\s*T([0-9]+)\s*,\s*W([0-9]+)\s*,\s*I([0-9]+)\s*,\s*A([0-9]+)\s*,\s*Ld([0-9]+)\s*,\s*Sv([0-9\-]+)\s*"`)
	matches := regex.FindStringSubmatch(string(b))
	stats.Movement = matches[1]
	stats.WeaponSkill, _ = strconv.Atoi(matches[2])
	stats.BallisticSkill, _ = strconv.Atoi(matches[3])
	stats.Strength, _ = strconv.Atoi(matches[4])
	stats.Toughness, _ = strconv.Atoi(matches[5])
	stats.Wounds, _ = strconv.Atoi(matches[6])
	stats.Initiative, _ = strconv.Atoi(matches[7])
	stats.Attacks, _ = strconv.Atoi(matches[8])
	stats.Leadership, _ = strconv.Atoi(matches[9])
	stats.Save = matches[10]

	return nil
}

func (warband *WarbandName) UnmarshalJSON(b []byte) error {
	regex := regexp.MustCompile(`"([^\(]+)\(([^\)]+)\)`)
	matches := regex.FindStringSubmatch(string(b))
	warband.Name = strings.TrimSpace(matches[1])
	warband.Race = strings.TrimSpace(matches[2])
	return nil
}

func (itemList *ItemList) UnmarshalJSON(b []byte) error {
	items := strings.Split(string(b[1:len(b)-1]), ",")
	for _, item := range items {
		itemList.List = append(itemList.List, strings.TrimSpace(item))
	}
	return nil
}

func (hands *Weapons) UnmarshalJSON(b []byte) error {
	items := strings.Split(string(b[1:len(b)-1]), ",")
	hands.MainHand = strings.TrimSpace(items[0])
	if len(items) > 1 {
		hands.OffHand = strings.TrimSpace(items[1])
	}
	return nil
}

func ParseWarband(warbandDefinition []byte) Warband {
	var warband Warband

	err := yaml.Unmarshal(warbandDefinition, &warband)
	if err != nil {
		panic(err)
	}

	for _, h := range warband.Heros {
		regex := regexp.MustCompile(`([^\(]+)\(([^\)]+)\)\s*\[([0-9]+)XP\]\s*`)
		matches := regex.FindStringSubmatch(string(h.Header))
		h.Name = strings.TrimSpace(matches[1])
		h.Type = strings.TrimSpace(matches[2])
		h.Experience, _ = strconv.Atoi(strings.TrimSpace(matches[3]))
		if h.WarbandAddition > 0 {
			warband.Rating = warband.Rating + h.WarbandAddition
			warband.WarbandAddition_sum = warband.WarbandAddition_sum + h.WarbandAddition
		}

		if !h.Large && !h.HiredSword {
			warband.Rating = warband.Rating + h.Experience + 5
			warband.Hero_sum_xp = warband.Hero_sum_xp + h.Experience
			warband.Hero_cnt = warband.Hero_cnt + 1
		} else if h.Large {
			warband.Rating = warband.Rating + h.Experience + 20
			warband.Hero_sum_xp = warband.Hero_sum_xp + h.Experience
			warband.Large_cnt = warband.Large_cnt + 1
		} else if h.HiredSword {
			warband.Rating = warband.Rating + h.Experience + 5
			warband.Hiredsword_sum_xp = warband.Hiredsword_sum_xp + h.Experience
			warband.Hiredsword_cnt = warband.Hiredsword_cnt + 1
		} // TODO it is not possible that a large hired sword can be added

		// hier text Skill listen-Name zu boolschen Wert umwandeln
		h.BSkillLists.Speed = false
		h.BSkillLists.Shooting = false
		h.BSkillLists.Special = false
		h.BSkillLists.Combat = false
		h.BSkillLists.Academic = false
		h.BSkillLists.Strength = false
		for _, s := range h.SkillLists.List {
			if strings.EqualFold(s, "Speed") {
				h.BSkillLists.Speed = true
			}
			if strings.EqualFold(s, "Shooting") {
				h.BSkillLists.Shooting = true
			}
			if strings.EqualFold(s, "Special") {
				h.BSkillLists.Special = true
			}
			if strings.EqualFold(s, "Combat") {
				h.BSkillLists.Combat = true
			}
			if strings.EqualFold(s, "Academic") {
				h.BSkillLists.Academic = true
			}
			if strings.EqualFold(s, "Strength") {
				h.BSkillLists.Strength = true
			}
		}
	}

	for _, hg := range warband.HenchmenGroups {
		regex := regexp.MustCompile(`([^\(]+)\(([0-9]+)x?\s+([^\)]+)\)\s*\[([0-9]+)XP\]\s*`)
		matches := regex.FindStringSubmatch(string(hg.Header))
		hg.Name = strings.TrimSpace(matches[1])
		hg.Number, _ = strconv.Atoi(strings.TrimSpace(matches[2]))
		hg.Type = strings.TrimSpace(matches[3])
		hg.Experience, _ = strconv.Atoi(strings.TrimSpace(matches[4]))
		if !hg.Large {
			warband.Henchmen_sum_xp = warband.Henchmen_sum_xp + (hg.Experience * hg.Number)
			warband.Henchmen_cnt = warband.Henchmen_cnt + hg.Number
			warband.Rating = warband.Rating + (hg.Experience+5)*hg.Number
		} else {
			warband.Henchmen_sum_xp = warband.Henchmen_sum_xp + (hg.Experience * hg.Number)
			warband.Large_cnt = warband.Large_cnt + 1
			warband.Rating = warband.Rating + (hg.Experience+20)*hg.Number
		}

	}

	warband.Routtest = int(math.RoundToEven(float64(warband.Hero_cnt+warband.Henchmen_cnt+warband.Large_cnt+warband.Hiredsword_cnt) / 4.0))
	if float64(warband.Hero_cnt+warband.Henchmen_cnt+warband.Large_cnt+warband.Hiredsword_cnt)/4.0 > float64(warband.Routtest) {
		warband.Routtest = warband.Routtest + 1
	}

	//spew.Dump(warband)

	return warband
}
