//==========================================================================================================================================\\
//                                                                                                                                          \\
//                              Creative Commons Attribution-NonCommercial 4.0 International                                                \\
//                              https://creativecommons.org/licenses/by-nc/4.0/                                                             \\
//                                                                                                                                          \\
//   * You are free to:                                                                                                                     \\
//      * Copy and redistribute the material in any medium or format.                                                                       \\
//      * Remix, transform, and build upon the material.                                                                                    \\
//                                                                                                                                          \\
//   * Under the following terms:                                                                                                           \\
//      * You must give appropriate credit, provide a link to the license, and indicate if changes were made.                               \\
//      * You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use.                   \\
//      * You may not use the material for commercial purposes.                                                                             \\
//      * You may not apply legal terms or technological measures that legally restrict others from doing anything the license permits.     \\
//                                                                                                                                          \\
//==========================================================================================================================================\\

#include "json"
#include "Reflection"

#include "BetterWeapons/weapon_sniperrifle"
#include "BetterWeapons/weapon_pipewrench"
#include "BetterWeapons/weapon_9mmhandgun"
#include "BetterWeapons/weapon_shockrifle"
#include "BetterWeapons/weapon_uziakimbo"
#include "BetterWeapons/weapon_hornetgun"
#include "BetterWeapons/weapon_crossbow"
#include "BetterWeapons/weapon_minigun"
#include "BetterWeapons/weapon_crowbar"
#include "BetterWeapons/weapon_shotgun"
#include "BetterWeapons/weapon_python"
#include "BetterWeapons/weapon_9mmAR"
#include "BetterWeapons/weapon_eagle"
#include "BetterWeapons/weapon_m249"
#include "BetterWeapons/weapon_uzi"
#include "BetterWeapons/weapon_m16"

json pJson;
float m_flNextGrunt;
float flNextHornetSoundThink;
const int BW_MP5_DMG = g_EngineFuncs.CVarGetFloat('sk_plr_9mmAR_bullet');

void PluginInit()
{
	g_Module.ScriptInfo.SetAuthor( "Mikk" );
	g_Module.ScriptInfo.SetContactInfo( "https://github.com/Mikk155/Sven-Co-op" );

	pJson.load( 'plugins/BetterWeapons.json' );

	// This for testing, dont need on final release
                    Register();
}

bool Registered;

void MapInit()
{
	if( pJson.reload('plugins/BetterWeapons.json') != 1 )
	{
		UnRegister();
	}

	if( array<string>( pJson[ 'blacklist maps' ] ).find( string( g_Engine.mapname ) ) > 0 )
	{
		UnRegister();
		return;
	}

	if( !Registered )
	{
		Register();
	}

	flNextHornetSoundThink = 0.0;
	m_flNextGrunt = 0;
}

void UnRegister()
{
    g_Hooks.RemoveHook( Hooks::Game::MapChange, @OnMapChange );
    g_Hooks.RemoveHook( Hooks::Player::PlayerKilled, @OnPlayerKilled );
    g_Hooks.RemoveHook( Hooks::Player::PlayerPostThink, @OnPlayerPostThink );
    g_Hooks.RemoveHook( Hooks::Weapon::WeaponPrimaryAttack, @WeaponPrimaryAttack );
    g_Hooks.RemoveHook( Hooks::Weapon::WeaponTertiaryAttack, @WeaponTertiaryAttack );
    g_Hooks.RemoveHook( Hooks::Weapon::WeaponSecondaryAttack, @WeaponSecondaryAttack );
    Registered = false;
}

void Register()
{
    g_Hooks.RegisterHook( Hooks::Game::MapChange, @OnMapChange );
    g_Hooks.RegisterHook( Hooks::Player::PlayerKilled, @OnPlayerKilled );
    g_Hooks.RegisterHook( Hooks::Player::PlayerPostThink, @OnPlayerPostThink );
    g_Hooks.RegisterHook( Hooks::Weapon::WeaponPrimaryAttack, @WeaponPrimaryAttack );
    g_Hooks.RegisterHook( Hooks::Weapon::WeaponTertiaryAttack, @WeaponTertiaryAttack );
    g_Hooks.RegisterHook( Hooks::Weapon::WeaponSecondaryAttack, @WeaponSecondaryAttack );
    Registered = true;
}

void DynamicTracer( Vector start, Vector end ) // create tracer message
{
	NetworkMessage DTracer( MSG_PVS, NetworkMessages::SVC_TEMPENTITY, null );
		DTracer.WriteByte( TE_TRACER );
		DTracer.WriteCoord( start.x );
		DTracer.WriteCoord( start.y );
		DTracer.WriteCoord( start.z );
		DTracer.WriteCoord( end.x );
		DTracer.WriteCoord( end.y );
		DTracer.WriteCoord( end.z );
	DTracer.End();
}

void PlayTracer( CBasePlayer@ pPlayer, Vector& in vecAttachOrigin, Vector& in vecAttachAngles, TraceResult& in tr ) // play tracer message
{
	g_EngineFuncs.GetAttachment( pPlayer.edict(), 0, vecAttachOrigin, vecAttachAngles );
	DynamicTracer( vecAttachOrigin, tr.vecEndPos ); //+ g_Engine.v_forward * 64, tr.vecEndPos );
}

