#pragma once

#include "../core.h"

#include <cstdint>

namespace flyff {
    struct __attribute__((packed)) Object {
        struct StateFlags {
            StateFlags() = delete;

            static constexpr uint32_t Combat = 0x00000001;  // battle mode
            static constexpr uint32_t Walk = 0x00000002;    // walking mode
            static constexpr uint32_t Sit = 0x00000004;     // sitting state
            static constexpr uint32_t Fly = 0x0000008;      // flight mode
            static constexpr uint32_t Acceleration = 0x00000010;  // Acceleration status
            static constexpr uint32_t Etc = 0x00000020;  // custom motion
            static constexpr uint32_t AccelerationTurn = 0x00000040;  // Sharp turn.
            static constexpr uint32_t Turbo = 0x00000080;    // Turbo mode.
        };

        enum class MoveState : uint8_t {
            None,
            // Standby
            Stand,
            // sit down
            Sit,
            // walk forward
            MoveForward,
            // Walk backwards
            MoveBackward,
            // Walk sideways (horizontal movement)
            Left,
            Right,
            // Pick up (things, etc.)
            PickUp,
            // Walk horizontally left
            MoveLeft,
            // Walk to the right of the span
            MoveRight,
            // Run in place
            StopRun,
        };

        enum class AttackState : uint8_t {
            None,
            // 1st hit attack in action
            Attack1,
            // Double hit attack in action
            Attack2,
            Attack3,
            // ...
            Attack4,
            // OBJSTA_ATK_MAGIC1
            Unknown1,
            // Shoot arrow (OBJSTA_ATK_RANGE1)
            Ranged,
            Unknown3,
            Unknown4,

            // Magic Casting #1 (Start) (OBJSTA_ATK_CASTING1)
            Casting1,
            // Magic Casting 2 times (repeat.) (OBJSTA_ATK_CASTING2)
            Casting2,
            // Magic firing action. (OBJSTA_ATK_MAGICSKILL)
            MagicSkill,

            // wand attack in action
//            Magic,
//            // Ranged attack in action
//            Range,
//            Range3,
//            Range4,
//            // Melee combat skill in action
//            MeleeSkill,
//            // Long range combat skill in action
//            RangeSkill,
//            // Special Attack: Used by monsters.
//            SpecialAttack1,
//            // Special Attack 2: Used by monsters.
//            SpecialAttack2,
        };

        enum class JumpState : uint8_t {
            None,
            // Jump before jump - Jump before
            Ready,
            // Jumping
            Jump,
            // Falling
            Fall,
            // Landing
            Land,
        };

        struct State {
            State() = delete;

            static constexpr uint32_t None = 0x00000000; // Nothing.

            // All movement
            static constexpr uint32_t MovementAll = 0x000000FF;
            // Is there anything other than STAND?
            static constexpr uint32_t NotStand = (MovementAll & (~static_cast<uint32_t>(MoveState::Stand)));

            // turn left
            static constexpr uint32_t TurnLeft = 0x00000100;
            // right turn
            static constexpr uint32_t TurnRight = 0x00000200;
            static constexpr uint32_t TurnAll = 0x00000300;
            // look up/down
            static constexpr uint32_t LookUp = 0x00000400;
            static constexpr uint32_t LookDown = 0x00000800;
            static constexpr uint32_t LookAll = 0x00000C00;

            // jump

            // Jump before jump - Jump before
            static constexpr uint32_t FJumpReady = 0x00001000;
            // Jumping
            static constexpr uint32_t FJump = 0x00002000;
            // Falling
            static constexpr uint32_t FFall = 0x00003000;
            // Landing
            static constexpr uint32_t FLand = 0x00004000;
            // Jump before jump - Jump in place
            static constexpr uint32_t SJump1 = 0x00005000;
            // Jumping
            static constexpr uint32_t SJump2 = 0x00006000;
            // Falling
            static constexpr uint32_t SJump3 = 0x00007000;
            // Landing
            static constexpr uint32_t SJump4 = 0x00008000;
            // jump before jump - back jump
            static constexpr uint32_t BJumpReady = 0x00009000;
            // Jumping
            static constexpr uint32_t BJump = 0x0000a000;
            // Falling
            static constexpr uint32_t BFall = 0x0000b000;
            // Landing
            static constexpr uint32_t BLand = 0x0000c000;
            static constexpr uint32_t JumpAll = 0x0000F000;

            // attack

