package leveldat

import (
	"math"
	"time"

	"github.com/df-mc/dragonfly/server/block/cube"
	"github.com/df-mc/dragonfly/server/world"
	"github.com/sandertv/gophertunnel/minecraft/protocol"
)

// Data holds a collection of data that specify a range of Settings of the
// world. These Settings usually alter the way that players interact with the
// world. The data held here is usually saved in a level.dat file of the world.
// Data may be used in LevelDat.Unmarshal to collect the data of the level.dat.
type Data struct {
	BaseGameVersion                string `nbt:"baseGameVersion"`
	BiomeOverride                  string
	ConfirmedPlatformLockedContent bool
	CentreMapsToOrigin             bool  `nbt:"CenterMapsToOrigin"`
	CheatsEnabled                  bool  `nbt:"cheatsEnabled"`
	DaylightCycle                  int32 `nbt:"daylightCycle"`
	Difficulty                     int32
	EduOffer                       int32 `nbt:"eduOffer"`
	FlatWorldLayers                string
	ForceGameType                  bool
	GameType                       int32
	Generator                      int32
	InventoryVersion               string
	LANBroadcast                   bool
	LANBroadcastIntent             bool
	LastPlayed                     int64
	LevelName                      string
	LimitedWorldOriginX            int32
	LimitedWorldOriginY            int32
	LimitedWorldOriginZ            int32
	LimitedWorldDepth              int32 `nbt:"limitedWorldDepth"`
	LimitedWorldWidth              int32 `nbt:"limitedWorldWidth"`
	LocatorBar                     bool  `nbt:"locatorbar"`
	MinimumCompatibleClientVersion []int32
	MultiPlayerGame                bool `nbt:"MultiplayerGame"`
	MultiPlayerGameIntent          bool `nbt:"MultiplayerGameIntent"`
	NetherScale                    int32
	NetworkVersion                 int32
	Platform                       int32
	PlatformBroadcastIntent        int32
	RandomSeed                     int64
	ShowTags                       bool `nbt:"showtags"`
	SingleUseWorld                 bool `nbt:"isSingleUseWorld"`
	SpawnX, SpawnY, SpawnZ         int32
	SpawnV1Villagers               bool
	StorageVersion                 int32
	Time                           int64
	XBLBroadcast                   bool
	XBLBroadcastIntent             int32
	XBLBroadcastMode               int32
	Abilities                      struct {
		AttackMobs             bool    `nbt:"attackmobs"`
		AttackPlayers          bool    `nbt:"attackplayers"`
		Build                  bool    `nbt:"build"`
		Mine                   bool    `nbt:"mine"`
		DoorsAndSwitches       bool    `nbt:"doorsandswitches"`
		FlySpeed               float32 `nbt:"flySpeed"`
		Flying                 bool    `nbt:"flying"`
		InstantBuild           bool    `nbt:"instabuild"`
		Invulnerable           bool    `nbt:"invulnerable"`
		Lightning              bool    `nbt:"lightning"`
		MayFly                 bool    `nbt:"mayfly"`
		OP                     bool    `nbt:"op"`
		OpenContainers         bool    `nbt:"opencontainers"`
		PermissionsLevel       int32   `nbt:"permissionsLevel"`
		PlayerPermissionsLevel int32   `nbt:"playerPermissionsLevel"`
		Teleport               bool    `nbt:"teleport"`
		WalkSpeed              float32 `nbt:"walkSpeed"`
		VerticalFlySpeed       float32 `nbt:"verticalFlySpeed"`
	} `nbt:"abilities"`
	BonusChestEnabled                      bool           `nbt:"bonusChestEnabled"`
	BonusChestSpawned                      bool           `nbt:"bonusChestSpawned"`
	CommandBlockOutput                     bool           `nbt:"commandblockoutput"`
	CommandBlocksEnabled                   bool           `nbt:"commandblocksenabled"`
	CommandsEnabled                        bool           `nbt:"commandsEnabled"`
	CurrentTick                            int64          `nbt:"currentTick"`
	DoDayLightCycle                        bool           `nbt:"dodaylightcycle"`
	DoEntityDrops                          bool           `nbt:"doentitydrops"`
	DoFireTick                             bool           `nbt:"dofiretick"`
	DoImmediateRespawn                     bool           `nbt:"doimmediaterespawn"`
	DoInsomnia                             bool           `nbt:"doinsomnia"`
	DoMobLoot                              bool           `nbt:"domobloot"`
	DoMobSpawning                          bool           `nbt:"domobspawning"`
	DoTileDrops                            bool           `nbt:"dotiledrops"`
	DoWeatherCycle                         bool           `nbt:"doweathercycle"`
	DrowningDamage                         bool           `nbt:"drowningdamage"`
	EduLevel                               bool           `nbt:"eduLevel"`
	EducationFeaturesEnabled               bool           `nbt:"educationFeaturesEnabled"`
	ExperimentalGamePlay                   bool           `nbt:"experimentalgameplay"`
	FallDamage                             bool           `nbt:"falldamage"`
	FireDamage                             bool           `nbt:"firedamage"`
	FunctionCommandLimit                   int32          `nbt:"functioncommandlimit"`
	HasBeenLoadedInCreative                bool           `nbt:"hasBeenLoadedInCreative"`
	HasLockedBehaviourPack                 bool           `nbt:"hasLockedBehaviorPack"`
	HasLockedResourcePack                  bool           `nbt:"hasLockedResourcePack"`
	ImmutableWorld                         bool           `nbt:"immutableWorld"`
	IsCreatedInEditor                      bool           `nbt:"isCreatedInEditor"`
	IsExportedFromEditor                   bool           `nbt:"isExportedFromEditor"`
	IsFromLockedTemplate                   bool           `nbt:"isFromLockedTemplate"`
	IsFromWorldTemplate                    bool           `nbt:"isFromWorldTemplate"`
	IsWorldTemplateOptionLocked            bool           `nbt:"isWorldTemplateOptionLocked"`
	KeepInventory                          bool           `nbt:"keepinventory"`
	LastOpenedWithVersion                  []int32        `nbt:"lastOpenedWithVersion"`
	LightningLevel                         float32        `nbt:"lightningLevel"`
	LightningTime                          int32          `nbt:"lightningTime"`
	MaxCommandChainLength                  int32          `nbt:"maxcommandchainlength"`
	MobGriefing                            bool           `nbt:"mobgriefing"`
	NaturalRegeneration                    bool           `nbt:"naturalregeneration"`
	PRID                                   string         `nbt:"prid"`
	PVP                                    bool           `nbt:"pvp"`
	RainLevel                              float32        `nbt:"rainLevel"`
	RainTime                               int32          `nbt:"rainTime"`
	RandomTickSpeed                        int32          `nbt:"randomtickspeed"`
	RequiresCopiedPackRemovalCheck         bool           `nbt:"requiresCopiedPackRemovalCheck"`
	SendCommandFeedback                    bool           `nbt:"sendcommandfeedback"`
	ServerChunkTickRange                   int32          `nbt:"serverChunkTickRange"`
	ShowCoordinates                        bool           `nbt:"showcoordinates"`
	ShowDeathMessages                      bool           `nbt:"showdeathmessages"`
	SpawnMobs                              bool           `nbt:"spawnMobs"`
	SpawnRadius                            int32          `nbt:"spawnradius"`
	StartWithMapEnabled                    bool           `nbt:"startWithMapEnabled"`
	TexturePacksRequired                   bool           `nbt:"texturePacksRequired"`
	TNTExplodes                            bool           `nbt:"tntexplodes"`
	UseMSAGamerTagsOnly                    bool           `nbt:"useMsaGamertagsOnly"`
	WorldStartCount                        int64          `nbt:"worldStartCount"`
	Experiments                            map[string]any `nbt:"experiments"`
	FreezeDamage                           bool           `nbt:"freezedamage"`
	WorldPolicies                          map[string]any `nbt:"world_policies"`
	WorldVersion                           int32          `nbt:"WorldVersion"`
	RespawnBlocksExplode                   bool           `nbt:"respawnblocksexplode"`
	ShowBorderEffect                       bool           `nbt:"showbordereffect"`
	PermissionsLevel                       int32          `nbt:"permissionsLevel"`
	PlayerPermissionsLevel                 int32          `nbt:"playerPermissionsLevel"`
	IsRandomSeedAllowed                    bool           `nbt:"isRandomSeedAllowed"`
	DoLimitedCrafting                      bool           `nbt:"dolimitedcrafting"`
	EditorWorldType                        int32          `nbt:"editorWorldType"`
	PlayersSleepingPercentage              int32          `nbt:"playerssleepingpercentage"`
	RecipesUnlock                          bool           `nbt:"recipesunlock"`
	NaturalGeneration                      bool           `nbt:"naturalgeneration"`
	ProjectilesCanBreakBlocks              bool           `nbt:"projectilescanbreakblocks"`
	ShowRecipeMessages                     bool           `nbt:"showrecipemessages"`
	IsHardcore                             bool           `nbt:"IsHardcore"`
	ShowDaysPlayed                         bool           `nbt:"showdaysplayed"`
	TNTExplosionDropDecay                  bool           `nbt:"tntexplosiondropdecay"`
	HasUncompleteWorldFileOnDisk           bool           `nbt:"HasUncompleteWorldFileOnDisk"`
	PlayerHasDied                          bool           `nbt:"PlayerHasDied"`
	UseAllowList                           bool           `nbt:"UseAllowList"`
	AllowAnonymousBlockDropsInEditorWorlds bool           `nbt:"allowAnonymousBlockDropsInEditorWorlds"`
	PlayerWaypoints                        int32          `nbt:"playerwaypoints"`
	ServerEditorConnectionPolicy           int32          `nbt:"serverEditorConnectionPolicy"`
}