void WW2DynamicTracer( Vector start, Vector end, NetworkMessageDest msgType = MSG_PVS, edict_t@ dest = null )
{
	NetworkMessage WW2DT( msgType, NetworkMessages::SVC_TEMPENTITY, dest );
		WW2DT.WriteByte( TE_TRACER );
		WW2DT.WriteCoord( start.x );
		WW2DT.WriteCoord( start.y );
		WW2DT.WriteCoord( start.z );
		WW2DT.WriteCoord( end.x );
		WW2DT.WriteCoord( end.y );
		WW2DT.WriteCoord( end.z );
	WW2DT.End();
}

void WW2DynamicLight( Vector vecPos, int radius, int r, int g, int b, int8 life, int decay )
{
	NetworkMessage WW2DL( MSG_PVS, NetworkMessages::SVC_TEMPENTITY );
		WW2DL.WriteByte( TE_DLIGHT );
		WW2DL.WriteCoord( vecPos.x );
		WW2DL.WriteCoord( vecPos.y );
		WW2DL.WriteCoord( vecPos.z );
		WW2DL.WriteByte( radius );
		WW2DL.WriteByte( int(r) );
		WW2DL.WriteByte( int(g) );
		WW2DL.WriteByte( int(b) );
		WW2DL.WriteByte( life );
		WW2DL.WriteByte( decay );
	WW2DL.End();
}

void RemoveHornet(CBaseEntity@ pWho, Vector vecWhere, float flRadius) 
{
	array <CBaseEntity@> aEnts(32);
	int iNum = g_EntityFuncs.MonstersInSphere(aEnts, vecWhere, flRadius);
	for (int i = 0; i < iNum; ++i) 
	{
		CBaseEntity@ pEnt = aEnts[i];
		if (pEnt.pev.classname == "hornet") 
		{
			if (pEnt.FVisible(vecWhere))
			g_EntityFuncs.Remove(pEnt);
		}
	}
}

enum ATTACK
{
    PRIMARY = 1,
    SECONDARY = 2,
    TERTIARY = 3,
};

enum ReflectionHook
{
    NONE = 0,
    HOOK_HANDLED = 1
};

HookReturnCode OnMapChange()
{
    return HOOK_CONTINUE;
}

HookReturnCode WeaponPrimaryAttack( CBasePlayer@ pPlayer, CBasePlayerWeapon@ pWeapon ) { return OnPlayerAttack( pPlayer, pWeapon, ATTACK::PRIMARY ); }
HookReturnCode WeaponTertiaryAttack( CBasePlayer@ pPlayer, CBasePlayerWeapon@ pWeapon ) { return OnPlayerAttack( pPlayer, pWeapon, ATTACK::TERTIARY ); }
HookReturnCode WeaponSecondaryAttack( CBasePlayer@ pPlayer, CBasePlayerWeapon@ pWeapon ) { return OnPlayerAttack( pPlayer, pWeapon, ATTACK::SECONDARY ); }
HookReturnCode OnPlayerAttack( CBasePlayer@ pPlayer, CBasePlayerWeapon@ pWeapon, ATTACK AttackMode )
{
    if( pPlayer is null || pWeapon is null || pWeapon.m_fInReload )
        return HOOK_CONTINUE;

    Reflection::Function@ href = g_Reflection[ "BetterWeapons::" + pWeapon.GetClassname() + "::OnPlayerAttack" ];

    if( href !is null )
    {
        HookReturnCode bHooking = HOOK_CONTINUE;

        Reflection::ReturnValue@ pValue = href.Call( @pPlayer, @pWeapon, int(AttackMode) );

        int ibits = 0;
        if( pValue.HasReturnValue() )
        {
            pValue.ToAny().retrieve( ibits );
        }

        if( ibits != 0 )
        {
            ReflectionHook bits = ReflectionHook( ibits );

            if( ( bits & ReflectionHook::HOOK_HANDLED ) != 0 )
            {
                bHooking = HOOK_HANDLED;
            }
        }
        return bHooking;
    }

    return HOOK_CONTINUE;
}

HookReturnCode OnPlayerPostThink( CBasePlayer@ pPlayer )
{
    if( pPlayer is null )
        return HOOK_CONTINUE;

    CBaseEntity@ pItem = pPlayer.m_hActiveItem.GetEntity();
    CBasePlayerWeapon@ pWeapon = ( pItem !is null ? cast<CBasePlayerWeapon@>( pItem ) : null );

    if( pWeapon !is null )
    {
        if( g_Reflection[ "BetterWeapons::" + pWeapon.GetClassname() + "::OnPlayerPostThink" ] !is null )
            g_Reflection[ "BetterWeapons::" + pWeapon.GetClassname() + "::OnPlayerPostThink" ].Call( @pPlayer, @pWeapon );
    }

    return HOOK_CONTINUE;
}

HookReturnCode OnPlayerKilled( CBasePlayer@ pPlayer, CBaseEntity@ pAttacker, int iGib )
{
    if( pPlayer is null )
        return HOOK_CONTINUE;

    return HOOK_CONTINUE;
}
