//
// Created by koncord on 14.01.16.
//

#include <apps/openmw/mwworld/manualref.hpp>
#include <apps/openmw/mwmechanics/aitravel.hpp>
#include <components/esm/esmwriter.hpp>
#include "../mwbase/environment.hpp"
#include "../mwstate/statemanagerimp.hpp"
#include "../mwinput/inputmanagerimp.hpp"
#include "../mwscript/scriptmanagerimp.hpp"
#include "../mwgui/windowmanagerimp.hpp"
#include "../mwworld/worldimp.hpp"
#include "../mwworld/player.hpp"
#include "../mwworld/customdata.hpp"
#include "../mwmechanics/creaturestats.hpp"
#include "../mwclass/npc.hpp"
#include "../mwclass/creature.hpp"
#include "../mwmechanics/mechanicsmanagerimp.hpp"
#include "../mwworld/cellstore.hpp"
#include <apps/openmw/mwdialogue/dialoguemanagerimp.hpp>
#include <apps/openmw/mwworld/inventorystore.hpp>
#include <apps/openmw/mwmechanics/spellcasting.hpp>
#include <apps/openmw/mwgui/inventorywindow.hpp>
#include <components/openmw-mp/Log.hpp>

#include "LocalPlayer.hpp"
#include "Main.hpp"

using namespace mwmp;
using namespace std;

LocalPlayer::LocalPlayer()
{
    CharGenStage()->current = 0;
    CharGenStage()->end = 1;
    consoleAllowed = true;
}

LocalPlayer::~LocalPlayer()
{

}

Networking *LocalPlayer::GetNetworking()
{
    return mwmp::Main::get().getNetworking();
}

MWWorld::Ptr LocalPlayer::GetPlayerPtr()
{
    return MWBase::Environment::get().getWorld()->getPlayerPtr();
}

void LocalPlayer::Update()
{
    updateCell();
    updatePosition();
    updateDrawStateAndFlags();
    updateAttackState();
    updateDeadState();
    updateEquipped();
    updateInventory();
    updateDynamicStats();
    updateAttributes();
    updateSkills();
    updateLevel();
}

void LocalPlayer::charGen(int stageFirst, int stageEnd)
{
    CharGenStage()->current = stageFirst;
    CharGenStage()->end = stageEnd;
}

bool LocalPlayer::charGenThread() // todo: need fix
{
    MWBase::WindowManager *windowManager = MWBase::Environment::get().getWindowManager();
    if (windowManager->isGuiMode())
        return false;

    if (CharGenStage()->current >= CharGenStage()->end)
    {

        if (GetNetworking()->isConnected() && CharGenStage()->current == CharGenStage()->end &&
            CharGenStage()->end != 0)
        {
            MWBase::World *world = MWBase::Environment::get().getWorld();
            MWWorld::Ptr player = world->getPlayerPtr();
            (*Npc()) = *player.get<ESM::NPC>()->mBase;
            (*BirthSign()) = world->getPlayer().getBirthSign();

            LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "%s", "Sending ID_GAME_BASE_INFO to server with my CharGen info");
            GetNetworking()->GetPlayerPacket(ID_GAME_BASE_INFO)->Send(this);

            if (CharGenStage()->end != 1)
            {
                updateDynamicStats(true);
                updateAttributes(true);
                updateSkills(true);
                updateLevel(true);
                sendClass();
                GetNetworking()->GetPlayerPacket(ID_GAME_CHARGEN)->Send(this);
            }
            CharGenStage()->end = 0;
            /*RakNet::BitStream bs;
            GetNetworking()->GetPlayerPacket(ID_GAME_BASE_INFO)->Packet(&bs, this, true);
            GetNetworking()->SendData(&bs);*/

        }
        return true;
    }

    switch (CharGenStage()->current)
    {
    case 0:
        windowManager->pushGuiMode(MWGui::GM_Name);
        break;
    case 1:
        windowManager->pushGuiMode(MWGui::GM_Race);
        break;
    case 2:
        windowManager->pushGuiMode(MWGui::GM_Class);
        break;
    case 3:
        windowManager->pushGuiMode(MWGui::GM_Birth);
        break;
    default:
        windowManager->pushGuiMode(MWGui::GM_Review);
        break;
    }
    GetNetworking()->GetPlayerPacket(ID_GAME_CHARGEN)->Send(this);
    CharGenStage()->current++;

    return false;
}

