#define CC_COLORS_TYPE CC_COLORS_SHORT
#include <amxmodx>
#include <amxmisc>
#include <reapi>
#include <cstrike>
#include <cromchat>
#include <fvault>
#include <fun>

#define PLUGIN_NAME        "ReParachute - Simple"
#define PLUGIN_VERS        "3.0"
#define PLUGIN_AUTH        "Modified by Rem - Original by PurposeLess"

const KEYSMENU = MENU_KEY_1|MENU_KEY_2|MENU_KEY_3|MENU_KEY_4|MENU_KEY_5|MENU_KEY_6|MENU_KEY_7|MENU_KEY_8|MENU_KEY_9|MENU_KEY_0;
const MENU_KEY_EXIT = 9

new vault_filename[] = "Reparachute_parachute";

enum _:ModelsInfo {
    Flag,
    MenuName[200],
    ModelPath[200],
    SpecificUser[32]
}

new model_ent[MAX_CLIENTS + 1];
new bool:g_alive[MAX_CLIENTS + 1];
new parachute_model[33][64];
new bool:g_parachute_active[33];

new g_PlayerPageMenu[33];
new g_PlayerMenu_Items[33][8];
new Float:g_flLastMenuViewed[33];
new g_player_last_team[33];

new const g_eModels[][ModelsInfo] = {
    { 0, "Default Parachute", "models/reparachute/parachute_default.mdl", "" },
    { ADMIN_LEVEL_H, "Deportive Parachute", "models/reparachute/parachute_deportive.mdl", "" },
    { ADMIN_LEVEL_H, "Militar Parachute", "models/reparachute/parachute_militar.mdl", "" },
    { ADMIN_LEVEL_H, "Militar Parachute (Blue)", "models/reparachute/parachute_militar_blue.mdl", "" },
    { ADMIN_LEVEL_H, "Dragon Wings", "models/reparachute/parachute_dragon.mdl", "" },
    { ADMIN_LEVEL_H, "Fenix Wings", "models/reparachute/parachute_fenix.mdl", "" },
    { ADMIN_LEVEL_H, "Divine Wings", "models/reparachute/parachute_divine.mdl", "" },
    { ADMIN_LEVEL_H, "Fairy Wings", "models/reparachute/parachute_fairy.mdl", "" }
    // { admin flag / 0 for everyone , "parachute name", "parachute model path", "steam id or name access" }
};

new reparachute_mode;
new reparachute_team;
new reparachute_animations;
new para_model_index[sizeof(g_eModels)];

new g_iMenuId_Skins;

public plugin_init() {
    register_plugin(PLUGIN_NAME, PLUGIN_VERS, PLUGIN_AUTH);

    RegisterHookChain(RG_CBasePlayer_PreThink, "@CBasePlayer_PreThink", .post=false);
    RegisterHookChain(RG_CBasePlayer_Spawn, "@CBasePlayer_Spawn", .post=true);
    RegisterHookChain(RG_CBasePlayer_Killed, "@CBasePlayer_Killed", .post=false);

    register_concmd("say parachute", "cmdParachuteMenu");
    register_concmd("say_team parachute", "cmdParachuteMenu");
    register_concmd("say /parachute", "cmdParachuteMenu");
    register_concmd("say_team /parachute", "cmdParachuteMenu");
    register_concmd("parachute_menu", "cmdParachuteMenu");
    CC_SetPrefix("!r[!gReParachute!r]");

    reparachute_mode = register_cvar("reparachute_mode", "0", ADMIN_RCON); // 0 = ALL, 1 = Admin Only, 2 = disable
    reparachute_team = register_cvar("reparachute_team", "0", ADMIN_RCON);  // 0 = ALL, 1 = Terrorist, 2 = Counter-Terrorist
    reparachute_animations = register_cvar("reparachute_animations", "0", ADMIN_RCON);  // 0 = disable, 1 = enable

    if (!fvault_touch(vault_filename, "init")) {
        set_fail_state("Error opening fvault");
    }

    g_iMenuId_Skins = register_menuid("Show_SkinsMenu");
    register_menucmd(g_iMenuId_Skins, KEYSMENU, "HandleSkinsMenu");
    register_menucmd(register_menuid("ParachuteMainMenu"), KEYSMENU, "mainMenuHandler");
}

public plugin_precache() {
    for (new i = 0; i < sizeof(g_eModels); i++) {
        para_model_index[i] = precache_model(g_eModels[i][ModelPath]);
    }
}