// FillDefault fills out d with all the default level.dat values.
func (d *Data) FillDefault() {
	d.Abilities.AttackMobs = true
	d.Abilities.AttackPlayers = true
	d.Abilities.Build = true
	d.Abilities.DoorsAndSwitches = true
	d.Abilities.FlySpeed = 0.05
	d.Abilities.Mine = true
	d.Abilities.OpenContainers = true
	d.Abilities.PlayerPermissionsLevel = 1
	d.Abilities.WalkSpeed = 0.1
	d.Abilities.VerticalFlySpeed = 1.0
	d.BaseGameVersion = "*"
	d.CommandBlockOutput = true
	d.CommandBlocksEnabled = true
	d.CommandsEnabled = true
	d.Difficulty = 2
	d.DoDayLightCycle = true
	d.DoEntityDrops = true
	d.DoFireTick = true
	d.DoInsomnia = true
	d.DoMobLoot = true
	d.DoMobSpawning = true
	d.DoTileDrops = true
	d.DoWeatherCycle = true
	d.DrowningDamage = true
	d.FallDamage = true
	d.FireDamage = true
	d.FreezeDamage = true
	d.FunctionCommandLimit = 10000
	d.GameType = 1
	d.Generator = 2
	d.HasBeenLoadedInCreative = true
	d.InventoryVersion = protocol.CurrentVersion
	d.LANBroadcast = true
	d.LANBroadcastIntent = true
	d.LastOpenedWithVersion = minimumCompatibleClientVersion
	d.LevelName = "World"
	d.LightningLevel = 1.0
	d.LimitedWorldDepth = 16
	d.LimitedWorldOriginY = math.MaxInt16
	d.LimitedWorldWidth = 16
	d.MaxCommandChainLength = math.MaxUint16
	d.MinimumCompatibleClientVersion = minimumCompatibleClientVersion
	d.MobGriefing = true
	d.MultiPlayerGame = true
	d.MultiPlayerGameIntent = true
	d.NaturalRegeneration = true
	d.NetherScale = 8
	d.NetworkVersion = protocol.CurrentProtocol
	d.PVP = true
	d.Platform = 2
	d.PlatformBroadcastIntent = 3
	d.RainLevel = 1.0
	d.RandomSeed = time.Now().Unix()
	d.RandomTickSpeed = 1
	d.RespawnBlocksExplode = true
	d.SendCommandFeedback = true
	d.ServerChunkTickRange = 6
	d.ShowBorderEffect = true
	d.ShowDeathMessages = true
	d.ShowTags = true
	d.SpawnMobs = true
	d.SpawnRadius = 5
	d.SpawnRadius = 5
	d.SpawnY = math.MaxInt16
	d.StorageVersion = 9
	d.TNTExplodes = true
	d.WorldVersion = 1
	d.XBLBroadcastIntent = 3
}

