package d2datadict

import (
	"log"
	"strings"

	"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"

	"github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface"

	dh "github.com/OpenDiablo2/OpenDiablo2/d2helper"
)

type ArmorRecord struct {
	Name string

	Version     int  // 0 = classic, 100 = expansion
	CompactSave bool // if true, doesn't store any stats upon saving
	Rarity      int  // higher, the rarer
	Spawnable   bool // if 0, cannot spawn in shops

	MinAC            int
	MaxAC            int
	Absorbs          int // unused?
	Speed            int // affects movement speed of wielder, >0 = you move slower, <0 = you move faster
	RequiredStrength int
	Block            int  // chance to block, capped at 75%
	Durability       int  // base durability 0-255
	NoDurability     bool // if true, item has no durability

	Level         int    // base item level (controls monster drops, for instance a lv20 monster cannot drop a lv30 item)
	RequiredLevel int    // required level to wield
	Cost          int    // base cost
	GambleCost    int    // for reference only, not used
	Code          string // identifies the item
	NameString    string // seems to be identical to code?
	MagicLevel    int    // additional magic level (for gambling?)
	AutoPrefix    int    // prefix automatically assigned to this item on spawn, maps to group column of Automagic.txt

	AlternateGfx string // code of the DCC used when equipped
	OpenBetaGfx  string // unknown
	NormalCode   string
	UberCode     string
	UltraCode    string

	SpellOffset int // unknown

	Component       int // corresponds to Composit.txt, player animation layer used by this
	InventoryWidth  int
	InventoryHeight int
	HasInventory    bool // if true, item can store gems or runes
	GemSockets      int  // number of gems to store
	GemApplyType    int  // what kind of gem effect is applied
	// 0 = weapon, 1= armor or helmet, 2 = shield

	FlippyFile          string // DC6 file animation to play when item drops on the ground
	InventoryFile       string // DC6 file used in your inventory
	UniqueInventoryFile string // DC6 file used by the unique version of this item
	SetInventoryFile    string // DC6 file used by the set version of this item

	// these represent how player animations and graphics change upon wearing this
	// these come from ArmType.txt
	AnimRightArm         int
	AnimLeftArm          int
	AnimTorso            int
	AnimLegs             int
	AnimRightShoulderPad int
	AnimLeftShoulderPad  int

	Useable bool // can be used via right click if true
	// game knows what to do if used by item code
	Throwable bool
	Stackable bool // can be stacked in inventory
	MinStack  int  // min size of stack when item is spawned, used if stackable
	MaxStack  int  // max size of stack when item is spawned

	Type  string // base type in ItemTypes.txt
	Type2 string

	DropSound    string // sfx for dropping
	DropSfxFrame int    // what frame of drop animation the sfx triggers on
	UseSound     string // sfx for using

	Unique      bool // if true, only spawns as unique
	Transparent bool // unused
	TransTable  int  // unknown, related to blending mode?
	Quivered    bool // if true, requires ammo to use
	LightRadius int  // apparently unused
	Belt        bool // tells what kind of belt this item is

	Quest int // indicates that this item belongs to a given quest?

	MissileType       int // missile gfx for throwing
	DurabilityWarning int // controls what warning icon appears when durability is low
	QuantityWarning   int // controls at what quantity the low quantity warning appears

	MinDamage      int
	MaxDamage      int
	StrengthBonus  int
	DexterityBonus int
	// final mindam = min * str / strbonus + min * dex / dexbonus
	// same for maxdam

	GemOffset int // unknown
	BitField1 int // 1 = leather item, 3 = metal

	Vendors map[string]*ItemVendorParams // controls vendor settings

	SourceArt               string // unused?
	GameArt                 string // unused?
	ColorTransform          int    // colormap to use for player's gfx
	InventoryColorTransform int    // colormap to use for inventory's gfx

	SkipName         bool   // if true, don't include the base name in the item description
	NightmareUpgrade string // upgraded in higher difficulties
	HellUpgrade      string

	UnusedMinDamage int
	UnusedMaxDamage int

	Nameable bool // if true, item can be personalized
}