public isAdmin(id) {
    return get_user_flags(id) & ADMIN_RESERVATION;
}

public client_connect(id) {
    @reset_parachute(id);

    g_PlayerPageMenu[id] = 0;
    g_flLastMenuViewed[id] = 0.0;
    g_player_last_team[id] = 0;
    if (!loadParachute(id)) {
        format(parachute_model[id], sizeof(parachute_model[]), "models/reparachute/parachute_default.mdl");
        g_parachute_active[id] = true;
        saveParachute(id);
    } else {

        if (!is_valid_parachute_model(parachute_model[id])) {
            format(parachute_model[id], sizeof(parachute_model[]), "models/reparachute/parachute_default.mdl");
            saveParachute(id);
        }
    }
}

public client_disconnected(id) {
    g_alive[id] = false;
    @reset_parachute(id);
}

@CBasePlayer_Spawn(const id) {
    g_alive[id] = true;
    @reset_parachute(id);
    g_player_last_team[id] = get_user_team(id);
}

@CBasePlayer_Killed(const victim, const attacker) {
    g_alive[victim] = false;
    @reset_parachute(victim);
}

@CBasePlayer_PreThink(const id) {
    if (!g_alive[id] || !g_parachute_active[id]) {
        return;
    }

    new current_team = get_user_team(id);
    if (g_player_last_team[id] != current_team) {
        if (g_player_last_team[id] != 0) {
            @check_team_change_parachute(id, current_team);
        }
        g_player_last_team[id] = current_team;
    }

    new currentModelIndex = find_model_index(parachute_model[id]);

    if (!isAdmin(id) && currentModelIndex != -1 && g_eModels[currentModelIndex][Flag] != 0) {
        format(parachute_model[id], sizeof(parachute_model[]), "models/reparachute/parachute_default.mdl");
        saveParachute(id);

        if (model_ent[id]) {
            @reset_parachute(id);
        }
    }

    if (!canUseParachuteTeam(id)) {
        return;
    }

    if (!canUseParachuteFlags(id)) {
        return;
    }

    currentModelIndex = find_model_index(parachute_model[id]);

    if (currentModelIndex != -1 && !canSelectModel(id, currentModelIndex)) {
        if (!equal(parachute_model[id], "models/reparachute/parachute_default.mdl")) {
            format(parachute_model[id], sizeof(parachute_model[]), "models/reparachute/parachute_default.mdl");
        }
    }

    static waterlevel;
    waterlevel = get_entvar(id, var_waterlevel);

    if (waterlevel > 0) {
        @reset_parachute(id);
        return;
    }

    static button, oldbuttons;
    button = get_entvar(id, var_button);
    oldbuttons = get_entvar(id, var_oldbuttons);

    if (button & IN_USE) {
        static Float:velocity[3];
        get_entvar(id, var_velocity, velocity);

        if (velocity[2] < 0.0) {
            if (!model_ent[id]) {

                if (!is_valid_parachute_model(parachute_model[id])) {

                    format(parachute_model[id], sizeof(parachute_model[]), "models/reparachute/parachute_default.mdl");
                    saveParachute(id);
                }

                new modelIndex = find_model_index(parachute_model[id]);
                if (modelIndex == -1) {

                    modelIndex = 0;
                    format(parachute_model[id], sizeof(parachute_model[]), "models/reparachute/parachute_default.mdl");
                    saveParachute(id);
                }

                new name[32];
                get_user_name(id, name, charsmax(name));

                model_ent[id] = rg_create_entity("info_target");

                set_entvar(model_ent[id], var_model, parachute_model[id]);
                set_entvar(model_ent[id], var_modelindex, para_model_index[modelIndex]);
                set_entvar(model_ent[id], var_movetype, MOVETYPE_FOLLOW);
                set_entvar(model_ent[id], var_aiment, id);

                if (get_pcvar_num(reparachute_animations)) {
                    set_entvar(model_ent[id], var_sequence, 0); 
                    set_entvar(model_ent[id], var_frame, 0.0);
                    set_entvar(model_ent[id], var_gaitsequence, 1);
                } else {
                    set_entvar(model_ent[id], var_sequence, 1); 
                    set_entvar(model_ent[id], var_gaitsequence, 1);
                }

            }

            velocity[2] = (velocity[2] + 40.0 < -100.0) ? velocity[2] + 40.0 : -100.0;

            set_entvar(id, var_sequence, 3);
            set_entvar(id, var_gaitsequence, 1);
            set_entvar(id, var_velocity, velocity);

            if (get_pcvar_num(reparachute_animations) && model_ent[id]) {
                new Float:current_frame = get_entvar(model_ent[id], var_frame);
                new current_sequence = get_entvar(model_ent[id], var_sequence);

                if (current_frame < 0.0 || current_frame > 254.0) {

                    if (current_sequence != 1) {
                        set_entvar(model_ent[id], var_sequence, 1); 
                    }
                    set_entvar(model_ent[id], var_frame, 0.0);
                } else {

                    set_entvar(model_ent[id], var_frame, current_frame + 1.0);
                }
            }
        } else {
            @reset_parachute(id);
        }
    } else if (oldbuttons & IN_USE) {
        @reset_parachute(id);
    }
}

