package gen7

import (
	"bytes"
	"encoding/binary"
	"fmt"
	"strings"
	"unicode/utf16"

	p "github.com/ajswis/go-pkparse-server/pokemon"
)

// Parse accepts a raw pokemon (byte slice) and either returns a Pokemon struct
// with all applicable fields populated or an error depicting the reason for
// failure.
func Parse(rawPokemon p.RawPokemon) (*p.Pokemon, error) {
	if l := len(rawPokemon); l < 232 {
		return nil, fmt.Errorf("Invalid length for generation 7 pokemon: %d bytes", l)
	}

	var pkmn p.Pokemon
	pkmn.RawPokemon = &rawPokemon

	pkmn.EncryptionConstant = binary.LittleEndian.Uint32(rawPokemon[0x00:0x08])
	pkmn.PokedexNumber = binary.LittleEndian.Uint16(rawPokemon[0x08:0x0a])
	pkmn.HeldItemID = binary.LittleEndian.Uint16(rawPokemon[0x0a:0x0c])
	pkmn.TrainerID = binary.LittleEndian.Uint16(rawPokemon[0x0c:0x0e])
	pkmn.SecretID = binary.LittleEndian.Uint16(rawPokemon[0x0e:0x10])
	pkmn.FullTrainerID = binary.LittleEndian.Uint32(rawPokemon[0x0c:0x10])
	pkmn.Experience = binary.LittleEndian.Uint32(rawPokemon[0x10:0x15])
	pkmn.AbilityID = rawPokemon[0x14]
	pkmn.AbilityNum = rawPokemon[0x14]
	pkmn.PID = binary.LittleEndian.Uint32(rawPokemon[0x18:0x1c])
	pkmn.NatureID = rawPokemon[0x1c]

	genderByte := rawPokemon[0x1d]
	pkmn.FatefulEncounter = (genderByte & 0x01) == 1
	if (genderByte & 0x02) == 0x02 {
		pkmn.Gender = "F"
	} else if (genderByte & 0x04) == 0x04 {
		pkmn.Gender = ""
	} else {
		pkmn.Gender = "M"
	}
	pkmn.FormID = genderByte >> 3

	pkmn.HPEV = rawPokemon[0x1e]
	pkmn.AtkEV = rawPokemon[0x1f]
	pkmn.DefEV = rawPokemon[0x20]
	pkmn.SpeEV = rawPokemon[0x21]
	pkmn.SpAtkEV = rawPokemon[0x22]
	pkmn.SpDefEV = rawPokemon[0x23]

	pokerusByte := rawPokemon[0x2b]
	pkmn.PokerusDuration = pokerusByte & 15
	pkmn.PokerusStrain = pokerusByte >> 4

	pkmn.RawNickname = rawPokemon[0x40:0x58]
	pkmn.Nickname = binaryToUTF16leString(pkmn.RawNickname)

	pkmn.Move1ID = binary.LittleEndian.Uint16(rawPokemon[0x5a:0x5c])
	pkmn.Move2ID = binary.LittleEndian.Uint16(rawPokemon[0x5c:0x5e])
	pkmn.Move3ID = binary.LittleEndian.Uint16(rawPokemon[0x5e:0x60])
	pkmn.Move4ID = binary.LittleEndian.Uint16(rawPokemon[0x60:0x62])
	pkmn.Move1PP = rawPokemon[0x62]
	pkmn.Move2PP = rawPokemon[0x63]
	pkmn.Move3PP = rawPokemon[0x64]
	pkmn.Move4PP = rawPokemon[0x65]
	pkmn.Move1PPUsed = rawPokemon[0x66]
	pkmn.Move2PPUsed = rawPokemon[0x67]
	pkmn.Move3PPUsed = rawPokemon[0x68]
	pkmn.Move4PPUsed = rawPokemon[0x69]
	pkmn.EggMove1ID = binary.LittleEndian.Uint16(rawPokemon[0x6a:0x6c])
	pkmn.EggMove2ID = binary.LittleEndian.Uint16(rawPokemon[0x6c:0x6e])
	pkmn.EggMove3ID = binary.LittleEndian.Uint16(rawPokemon[0x6e:0x70])
	pkmn.EggMove4ID = binary.LittleEndian.Uint16(rawPokemon[0x70:0x72])

	ivBytes := binary.LittleEndian.Uint32(rawPokemon[0x74:0x78])
	pkmn.HPIV = ivBytes & 0x1f
	pkmn.AtkIV = ivBytes >> 5 & 0x1f
	pkmn.DefIV = ivBytes >> 10 & 0x1f
	pkmn.SpeIV = ivBytes >> 15 & 0x1f
	pkmn.SpAtkIV = ivBytes >> 20 & 0x1f
	pkmn.SpDefIV = ivBytes >> 25 & 0x1f
	pkmn.IsEgg = ((ivBytes >> 30) % 2) == 1
	pkmn.IsNicknamed = ((ivBytes >> 31) % 2) == 1

	return &pkmn, nil
}

func binaryToUTF16leString(b []byte) string {
	r := bytes.NewReader(b)
	u16 := make([]uint16, len(b)/2)
	binary.Read(r, binary.LittleEndian, &u16)
	runes := utf16.Decode(u16)
	s := strings.Split(string(runes), "\u0000")[0]

	return s
}