type ArmorVendorParams struct {
	Min        int // minimum of this item they can stock
	Max        int // max they can stock
	MagicMin   int
	MagicMax   int
	MagicLevel uint8
}

func createArmorRecord(line string) ArmorRecord {
	r := strings.Split(line, "\t")
	i := -1
	inc := func() int {
		i++
		return i
	}
	result := ArmorRecord{
		Name: r[inc()],

		Version:     dh.StringToInt(dh.EmptyToZero(r[inc()])),
		CompactSave: dh.StringToInt(dh.EmptyToZero(r[inc()])) == 1,
		Rarity:      dh.StringToInt(dh.EmptyToZero(r[inc()])),
		Spawnable:   dh.StringToInt(dh.EmptyToZero(r[inc()])) == 1,

		MinAC:            dh.StringToInt(dh.EmptyToZero(r[inc()])),
		MaxAC:            dh.StringToInt(dh.EmptyToZero(r[inc()])),
		Absorbs:          dh.StringToInt(dh.EmptyToZero(r[inc()])),
		Speed:            dh.StringToInt(dh.EmptyToZero(r[inc()])),
		RequiredStrength: dh.StringToInt(dh.EmptyToZero(r[inc()])),
		Block:            dh.StringToInt(dh.EmptyToZero(r[inc()])),
		Durability:       dh.StringToInt(dh.EmptyToZero(r[inc()])),
		NoDurability:     dh.StringToInt(dh.EmptyToZero(r[inc()])) == 1,

		Level:         dh.StringToInt(dh.EmptyToZero(r[inc()])),
		RequiredLevel: dh.StringToInt(dh.EmptyToZero(r[inc()])),
		Cost:          dh.StringToInt(dh.EmptyToZero(r[inc()])),
		GambleCost:    dh.StringToInt(dh.EmptyToZero(r[inc()])),
		Code:          r[inc()],
		NameString:    r[inc()],
		MagicLevel:    dh.StringToInt(dh.EmptyToZero(r[inc()])),
		AutoPrefix:    dh.StringToInt(dh.EmptyToZero(r[inc()])),

		AlternateGfx: r[inc()],
		OpenBetaGfx:  r[inc()],
		NormalCode:   r[inc()],
		UberCode:     r[inc()],
		UltraCode:    r[inc()],

		SpellOffset: dh.StringToInt(dh.EmptyToZero(r[inc()])),

		Component:       dh.StringToInt(dh.EmptyToZero(r[inc()])),
		InventoryWidth:  dh.StringToInt(dh.EmptyToZero(r[inc()])),
		InventoryHeight: dh.StringToInt(dh.EmptyToZero(r[inc()])),
		HasInventory:    dh.StringToInt(dh.EmptyToZero(r[inc()])) == 1,
		GemSockets:      dh.StringToInt(dh.EmptyToZero(r[inc()])),
		GemApplyType:    dh.StringToInt(dh.EmptyToZero(r[inc()])),

		FlippyFile:          r[inc()],
		InventoryFile:       r[inc()],
		UniqueInventoryFile: r[inc()],
		SetInventoryFile:    r[inc()],

		AnimRightArm:         dh.StringToInt(dh.EmptyToZero(r[inc()])),
		AnimLeftArm:          dh.StringToInt(dh.EmptyToZero(r[inc()])),
		AnimTorso:            dh.StringToInt(dh.EmptyToZero(r[inc()])),
		AnimLegs:             dh.StringToInt(dh.EmptyToZero(r[inc()])),
		AnimRightShoulderPad: dh.StringToInt(dh.EmptyToZero(r[inc()])),
		AnimLeftShoulderPad:  dh.StringToInt(dh.EmptyToZero(r[inc()])),

		Useable: dh.StringToInt(dh.EmptyToZero(r[inc()])) == 1,

		Throwable: dh.StringToInt(dh.EmptyToZero(r[inc()])) == 1,
		Stackable: dh.StringToInt(dh.EmptyToZero(r[inc()])) == 1,
		MinStack:  dh.StringToInt(dh.EmptyToZero(r[inc()])),
		MaxStack:  dh.StringToInt(dh.EmptyToZero(r[inc()])),

		Type:  r[inc()],
		Type2: r[inc()],

		DropSound:    r[inc()],
		DropSfxFrame: dh.StringToInt(dh.EmptyToZero(r[inc()])),
		UseSound:     r[inc()],

		Unique:      dh.StringToInt(dh.EmptyToZero(r[inc()])) == 1,
		Transparent: dh.StringToInt(dh.EmptyToZero(r[inc()])) == 1,
		TransTable:  dh.StringToInt(dh.EmptyToZero(r[inc()])),
		Quivered:    dh.StringToInt(dh.EmptyToZero(r[inc()])) == 1,
		LightRadius: dh.StringToInt(dh.EmptyToZero(r[inc()])),
		Belt:        dh.StringToInt(dh.EmptyToZero(r[inc()])) == 1,

		Quest: dh.StringToInt(dh.EmptyToZero(r[inc()])),

		MissileType:       dh.StringToInt(dh.EmptyToZero(r[inc()])),
		DurabilityWarning: dh.StringToInt(dh.EmptyToZero(r[inc()])),
		QuantityWarning:   dh.StringToInt(dh.EmptyToZero(r[inc()])),

		MinDamage:      dh.StringToInt(dh.EmptyToZero(r[inc()])),
		MaxDamage:      dh.StringToInt(dh.EmptyToZero(r[inc()])),
		StrengthBonus:  dh.StringToInt(dh.EmptyToZero(r[inc()])),
		DexterityBonus: dh.StringToInt(dh.EmptyToZero(r[inc()])),

		GemOffset: dh.StringToInt(dh.EmptyToZero(r[inc()])),
		BitField1: dh.StringToInt(dh.EmptyToZero(r[inc()])),

		Vendors: createArmorVendorParams(&r, inc),

		SourceArt:               r[inc()],
		GameArt:                 r[inc()],
		ColorTransform:          dh.StringToInt(dh.EmptyToZero(r[inc()])),
		InventoryColorTransform: dh.StringToInt(dh.EmptyToZero(r[inc()])),

		SkipName:         dh.StringToInt(dh.EmptyToZero(r[inc()])) == 1,
		NightmareUpgrade: r[inc()],
		HellUpgrade:      r[inc()],

		UnusedMinDamage: dh.StringToInt(dh.EmptyToZero(r[inc()])),
		UnusedMaxDamage: dh.StringToInt(dh.EmptyToZero(r[inc()])),

		Nameable: dh.StringToInt(dh.EmptyToZero(r[inc()])) == 1,
	}
	return result
}

func createArmorVendorParams(r *[]string, inc func() int) map[string]*ItemVendorParams {
	vs := make([]string, 17)
	vs[0] = "Charsi"
	vs[1] = "Gheed"
	vs[2] = "Akara"
	vs[3] = "Fara"
	vs[4] = "Lysander"
	vs[5] = "Drognan"
	vs[6] = "Hralti"
	vs[7] = "Alkor"
	vs[8] = "Ormus"
	vs[9] = "Elzix"
	vs[10] = "Asheara"
	vs[11] = "Cain"
	vs[12] = "Halbu"
	vs[13] = "Jamella"
	vs[14] = "Larzuk"
	vs[15] = "Malah"
	vs[16] = "Drehya"

	return CreateItemVendorParams(r, inc, vs)
}

var Armors map[string]*ArmorRecord

func LoadArmors(fileProvider d2interface.FileProvider) {
	Armors = make(map[string]*ArmorRecord)
	data := strings.Split(string(fileProvider.LoadFile(d2resource.Armor)), "\r\n")[1:]
	for _, line := range data {
		if len(line) == 0 {
			continue
		}
		rec := createArmorRecord(line)
		Armors[rec.Code] = &rec
	}
	log.Printf("Loaded %d armors", len(Armors))
}
