const std = @import("std");
const protocol = @import("protocol");
const Session = @import("../Session.zig");
const Packet = @import("../Packet.zig");
const Config = @import("../services/config.zig");
const Res_config = @import("../services/res_config.zig");
const Data = @import("../data.zig");

const UidGenerator = @import("../services/item.zig").UidGenerator;
const ArrayList = std.ArrayList;
const Allocator = std.mem.Allocator;
const CmdID = protocol.CmdID;

pub const SceneManager = struct {
    allocator: std.mem.Allocator,

    pub fn init(allocator: std.mem.Allocator) SceneManager {
        return SceneManager{ .allocator = allocator };
    }

    pub fn createScene(
        self: *SceneManager,
        plane_id: u32,
        floor_id: u32,
        entry_id: u32,
        teleport_id: u32,
    ) !protocol.SceneInfo {
        const config = try Config.loadGameConfig(self.allocator, "config.json");
        const res_config = try Res_config.anchorLoader(self.allocator, "resources/res.json");
        var generator = UidGenerator().init();
        var scene_info = protocol.SceneInfo.init(self.allocator);
        scene_info.game_mode_type = 1;
        scene_info.plane_id = plane_id;
        scene_info.floor_id = floor_id;
        scene_info.entry_id = entry_id;
        scene_info.leader_entity_id = 100000;
        scene_info.world_id = 501;
        scene_info.client_pos_version = 1;
        var group_map = std.AutoHashMap(u32, protocol.SceneEntityGroupInfo).init(self.allocator);
        defer group_map.deinit();

        for (res_config.scene_config.items) |sceneConf| {
            for (sceneConf.teleports.items) |teleConf| {
                if (teleConf.teleportId == teleport_id) {
                    var scene_group = protocol.SceneEntityGroupInfo.init(self.allocator);
                    scene_group.state = 1;
                    for (config.avatar_config.items, 0..) |avatarConf, idx| {
                        try scene_group.entity_list.append(.{
                            .InstId = 1,
                            .EntityId = @intCast(idx + 100000),
                            .entityCase_ = .{ .Actor = .{
                                .base_avatar_id = avatarConf.id,
                                .avatar_type = .AVATAR_FORMAL_TYPE,
                                .uid = 0,
                                .map_layer = 0,
                            } },
                            .Motion = .{
                                .pos = .{ .x = teleConf.pos.x, .y = teleConf.pos.y, .z = teleConf.pos.z },
                                .rot = .{ .x = teleConf.rot.x, .y = teleConf.rot.y, .z = teleConf.rot.z },
                            },
                        });
                    }
                    try scene_info.entity_group_list.append(scene_group);
                }
            }
            if (scene_info.plane_id != 10000 and scene_info.plane_id != 10202 and sceneConf.planeID == scene_info.plane_id) {
                for (sceneConf.props.items) |propConf| {
                    var scene_group = try getOrCreateGroup(&group_map, propConf.groupId, self.allocator);
                    var prop_info = protocol.ScenePropInfo.init(self.allocator);
                    prop_info.prop_id = propConf.propId;
                    prop_info.prop_state = propConf.propState;
                    try scene_group.entity_list.append(.{
                        .entityCase_ = .{ .Prop = prop_info },
                        .GroupId = scene_group.group_id,
                        .InstId = propConf.instId,
                        .EntityId = generator.nextId(),
                        .Motion = .{
                            .pos = .{ .x = propConf.pos.x, .y = propConf.pos.y, .z = propConf.pos.z },
                            .rot = .{ .x = propConf.rot.x, .y = propConf.rot.y, .z = propConf.rot.z },
                        },
                    });
                }
                for (sceneConf.monsters.items) |monsConf| {
                    var scene_group = try getOrCreateGroup(&group_map, monsConf.groupId, self.allocator);
                    var monster_info = protocol.SceneNpcMonsterInfo.init(self.allocator);
                    monster_info.monster_id = monsConf.monsterId;
                    monster_info.event_id = monsConf.eventId;
                    monster_info.world_level = 6;
                    try scene_group.entity_list.append(.{
                        .entityCase_ = .{ .NpcMonster = monster_info },
                        .GroupId = scene_group.group_id,
                        .InstId = monsConf.instId,
                        .EntityId = generator.nextId(),
                        .Motion = .{
                            .pos = .{ .x = monsConf.pos.x, .y = monsConf.pos.y, .z = monsConf.pos.z },
                            .rot = .{ .x = monsConf.rot.x, .y = monsConf.rot.y, .z = monsConf.rot.z },
                        },
                    });
                }
            }
        }
        var iter = group_map.iterator();
        while (iter.next()) |entry| {
            try scene_info.entity_group_list.append(entry.value_ptr.*);
            try scene_info.entity_list.appendSlice(entry.value_ptr.entity_list.items);
            try scene_info.DJBIBIJMEBH.append(entry.value_ptr.group_id);
            try scene_info.custom_data_list.append(protocol.CustomSaveData{
                .group_id = entry.value_ptr.group_id,
            });
            try scene_info.group_state_list.append(protocol.SceneGroupState{
                .group_id = entry.value_ptr.group_id,
                .state = 0,
                .is_default = true,
            });
        }
        const ranges = [_][2]usize{
            .{ 0, 101 },
            .{ 10000, 10051 },
            .{ 20000, 20001 },
            .{ 30000, 30020 },
        };
        for (ranges) |range| {
            for (range[0]..range[1]) |i| {
                try scene_info.lighten_section_list.append(@intCast(i));
            }
        }
        return scene_info;
    }
    fn getOrCreateGroup(group_map: *std.AutoHashMap(u32, protocol.SceneEntityGroupInfo), group_id: u32, allocator: std.mem.Allocator) !*protocol.SceneEntityGroupInfo {
        if (group_map.getPtr(group_id)) |existing_group| {
            return existing_group;
        }
        var new_group = protocol.SceneEntityGroupInfo.init(allocator);
        new_group.state = 1;
        new_group.group_id = group_id;
        try group_map.put(group_id, new_group);
        return group_map.getPtr(group_id).?;
    }
};
pub const ChallengeSceneManager = struct {
    allocator: std.mem.Allocator,
    pub fn init(allocator: std.mem.Allocator) ChallengeSceneManager {
        return ChallengeSceneManager{ .allocator = allocator };
    }
    pub fn createScene(
        self: *ChallengeSceneManager,
        avatar_list: ArrayList(u32),
        plane_id: u32,
        floor_id: u32,
        entry_id: u32,
        world_id: u32,
        monster_id: u32,
        event_id: u32,
        group_id: u32,
        maze_group_id: u32,
    ) !protocol.SceneInfo {
        const res_config = try Res_config.anchorLoader(self.allocator, "resources/res.json");
        var generator = UidGenerator().init();

        var scene_info = protocol.SceneInfo.init(self.allocator);
        scene_info.game_mode_type = 4;
        scene_info.plane_id = plane_id;
        scene_info.floor_id = floor_id;
        scene_info.entry_id = entry_id;
        scene_info.leader_entity_id = 1;
        scene_info.world_id = world_id;
        try scene_info.group_state_list.append(protocol.SceneGroupState{
            .group_id = maze_group_id,
            .is_default = true,
        });
        { // Character
            var scene_group = protocol.SceneEntityGroupInfo.init(self.allocator);
            scene_group.state = 1;
            scene_group.group_id = 0;
            for (avatar_list.items, 0..) |avatarConf, idx| {
                const newidx = idx + 100000;
                try scene_group.entity_list.append(.{
                    .InstId = 1,
                    .EntityId = @intCast(newidx),
                    .entityCase_ = .{
                        .Actor = .{
                            .base_avatar_id = avatarConf,
                            .avatar_type = .AVATAR_FORMAL_TYPE,
                            .uid = 1,
                            .map_layer = 0,
                        },
                    },
                    .Motion = .{ .pos = .{}, .rot = .{} },
                });
            }
            try scene_info.entity_group_list.append(scene_group);
        }
        for (res_config.scene_config.items) |sceneConf| {
            if (sceneConf.planeID == scene_info.plane_id) {
                for (sceneConf.monsters.items) |monsConf| { //create monster
                    var scene_group = protocol.SceneEntityGroupInfo.init(self.allocator);
                    scene_group.state = 1;
                    if (monsConf.groupId == group_id) {
                        scene_group.group_id = group_id;

                        var monster_info = protocol.SceneNpcMonsterInfo.init(self.allocator);
                        monster_info.monster_id = monster_id;
                        monster_info.event_id = event_id;
                        monster_info.world_level = 6;

                        try scene_group.entity_list.append(.{
                            .entityCase_ = .{ .NpcMonster = monster_info },
                            .GroupId = group_id,
                            .InstId = monsConf.instId,
                            .EntityId = generator.nextId(),
                            .Motion = .{
                                .pos = .{ .x = monsConf.pos.x, .y = monsConf.pos.y, .z = monsConf.pos.z },
                                .rot = .{ .x = monsConf.rot.x, .y = monsConf.rot.y, .z = monsConf.rot.z },
                            },
                        });
                        try scene_info.entity_group_list.append(scene_group);
                    }
                }
            }
        }
        for (res_config.scene_config.items) |sceneConf| {
            if (sceneConf.planeID == scene_info.plane_id) {
                for (sceneConf.props.items) |propConf| { //create props
                    var scene_group = protocol.SceneEntityGroupInfo.init(self.allocator);
                    scene_group.state = 1;
                    scene_group.group_id = group_id;

                    var prop_info = protocol.ScenePropInfo.init(self.allocator);
                    prop_info.prop_id = propConf.propId;
                    prop_info.prop_state = propConf.propState;

                    try scene_group.entity_list.append(.{
                        .entityCase_ = .{ .Prop = prop_info },
                        .GroupId = group_id,
                        .InstId = propConf.instId,
                        .EntityId = 0,
                        .Motion = .{
                            .pos = .{ .x = propConf.pos.x, .y = propConf.pos.y, .z = propConf.pos.z },
                            .rot = .{ .x = propConf.rot.x, .y = propConf.rot.y, .z = propConf.rot.z },
                        },
                    });
                    try scene_info.entity_group_list.append(scene_group);
                }
            }
        }
        return scene_info;
    }
};