            // 1st hit attack in action
            static constexpr uint32_t Atk1 = 0x00010000;
            static constexpr uint32_t Atk2 = 0x00020000; // Double hit attack in action
            static constexpr uint32_t Atk3 = 0x00030000; // ...
            static constexpr uint32_t Atk4 = 0x00040000; // ... // Might be incorporated into the concept of "attacking" later....
            // wand attack
            static constexpr uint32_t AtkMagic1 = 0x00050000; // wand attack in action
            static constexpr uint32_t AtkRange1 = 0x00060000; // Ranged attack in action
            static constexpr uint32_t Range3 = 0x00070000; //
            static constexpr uint32_t Range4 = 0x00080000; //
            // melee skill
            static constexpr uint32_t AtkMeleeSkill = 0x00090000; // Melee combat skill in action
            static constexpr uint32_t AtkRangeSkill = 0x000a0000; // Long range combat skill in action
            static constexpr uint32_t AtkCasting1 = 0x000b0000; // Magic Casting #1 (Start)
            static constexpr uint32_t AtkCasting2 = 0x000c0000; // Magic Casting 2 times (repeat.)
            static constexpr uint32_t AtkMagicSkill = 0x000d0000; // Magic firing action.
            static constexpr uint32_t SpecialAtk1 = 0x00100000; // Special Attack: Used by monsters.
            static constexpr uint32_t SpecialAtk2 = 0x00200000; // Special Attack 2: Used by monsters.
            static constexpr uint32_t AtkAll = 0x00FF0000;
            //static constexpr uint32_t  RANGE_ALL 0x0f000000


            // Dead state (dead state)
            static constexpr uint32_t Dead = 0x08000000;
            // Dead state
            static constexpr uint32_t Disappear = 0x09000000;
            // Resurrection 0xd = 1101
            static constexpr uint32_t Resurrection = 0x0C000000;
            static constexpr uint32_t DeadReserved1 = 0x0D000000;
            // Death - If the 0x00800000 bit is on, it is dead.
            static constexpr uint32_t DieAll = 0x08000000;
            // Both Hit & Die
            static constexpr uint32_t DmgAll = 0x0F000000;

            // Normal action - No other commands have any effect during this action.
            static constexpr uint32_t Collect = 0x10000000; // Collect.
            static constexpr uint32_t Appear = 0x20000000; // Appearance scene.
            static constexpr uint32_t Appear2 = 0x30000000; // Appearance 2
            static constexpr uint32_t Stun = 0x40000000; // Stun status.
            static constexpr uint32_t ActionAll = 0xF0000000; //
        };
    };


    static std::ostream &operator<<(std::ostream &os, const Object::MoveState &state) {
        switch(state) {
            case Object::MoveState::None:
                os << "None";
                break;
            case Object::MoveState::Stand:
                os << "Stand";
                break;
            case Object::MoveState::Sit:
                os << "Sit";
                break;
            case Object::MoveState::MoveForward:
                os << "MoveForward";
                break;
            case Object::MoveState::MoveBackward:
                os << "MoveBackward";
                break;
            case Object::MoveState::Left:
                os << "Left";
                break;
            case Object::MoveState::Right:
                os << "Right";
                break;
            case Object::MoveState::PickUp:
                os << "PickUp";
                break;
            case Object::MoveState::MoveLeft:
                os << "MoveLeft";
                break;
            case Object::MoveState::MoveRight:
                os << "MoveRight";
                break;
            case Object::MoveState::StopRun:
                os << "StopRun";
                break;
            default:
                os << "MoveState(" << static_cast<unsigned>(state) << ")";
                break;
        }
        return os;
    }

    static std::ostream &operator<<(std::ostream &os, const Object::AttackState &state) {
        switch(state) {
            case Object::AttackState::None:
                os << "None";
                break;
            case Object::AttackState::Attack1:
                os << "Attack1";
                break;
            case Object::AttackState::Attack2:
                os << "Attack2";
                break;
            case Object::AttackState::Attack3:
                os << "Attack3";
                break;
            case Object::AttackState::Attack4:
                os << "Attack4";
                break;
            case Object::AttackState::Casting1:
                os << "Casting1";
                break;
            case Object::AttackState::Casting2:
                os << "Casting2";
                break;
            case Object::AttackState::MagicSkill:
                os << "MagicSkill";
                break;
            default:
                os << "AttackState(" << static_cast<unsigned>(state) << ")";
                break;
        }
        return os;
    }

    static std::ostream &operator<<(std::ostream &os, const Object::JumpState &state) {
        switch(state) {
            case Object::JumpState::None:
                os << "None";
                break;
            case Object::JumpState::Ready:
                os << "Ready";
                break;
            case Object::JumpState::Jump:
                os << "Jump";
                break;
            case Object::JumpState::Fall:
                os << "Fall";
                break;
            case Object::JumpState::Land:
                os << "Land";
                break;
            default:
                os << "JumpState(" << static_cast<unsigned>(state) << ")";
                break;
        }
        return os;
    }
};