void LocalPlayer::updateDynamicStats(bool forceUpdate)
{
    MWWorld::Ptr player = GetPlayerPtr();

    MWMechanics::CreatureStats *ptrCreatureStats = &player.getClass().getCreatureStats(player);
    MWMechanics::DynamicStat<float> health(ptrCreatureStats->getHealth());
    MWMechanics::DynamicStat<float> magicka(ptrCreatureStats->getMagicka());
    MWMechanics::DynamicStat<float> fatigue(ptrCreatureStats->getFatigue());

    static MWMechanics::DynamicStat<float> oldHealth(ptrCreatureStats->getHealth());
    static MWMechanics::DynamicStat<float> oldMagicka(ptrCreatureStats->getMagicka());
    static MWMechanics::DynamicStat<float> oldFatigue(ptrCreatureStats->getFatigue());

    static float timer = 0;

    if ((timer += MWBase::Environment::get().getFrameDuration()) >= 0.5 || forceUpdate)
    {
        if (oldHealth != health || oldMagicka != magicka || oldFatigue != fatigue || forceUpdate)
        {
            oldHealth = health;
            oldMagicka = magicka;
            oldFatigue = fatigue;

            health.writeState(CreatureStats()->mDynamic[0]);
            magicka.writeState(CreatureStats()->mDynamic[1]);
            fatigue.writeState(CreatureStats()->mDynamic[2]);

            timer = 0;

            GetNetworking()->GetPlayerPacket(ID_GAME_DYNAMICSTATS)->Send(this);
        }
    }
}

void LocalPlayer::updateAttributes(bool forceUpdate)
{
    MWWorld::Ptr player = GetPlayerPtr();
    const MWMechanics::NpcStats &ptrNpcStats = player.getClass().getNpcStats(player);
    bool isUpdating = false;

    for (int i = 0; i < 8; ++i)
    {
        if (ptrNpcStats.getAttribute(i).getBase() != CreatureStats()->mAttributes[i].mBase)
        {
            ptrNpcStats.getAttribute(i).writeState(CreatureStats()->mAttributes[i]);
            isUpdating = true;
        }
    }

    if (isUpdating || forceUpdate)
    {
        GetNetworking()->GetPlayerPacket(ID_GAME_ATTRIBUTE)->Send(this);
    }
}

void LocalPlayer::updateSkills(bool forceUpdate)
{
    MWWorld::Ptr player = GetPlayerPtr();
    const MWMechanics::NpcStats &ptrNpcStats = player.getClass().getNpcStats(player);
    bool isUpdating = false;


    for (int i = 0; i < 27; ++i)
    {
        if (ptrNpcStats.getSkill(i).getBase() != NpcStats()->mSkills[i].mBase)
        {
            ptrNpcStats.getSkill(i).writeState(NpcStats()->mSkills[i]);
            isUpdating = true;
        }
        // If we only have skill progress, update the state for relevant skills
        // but don't send a packet just because of this (to avoid spam)
        else if (ptrNpcStats.getSkill(i).getProgress() != NpcStats()->mSkills[i].mProgress)
        {
            ptrNpcStats.getSkill(i).writeState(NpcStats()->mSkills[i]);
        }    
    }

    for (int i = 0; i < 8; i++)
    {
        if (ptrNpcStats.getSkillIncrease(i) != NpcStats()->mSkillIncrease[i]) {
            NpcStats()->mSkillIncrease[i] = ptrNpcStats.getSkillIncrease(i);
        }
    }

    if (isUpdating || forceUpdate)
    {
        NpcStats()->mLevelProgress = ptrNpcStats.getLevelProgress();
        GetNetworking()->GetPlayerPacket(ID_GAME_SKILL)->Send(this);
    }
}

void LocalPlayer::updateLevel(bool forceUpdate)
{
    MWWorld::Ptr player = GetPlayerPtr();
    const MWMechanics::NpcStats &ptrNpcStats = player.getClass().getNpcStats(player);

    if (ptrNpcStats.getLevel() != CreatureStats()->mLevel || forceUpdate)
    {
        CreatureStats()->mLevel = ptrNpcStats.getLevel();
        GetNetworking()->GetPlayerPacket(ID_GAME_LEVEL)->Send(this);

        // Also update skills to refresh level progress and attribute bonuses
        // for next level up
        updateSkills(true);
    }
}