public @reset_parachute(id) {
    if (model_ent[id]) {

        if (get_pcvar_num(reparachute_animations)) {
            set_entvar(model_ent[id], var_sequence, 2); 
            set_entvar(model_ent[id], var_frame, 0.0);
        }

        set_entvar(model_ent[id], var_flags, FL_KILLME);
        model_ent[id] = 0;
    }
}

public resetParachuteConfig(id) {
    format(parachute_model[id], sizeof(parachute_model[]), "models/reparachute/parachute_default.mdl");
    g_parachute_active[id] = true;

    if (model_ent[id]) {

        if (is_valid_parachute_model(parachute_model[id])) {
            set_entvar(model_ent[id], var_model, parachute_model[id]);
            set_entvar(model_ent[id], var_modelindex, para_model_index[find_model_index(parachute_model[id])]);
        } else {

            set_entvar(model_ent[id], var_model, "models/reparachute/parachute_default.mdl");
            set_entvar(model_ent[id], var_modelindex, para_model_index[0]);
        }
    }

    saveParachute(id);
}

public canSelectModel(id, modelIndex) {
    if ((g_eModels[modelIndex][Flag] == 0) || (get_user_flags(id) & g_eModels[modelIndex][Flag])) {
        if (strlen(g_eModels[modelIndex][SpecificUser]) == 0) {
            return true;
        }
        new authid[32], username[32];
        get_user_authid(id, authid, sizeof(authid));
        get_user_name(id, username, sizeof(username));

        if (equal(authid, g_eModels[modelIndex][SpecificUser]) || equal(username, g_eModels[modelIndex][SpecificUser])) {
        return true;
        }
    }
    return false;
}

public checkParachuteAccess(id) {
    if (get_pcvar_num(reparachute_mode) == 1) {
        CC_SendMessage(id, "!rMenus disabled");
        return false;
    }

    if (get_pcvar_num(reparachute_mode) == 2) {
        CC_SendMessage(id, "!rMenus disabled");
        return false;
    }

    if (!canUseParachuteFlags(id)) {
        CC_SendMessage(id, "!rAccess denied by server configuration");
        return false;
    }

    return true;
}

public cmdParachuteMenu(id) {
    if (!checkParachuteAccess(id)) {
        return;
    }

    new szMenu[1024], len = 0;
    new keys = MENU_KEY_0;

    len += formatex(szMenu[len], charsmax(szMenu) - len, "\r[ \yReParachute \r] \d- \wMain Menu^n\
        \d» \wCreated by \rRem^n\
        \d» \wVersion: \r3.0^n^n");

    len += formatex(szMenu[len], charsmax(szMenu) - len, "\r1.\w Skins Menu^n");
    keys |= MENU_KEY_1;

    len += formatex(szMenu[len], charsmax(szMenu) - len, "\r2.\w Parachute \d[%s\d]^n", g_parachute_active[id] ? "\yEnabled" : "\rDisabled");
    keys |= MENU_KEY_2;

    len += formatex(szMenu[len], charsmax(szMenu) - len, "^n\r0.\w Exit");
    keys |= MENU_KEY_0;

    show_menu(id, keys, szMenu, -1, "ParachuteMainMenu");
}