// Settings returns a world.Settings value based on the properties stored in d.
func (d *Data) Settings() *world.Settings {
	d.WorldStartCount += 1
	difficulty, _ := world.DifficultyByID(int(d.Difficulty))
	mode, _ := world.GameModeByID(int(d.GameType))
	return &world.Settings{
		Name:            d.LevelName,
		Spawn:           cube.Pos{int(d.SpawnX), int(d.SpawnY), int(d.SpawnZ)},
		Time:            d.Time,
		TimeCycle:       d.DoDayLightCycle,
		RainTime:        int64(d.RainTime),
		Raining:         d.RainLevel > 0,
		ThunderTime:     int64(d.LightningTime),
		Thundering:      d.LightningLevel > 0,
		WeatherCycle:    d.DoWeatherCycle,
		CurrentTick:     d.CurrentTick,
		DefaultGameMode: mode,
		Difficulty:      difficulty,
		TickRange:       d.ServerChunkTickRange,
	}
}

// PutSettings updates d with the Settings stored in s.
func (d *Data) PutSettings(s *world.Settings) {
	d.LevelName = s.Name
	d.SpawnX, d.SpawnY, d.SpawnZ = int32(s.Spawn.X()), int32(s.Spawn.Y()), int32(s.Spawn.Z())
	d.LimitedWorldOriginX, d.LimitedWorldOriginY, d.LimitedWorldOriginZ = d.SpawnX, d.SpawnY, d.SpawnZ
	d.Time = s.Time
	d.DoDayLightCycle = s.TimeCycle
	d.DoWeatherCycle = s.WeatherCycle
	d.RainTime, d.RainLevel = int32(s.RainTime), 0
	d.LightningTime, d.LightningLevel = int32(s.ThunderTime), 0
	if s.Raining {
		d.RainLevel = 1
	}
	if s.Thundering {
		d.LightningLevel = 1
	}
	d.CurrentTick = s.CurrentTick
	d.ServerChunkTickRange = s.TickRange
	mode, _ := world.GameModeID(s.DefaultGameMode)
	d.GameType = int32(mode)
	difficulty, _ := world.DifficultyID(s.Difficulty)
	d.Difficulty = int32(difficulty)
}