void LocalPlayer::updatePosition(bool forceUpdate)
{
    MWBase::World *world = MWBase::Environment::get().getWorld();
    MWWorld::Ptr player = world->getPlayerPtr();

    const MWMechanics::Movement &move = player.getClass().getMovementSettings(player);

    static bool posChanged = false;

    static bool isJumping = false;
    static bool sentJumpEnd = true;

    ESM::Position ptrPos = player.getRefData().getPosition();

    const bool isChangedPos = (move.mPosition[0] != 0 || move.mPosition[1] != 0 || move.mPosition[2] != 0
                               || move.mRotation[0] != 0 || move.mRotation[1] != 0 || move.mRotation[2] != 0);

    if (isChangedPos || posChanged || forceUpdate)
    {
        posChanged = isChangedPos;

        if (!isJumping && !world->isOnGround(player) && !world->isFlying(player))
        {
            isJumping = true;
        }

        (*Position()) = ptrPos;

        Dir()->pos[0] = move.mPosition[0];
        Dir()->pos[1] = move.mPosition[1];
        Dir()->pos[2] = move.mPosition[2];

        GetNetworking()->GetPlayerPacket(ID_GAME_POS)->Send(this);
    }
    else if (isJumping && world->isOnGround(player))
    {
        isJumping = false;
        sentJumpEnd = false;
    }
    // Packet with jump end position has to be sent one tick after above check
    else if (!sentJumpEnd)
    {
        sentJumpEnd = true;
        (*Position()) = ptrPos;
        GetNetworking()->GetPlayerPacket(ID_GAME_POS)->Send(this);
    }
}

void LocalPlayer::updateCell(bool forceUpdate)
{
    const ESM::Cell *ptrCell = MWBase::Environment::get().getWorld()->getPlayerPtr().getCell()->getCell();
    bool shouldUpdate = false;

    // Send a packet to server to update this LocalPlayer's cell if:
    // 1) forceUpdate is true
    // 2) The LocalPlayer's cell name does not equal the World Player's cell name
    // 3) The LocalPlayer's exterior cell coordinates do not equal the World Player's
    //    exterior cell coordinates
    if (forceUpdate)
    {
        shouldUpdate = true;
    }
    else if (!Misc::StringUtils::ciEqual(ptrCell->mName, GetCell()->mName))
    {
        shouldUpdate = true;
    }
    else if (ptrCell->isExterior())
    {
        if (ptrCell->mData.mX != GetCell()->mData.mX)
        {
            shouldUpdate = true;
        }
        else if (ptrCell->mData.mY != GetCell()->mData.mY)
        {
            shouldUpdate = true;
        }
    }

    if (shouldUpdate)
    {
        LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "%s", "Sending ID_GAME_CELL to server");

        LOG_APPEND(Log::LOG_INFO, "- Moved from %s to %s",
            GetCell()->getDescription().c_str(),
            ptrCell->getDescription().c_str());

        (*GetCell()) = *ptrCell;

        // Make sure the position is updated before a cell packet is sent, or else
        // cell change events in server scripts will have the wrong player position
        updatePosition(true);

        RakNet::BitStream bs;
        GetNetworking()->GetPlayerPacket((RakNet::MessageID) ID_GAME_CELL)->Packet(&bs, this, true);
        GetNetworking()->SendData(&bs);

        // Also update skill progress
        updateSkills(true);
    }
}

void LocalPlayer::updateChar()
{
    MWBase::Environment::get().getMechanicsManager()->setPlayerRace(
        Npc()->mRace,
        Npc()->isMale(),
        Npc()->mHead,
        Npc()->mHair
    );

    MWBase::Environment::get().getMechanicsManager()->setPlayerBirthsign(*BirthSign());

    MWBase::Environment::get().getWindowManager()->getInventoryWindow()->rebuildAvatar();
}