public mainMenuHandler(id, key) {
    if (!checkParachuteAccess(id)) {
        return PLUGIN_HANDLED;
    }

    switch (key) {
        case MENU_KEY_EXIT: {
            return PLUGIN_HANDLED;
        }
        case 0: { 
            cmdSkinMenu(id);
        }
        case 1: { 
            g_parachute_active[id] = !g_parachute_active[id];
            saveParachute(id);

            CC_SendMessage(id, g_parachute_active[id] ? "!bParachute enabled" : "!rParachute disabled");
            if (!g_parachute_active[id]) {
                @reset_parachute(id);
            }

            cmdParachuteMenu(id);
        }
    }

    return PLUGIN_HANDLED;
}

public cmdSkinMenu(id) {
    if (!checkParachuteAccess(id)) {
        return;
    }

    g_PlayerPageMenu[id] = 0;
    Show_SkinsMenu(id, 0);
}

public Show_SkinsMenu(id, page) {
    if (!checkParachuteAccess(id)) {
        return;
    }

    new szMenu[1024], len = 0;
    new keys = MENU_KEY_0;

    len += formatex(szMenu[len], charsmax(szMenu) - len, "\r[ \yReParachute \r] \d- \wParachute Skins^n^n");
    len += formatex(szMenu[len], charsmax(szMenu) - len, "\d» \wSelect your favorite skin^n^n");

    new validItems[sizeof(g_eModels)], validCount = 0;
    for (new i = 0; i < sizeof(g_eModels); i++) {
        if (canSelectModel(id, i)) {
            validItems[validCount] = i;
            validCount++;
        }
    }

    new itemsPerPage = 8;
    new start = page * itemsPerPage;
    new end = min(start + itemsPerPage, validCount);

    for (new i = 0; i < 8; i++) {
        g_PlayerMenu_Items[id][i] = -1;
    }

    new menuPos = 0;
    for (new i = start; i < end; i++) {
        new modelIndex = validItems[i];
        g_PlayerMenu_Items[id][menuPos] = modelIndex;

        if (equal(parachute_model[id], g_eModels[modelIndex][ModelPath])) {
            len += formatex(szMenu[len], charsmax(szMenu) - len, "\r%d.\d %s \w(\rSelected\w)^n", menuPos + 1, g_eModels[modelIndex][MenuName]);
        } else {
            len += formatex(szMenu[len], charsmax(szMenu) - len, "\r%d.\w %s^n", menuPos + 1, g_eModels[modelIndex][MenuName]);
        }

        keys |= (1 << menuPos);
        menuPos++;
    }

    if (validCount > 8) {
        len += formatex(szMenu[len], charsmax(szMenu) - len, "^n");

        if (end < validCount) {
            len += formatex(szMenu[len], charsmax(szMenu) - len, "\r9.\w Next Page^n");
            keys |= MENU_KEY_9;
        } else {
            len += formatex(szMenu[len], charsmax(szMenu) - len, "\d9. Next Page^n"); 
        }

        if (page > 0) {
            len += formatex(szMenu[len], charsmax(szMenu) - len, "\r0.\w Previous Page");
            keys |= MENU_KEY_0;
        } else {
            len += formatex(szMenu[len], charsmax(szMenu) - len, "\r0.\w Back");
            keys |= MENU_KEY_0;
        }
    } else {
        len += formatex(szMenu[len], charsmax(szMenu) - len, "^n\r0.\w Back");
        keys |= MENU_KEY_0;
    }

    g_PlayerPageMenu[id] = page;
    show_menu(id, keys, szMenu, -1, "Show_SkinsMenu");
}