pub const MazeMapManager = struct {
    allocator: std.mem.Allocator,
    pub fn init(allocator: std.mem.Allocator) MazeMapManager {
        return MazeMapManager{ .allocator = allocator };
    }
    pub fn setMazeMapData(
        self: *MazeMapManager,
        map_info: *protocol.SceneMapInfo,
        floor_id: u32,
    ) !void {
        const config = try Config.loadMapEntranceConfig(self.allocator, "resources/MapEntrance.json");
        const res_config = try Res_config.anchorLoader(self.allocator, "resources/res.json");
        var plane_ids = ArrayList(u32).init(self.allocator);
        for (config.map_entrance_config.items) |entrConf| {
            if (entrConf.floor_id == floor_id) {
                try plane_ids.append(entrConf.plane_id);
            }
        }

        map_info.maze_group_list = ArrayList(protocol.MazeGroup).init(self.allocator);
        map_info.maze_prop_list = ArrayList(protocol.MazePropState).init(self.allocator);
        for (res_config.scene_config.items) |resConf| {
            for (plane_ids.items) |plane_id| {
                if (resConf.planeID == plane_id) {
                    for (resConf.props.items) |propConf| {
                        try map_info.maze_group_list.append(protocol.MazeGroup{
                            .NOBKEONAKLE = ArrayList(u32).init(self.allocator),
                            .group_id = propConf.groupId,
                        });
                        try map_info.maze_prop_list.append(protocol.MazePropState{
                            .group_id = propConf.groupId,
                            .config_id = propConf.instId,
                            .state = propConf.propState,
                        });
                    }
                }
            }
        }
    }
};