void LocalPlayer::updateEquipped(bool forceUpdate)
{
    MWWorld::Ptr player = GetPlayerPtr();

    static bool equipChanged = false;

    if (forceUpdate)
        equipChanged = true;

    MWWorld::InventoryStore &invStore = player.getClass().getInventoryStore(player);
    for (int slot = 0; slot < MWWorld::InventoryStore::Slots; slot++)
    {
        MWWorld::ContainerStoreIterator it = invStore.getSlot(slot);
        if (it != invStore.end() && !::Misc::StringUtils::ciEqual(it->getCellRef().getRefId(), EquipedItem(slot)->refid))
        {
            equipChanged = true;

            EquipedItem(slot)->refid = it->getCellRef().getRefId();
            EquipedItem(slot)->health = it->getCellRef().getCharge();
            if (slot == MWWorld::InventoryStore::Slot_CarriedRight)
            {
                MWMechanics::WeaponType weaptype;
                MWMechanics::getActiveWeapon(player.getClass().getCreatureStats(player), player.getClass().getInventoryStore(player), &weaptype);
                if (weaptype != MWMechanics::WeapType_Thrown)
                    EquipedItem(slot)->count = 1;
            }
            else
                EquipedItem(slot)->count = invStore.count(it->getCellRef().getRefId());
        }
        else if (it == invStore.end() && !EquipedItem(slot)->refid.empty())
        {
            equipChanged = true;
            EquipedItem(slot)->refid = "";
            EquipedItem(slot)->count = 0;
            EquipedItem(slot)->health = 0;
        }
    }

    if (equipChanged)
    {
        RakNet::BitStream bs;
        bs.ResetWritePointer();
        GetNetworking()->GetPlayerPacket((RakNet::MessageID) ID_GAME_EQUIPMENT)->Packet(&bs, this, true);
        GetNetworking()->SendData(&bs);
        equipChanged = false;
    }
}

void LocalPlayer::updateInventory(bool forceUpdate)
{
    static bool invChanged = false;
    if (forceUpdate)
        invChanged = true;

    MWWorld::Ptr player = GetPlayerPtr();
    MWWorld::InventoryStore &invStore = player.getClass().getInventoryStore(player);
    mwmp::Item item;

    if (!invChanged)
        for (vector<Item>::iterator iter = inventory.items.begin(); iter != inventory.items.end(); ++iter)
        {
            MWWorld::ContainerStoreIterator result(invStore.begin());
            for (; result != invStore.end(); ++result)
            {
                item.refid = result->getCellRef().getRefId();
                if (item.refid.find("$dynamic") != string::npos) // skip generated items (self enchanted for e.g.)
                    continue;

                item.count = result->getRefData().getCount();
                item.health = result->getCellRef().getCharge();

                if (item == (*iter))
                    break;
            }
            if (result == invStore.end())
            {
                invChanged = true;
                break;
            }
        }
    if (!invChanged)
        for (MWWorld::ContainerStoreIterator iter(invStore.begin()); iter != invStore.end(); ++iter)
        {
            item.refid = iter->getCellRef().getRefId();
            if (item.refid.find("$dynamic") != string::npos) // skip generated items (self enchanted for e.g.)
                continue;

            item.count = iter->getRefData().getCount();
            item.health = iter->getCellRef().getCharge();

            vector<Item>::iterator result = inventory.items.begin();

            for (; result != inventory.items.end(); result++)
            {
                if ((*result) == item)
                    break;
            }

            if (result == inventory.items.end())
            {
                invChanged = true;
                break;
            }
        }

    if (!invChanged)
        return;

    invChanged = false;

    inventory.items.clear();
    for (MWWorld::ContainerStoreIterator iter(invStore.begin()); iter != invStore.end(); ++iter)
    {
        item.refid = iter->getCellRef().getRefId();
        if (item.refid.find("$dynamic") != string::npos) // skip generated items (self enchanted for e.g.)
            continue;

        item.count = iter->getRefData().getCount();
        item.health = iter->getCellRef().getCharge();

        inventory.items.push_back(item);
    }

    inventory.count = (unsigned int) inventory.items.size();
    inventory.action = Inventory::UPDATE;
    Main::get().getNetworking()->GetPlayerPacket(ID_GAME_INVENTORY)->Send(this);
}