public HandleSkinsMenu(id, key) {
    if (!checkParachuteAccess(id)) {
        return PLUGIN_HANDLED;
    }

    switch (key) {
        case 8: { // Key 9 - Next Page
            new validCount = 0;
            for (new i = 0; i < sizeof(g_eModels); i++) {
                if (canSelectModel(id, i)) {
                    validCount++;
                }
            }

            if (validCount > 8 && (g_PlayerPageMenu[id] + 1) * 8 < validCount) {
                Show_SkinsMenu(id, g_PlayerPageMenu[id] + 1);
            }
            return PLUGIN_HANDLED;
        }
        case 9: { // Key 0 - Previous Page or Back
            if (g_PlayerPageMenu[id] > 0) {
                Show_SkinsMenu(id, g_PlayerPageMenu[id] - 1);
            } else {
                cmdParachuteMenu(id);
            }
            return PLUGIN_HANDLED;
        }
        default: {
            new itemIndex = g_PlayerMenu_Items[id][key];

            if (itemIndex >= 0 && itemIndex < sizeof(g_eModels)) {
                if (is_valid_parachute_model(g_eModels[itemIndex][ModelPath])) {
                    format(parachute_model[id], sizeof(parachute_model[]), "%s", g_eModels[itemIndex][ModelPath]);

                    if (model_ent[id]) {
                        set_entvar(model_ent[id], var_model, parachute_model[id]);
                        set_entvar(model_ent[id], var_modelindex, para_model_index[itemIndex]);
                    }

                    saveParachute(id);
                } else {
                    format(parachute_model[id], sizeof(parachute_model[]), "models/reparachute/parachute_default.mdl");

                    if (model_ent[id]) {
                        set_entvar(model_ent[id], var_model, parachute_model[id]);
                        set_entvar(model_ent[id], var_modelindex, para_model_index[0]);
                    }

                    saveParachute(id);
                    CC_SendMessage(id, "!rInvalid model, default model was used");
                }

                Show_SkinsMenu(id, g_PlayerPageMenu[id]);
            }
            return PLUGIN_HANDLED;
        }
    }

    return PLUGIN_HANDLED;
}

saveParachute(id) {
    new vaultkey[64], vaultdata[128];
    get_user_authid(id, vaultkey, sizeof(vaultkey));

    if (equal(vaultkey, "STEAM_ID_LAN")) {
        get_user_name(id, vaultkey, sizeof(vaultkey));
    }

    format(vaultdata, sizeof(vaultdata), "%s %d", parachute_model[id], g_parachute_active[id]);

    fvault_set_data(vault_filename, vaultkey, vaultdata);
}

loadParachute(id) {
    new vaultkey[64], vaultdata[128];
    get_user_authid(id, vaultkey, sizeof(vaultkey));

    if (equal(vaultkey, "STEAM_ID_LAN")) {
        get_user_name(id, vaultkey, sizeof(vaultkey));
    }

    if (fvault_get_data(vault_filename, vaultkey, vaultdata, sizeof(vaultdata))) {
        new model[64], active[5];
        parse(vaultdata, model, sizeof(model), active, sizeof(active));

        if (is_valid_parachute_model(model)) {
            format(parachute_model[id], sizeof(parachute_model[]), "%s", model);
        } else {

            format(parachute_model[id], sizeof(parachute_model[]), "models/reparachute/parachute_default.mdl");
        }

        g_parachute_active[id] = (str_to_num(active) != 0);
        return true;
    }

    format(parachute_model[id], sizeof(parachute_model[]), "models/reparachute/parachute_default.mdl");
    g_parachute_active[id] = true;
    return false;
}

public find_model_index(model[]) {

    if (strlen(model) == 0 || !containi(model, ".mdl")) {
        return -1;
    }

    if (!equal(model, "models/", 7)) {
        return -1;
    }

    for (new i = 0; i < sizeof(g_eModels); i++) {
        if (equal(g_eModels[i][ModelPath], model)) {
            return i;
        }
    }
    return -1;
}

public bool:is_valid_parachute_model(model[]) {

    if (strlen(model) == 0) {
        return false;
    }

    if (!containi(model, ".mdl")) {
        return false;
    }

    if (!equal(model, "models/", 7)) {
        return false;
    }

    if (!containi(model, "parachute_")) {
        return false;
    }

    return (find_model_index(model) != -1);
}

public canUseParachuteTeam(id) {
    new team = get_user_team(id);
    new team_mode = get_pcvar_num(reparachute_team);

    switch (team_mode) {
        case 0:
            return true;
        case 1:
            if (team == 1) return true;
        case 2:
            if (team == 2) return true;
    }
    return false;
}

public canUseParachuteFlags(id) {
    new mode = get_pcvar_num(reparachute_mode);

    switch (mode) {
        case 0:
            return true;
        case 1:
            return isAdmin(id);
        case 2:
        {
            removeAllParachutes();
            return false;
        }
    }

    return false;
}

removeAllParachutes() {
    for (new id = 1; id <= get_maxplayers(); id++) {
        if (is_user_connected(id)) {
            @reset_parachute(id);
        }
    }
}

public @check_team_change_parachute(id, new_team) {
    if (!is_user_connected(id) || !g_alive[id]) {
        return;
    }

    if (!canUseParachuteTeam(id)) {

        if (model_ent[id]) {
            @reset_parachute(id);
            CC_SendMessage(id, "!rParachute disabled due to team change");
        }
    }
}