void LocalPlayer::updateAttackState(bool forceUpdate)
{
    MWBase::World *world = MWBase::Environment::get().getWorld();
    MWWorld::Ptr player = GetPlayerPtr();

    using namespace MWMechanics;

    static bool attackPressed = false; // prevent flood
    MWMechanics::DrawState_ state = player.getClass().getNpcStats(player).getDrawState();
    //player.getClass().hit(player, 1, ESM::Weapon::AT_Chop);
    if (world->getPlayer().getAttackingOrSpell() && !attackPressed)
    {
        MWWorld::Ptr weapon = MWWorld::Ptr(); // hand-to-hand
                                              //player.getClass().onHit(player, 0.5, true, weapon, 0, 1);
        if (state == MWMechanics::DrawState_Spell)
        {
            const string &spell = MWBase::Environment::get().getWindowManager()->getSelectedSpell();

            GetAttack()->attacker = guid;
            GetAttack()->type = 1;
            GetAttack()->pressed = true;
            GetAttack()->refid = spell;

            /*RakNet::BitStream bs;
            GetNetworking()->GetPlayerPacket((RakNet::MessageID) ID_GAME_ATTACK)->Packet(&bs, this, true);
            GetNetworking()->SendData(&bs);*/
        }
        else if (state == MWMechanics::DrawState_Weapon)
        {
            //PrepareAttack(2);
        }
        attackPressed = true;
    }
    else if (!world->getPlayer().getAttackingOrSpell() && attackPressed)
    {
        if (/*state == MWMechanics::DrawState_Spell ||*/ state == MWMechanics::DrawState_Weapon)
        {
            //localNetPlayer->GetAttack()->success = false;
            //SendAttack(0);
        }
        attackPressed = false;
    }
}

void LocalPlayer::updateDeadState(bool forceUpdate)
{
    MWWorld::Ptr player = GetPlayerPtr();

    MWMechanics::NpcStats *ptrNpcStats = &player.getClass().getNpcStats(player);
    static bool isDead = false;

    if (ptrNpcStats->isDead() && !isDead)
    {
        CreatureStats()->mDead = true;
        RakNet::BitStream bs;
        GetNetworking()->GetPlayerPacket((RakNet::MessageID)ID_GAME_DIE)->Packet(&bs, this, true);
        GetNetworking()->SendData(&bs);
        isDead = true;
    }
    else if (ptrNpcStats->getHealth().getCurrent() > 0 && isDead)
        isDead = false;
}

void LocalPlayer::updateDrawStateAndFlags(bool forceUpdate)
{
    MWBase::World *world = MWBase::Environment::get().getWorld();
    MWWorld::Ptr player = world->getPlayerPtr();


    MWMechanics::NpcStats ptrNpcStats = player.getClass().getNpcStats(player);
    using namespace MWMechanics;

    static bool oldRun = ptrNpcStats.getMovementFlag(CreatureStats::Flag_Run);
    static bool oldSneak = ptrNpcStats.getMovementFlag(CreatureStats::Flag_Sneak);
    static bool oldForceJump = ptrNpcStats.getMovementFlag(CreatureStats::Flag_ForceJump);
    static bool oldForceMoveJump = ptrNpcStats.getMovementFlag(CreatureStats::Flag_ForceMoveJump);

    bool run = ptrNpcStats.getMovementFlag(CreatureStats::Flag_Run);
    bool sneak = ptrNpcStats.getMovementFlag(CreatureStats::Flag_Sneak);
    bool forceJump = ptrNpcStats.getMovementFlag(CreatureStats::Flag_ForceJump);
    bool forceMoveJump = ptrNpcStats.getMovementFlag(CreatureStats::Flag_ForceMoveJump);
    bool jump = !world->isOnGround(player) && !world->isFlying(player);
    static bool onJump = false;

    MWMechanics::DrawState_ state = player.getClass().getNpcStats(player).getDrawState();
    static MWMechanics::DrawState_ oldState = player.getClass().getNpcStats(player).getDrawState();
    //static float timer = 0;
    if (oldRun != run
        || oldSneak != sneak || oldForceJump != forceJump
        || oldForceMoveJump != forceMoveJump || oldState != state ||
        ((jump || onJump)/* && (timer += MWBase::Environment::get().getFrameDuration() )> 0.5*/)
        || forceUpdate)
    {
        oldSneak = sneak;
        oldRun = run;
        oldForceJump = forceJump;
        oldForceMoveJump = forceMoveJump;
        oldState = state;
        onJump = jump;

        movementFlags = 0;
#define __SETFLAG(flag, value) (value) ? (movementFlags | flag) : (movementFlags & ~flag)

        movementFlags = __SETFLAG(CreatureStats::Flag_Sneak, sneak);
        movementFlags = __SETFLAG(CreatureStats::Flag_Run, run);
        movementFlags = __SETFLAG(CreatureStats::Flag_ForceJump, forceJump);
        movementFlags = __SETFLAG(CreatureStats::Flag_ForceJump, jump);
        movementFlags = __SETFLAG(CreatureStats::Flag_ForceMoveJump, forceMoveJump);

#undef __SETFLAG

        if (state == MWMechanics::DrawState_Nothing)
            (*DrawState()) = 0;
        else if (state == MWMechanics::DrawState_Weapon)
            (*DrawState()) = 1;
        else if (state == MWMechanics::DrawState_Spell)
            (*DrawState()) = 2;

        if (jump)
            mwmp::Main::get().getLocalPlayer()->updatePosition(true); // fix position after jump;

        RakNet::BitStream bs;
        GetNetworking()->GetPlayerPacket((RakNet::MessageID) ID_GAME_DRAWSTATE)->Packet(&bs, this, true);
        GetNetworking()->SendData(&bs);
        //timer = 0;
    }
}

void LocalPlayer::setDynamicStats()
{
    MWBase::World *world = MWBase::Environment::get().getWorld();
    MWWorld::Ptr player = world->getPlayerPtr();

    MWMechanics::CreatureStats *ptrCreatureStats = &player.getClass().getCreatureStats(player);
    MWMechanics::DynamicStat<float> dynamicStat;

    for (int i = 0; i < 3; ++i)
    {
        dynamicStat = ptrCreatureStats->getDynamic(i);
        dynamicStat.setBase(CreatureStats()->mDynamic[i].mBase);
        dynamicStat.setCurrent(CreatureStats()->mDynamic[i].mCurrent);
        ptrCreatureStats->setDynamic(i, dynamicStat);
    }
}

void LocalPlayer::setAttributes()
{
    MWBase::World *world = MWBase::Environment::get().getWorld();
    MWWorld::Ptr player = world->getPlayerPtr();

    MWMechanics::CreatureStats *ptrCreatureStats = &player.getClass().getCreatureStats(player);
    MWMechanics::AttributeValue attributeValue;

    for (int i = 0; i < 8; ++i)
    {
        attributeValue.readState(CreatureStats()->mAttributes[i]);
        ptrCreatureStats->setAttribute(i, attributeValue);
    }
}

void LocalPlayer::setSkills()
{
    MWBase::World *world = MWBase::Environment::get().getWorld();
    MWWorld::Ptr player = world->getPlayerPtr();

    MWMechanics::NpcStats *ptrNpcStats = &player.getClass().getNpcStats(player);
    MWMechanics::SkillValue skillValue;

    for (int i = 0; i < 27; ++i)
    {
        skillValue.readState(NpcStats()->mSkills[i]);
        ptrNpcStats->setSkill(i, skillValue);
    }

    for (int i = 0; i < 8; ++i)
    {
        ptrNpcStats->setSkillIncrease(i, NpcStats()->mSkillIncrease[i]);
    }

    ptrNpcStats->setLevelProgress(NpcStats()->mLevelProgress);
}

void LocalPlayer::setLevel()
{
    MWBase::World *world = MWBase::Environment::get().getWorld();
    MWWorld::Ptr player = world->getPlayerPtr();

    MWMechanics::CreatureStats *ptrCreatureStats = &player.getClass().getCreatureStats(player);
    ptrCreatureStats->setLevel(CreatureStats()->mLevel);
}

void LocalPlayer::setPosition()
{
    MWBase::World *world = MWBase::Environment::get().getWorld();
    MWWorld::Ptr player = world->getPlayerPtr();

    world->getPlayer().setTeleported(true);
    world->moveObject(player, Position()->pos[0], Position()->pos[1], Position()->pos[2]);
    world->rotateObject(player, Position()->rot[0], Position()->rot[1], Position()->rot[2]);

    updatePosition(true);
}

void LocalPlayer::setCell()
{
    MWBase::World *world = MWBase::Environment::get().getWorld();
    MWWorld::Ptr player = world->getPlayerPtr();
    ESM::Position pos;

    world->getPlayer().setTeleported(true);

    int x = GetCell()->mData.mX;
    int y = GetCell()->mData.mY;

    if (GetCell()->isExterior())
    {
        world->indexToPosition(x, y, pos.pos[0], pos.pos[1], true);
        pos.pos[2] = 0;

        pos.rot[0] = pos.rot[1] = pos.rot[2] = 0;

        world->changeToExteriorCell(pos, true);
        world->fixPosition(player);
    }
    else if (world->findExteriorPosition(GetCell()->mName, pos))
    {
        world->changeToExteriorCell(pos, true);
        world->fixPosition(player);
    }
    else
    {
        world->findInteriorPosition(GetCell()->mName, pos);
        world->changeToInteriorCell(GetCell()->mName, pos, true);
    }

    updateCell(true);
}

void LocalPlayer::setClass()
{
    if (charClass.mId.empty()) // custom class
    {
        charClass.mData.mIsPlayable = 0x1;
        MWBase::Environment::get().getMechanicsManager()->setPlayerClass(charClass);
        MWBase::Environment::get().getWindowManager()->setPlayerClass(charClass);
    }
    else
    {
        MWBase::Environment::get().getMechanicsManager()->setPlayerClass(charClass.mId);

        const ESM::Class *existingCharClass = MWBase::Environment::get().getWorld()->getStore().get<ESM::Class>().find(charClass.mId);

        if (existingCharClass)
            MWBase::Environment::get().getWindowManager()->setPlayerClass(charClass);
    }
}

void LocalPlayer::setInventory()
{
    MWWorld::Ptr ptrPlayer = GetPlayerPtr();

    MWWorld::InventoryStore &ptrInventory = ptrPlayer.getClass().getInventoryStore(ptrPlayer);

    for (int slot = 0; slot < MWWorld::InventoryStore::Slots; slot++)
    {
        mwmp::Item *currentItem = EquipedItem(slot);

        if (!currentItem->refid.empty())
        {
            MWWorld::ContainerStoreIterator it = ptrInventory.begin();
            for (; it != ptrInventory.end(); ++it) // find item in inventory
            {
                if (::Misc::StringUtils::ciEqual(it->getCellRef().getRefId(), currentItem->refid))
                    break;
            }
            if (it == ptrInventory.end()) // if not exists add item
                ptrInventory.equip(slot, ptrInventory.ContainerStore::add(EquipedItem(slot)->refid.c_str(), 1, ptrPlayer), ptrPlayer);
            else
                ptrInventory.equip(slot, it, ptrPlayer);
        }
    }
}

void LocalPlayer::sendClass()
{
    MWBase::World *world = MWBase::Environment::get().getWorld();
    const ESM::NPC *cpl = world->getPlayerPtr().get<ESM::NPC>()->mBase;
    const ESM::Class *cls = world->getStore().get<ESM::Class>().find(cpl->mClass);

    if (cpl->mClass.find("$dynamic") != string::npos) // custom class
    {
        charClass.mId = "";
        charClass.mName = cls->mName;
        charClass.mDescription = cls->mDescription;
        charClass.mData = cls->mData;
    }
    else
        charClass.mId = cls->mId;

    GetNetworking()->GetPlayerPacket(ID_GAME_CHARCLASS)->Send(this);
}

void LocalPlayer::sendAttack(char type)
{
    MWMechanics::DrawState_ state = GetPlayerPtr().getClass().getNpcStats(GetPlayerPtr()).getDrawState();

    if (state == MWMechanics::DrawState_Spell)
    {

    }
    else
    {

    }
    GetAttack()->type = type;
    GetAttack()->pressed = false;
    RakNet::BitStream bs;
    GetNetworking()->GetPlayerPacket((RakNet::MessageID) ID_GAME_ATTACK)->Packet(&bs, this, true);
    GetNetworking()->SendData(&bs);
}

void LocalPlayer::prepareAttack(char type, bool state)
{
    if (GetAttack()->pressed == state)
        return;

    MWMechanics::DrawState_ dstate = GetPlayerPtr().getClass().getNpcStats(GetPlayerPtr()).getDrawState();

    if (dstate == MWMechanics::DrawState_Spell)
    {
        const string &spell = MWBase::Environment::get().getWindowManager()->getSelectedSpell();

        GetAttack()->refid = spell;
    }
    else
    {

    }

    GetAttack()->pressed = state;
    GetAttack()->type = type;
    GetAttack()->knockdown = false;
    GetAttack()->success = false;
    GetAttack()->block = false;
    GetAttack()->target = RakNet::RakNetGUID();
    GetAttack()->attacker = guid;

    RakNet::BitStream bs;
    GetNetworking()->GetPlayerPacket((RakNet::MessageID) ID_GAME_ATTACK)->Packet(&bs, this, true);
    GetNetworking()->SendData(&bs);
}