package com.alterdekim.hearthhack.component;

import com.alterdekim.PegasusGame;
import com.alterdekim.PegasusShared;
import com.alterdekim.hearthhack.util.GameDeck;
import com.alterdekim.hearthhack.component.interfaces.GamePacketCallback;
import com.alterdekim.hearthhack.component.interfaces.IGameRoom;
import com.alterdekim.hearthhack.config.ObjectConfig;
import com.alterdekim.hearthhack.dto.RoomPlayerDTO;
import com.alterdekim.hearthhack.entity.Deck;
import com.alterdekim.hearthhack.game.*;
import com.alterdekim.hearthhack.service.UserService;
import com.alterdekim.hearthhack.util.PegasusPacket;
import com.google.protobuf.InvalidProtocolBufferException;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.stream.Collectors;
import java.util.stream.IntStream;


@Slf4j
public class GameRoom extends Thread implements IGameRoom {

    private static final List<PegasusShared.BnetId> bnets = List.of(PegasusShared.BnetId.newBuilder()
            .setHi(144115198130930503L)
            .setLo(78330215).build(), PegasusShared.BnetId.newBuilder()
            .setHi(144115198130930503L)
            .setLo(77500085).build());

    private final ObjectConfig objectConfig;

    @Getter
    private final List<RoomPlayerDTO> players;

    private final ConcurrentHashMap<Long, GameDeck> decks;

    private final ConcurrentHashMap<Long, GamePacketCallback> callbacks;

    private final UserService userService;

    @Getter
    private final String password;

    private GameState globalState;
    private RunningState runningState;

    @Getter
    private final ConcurrentLinkedQueue<IncomeGamePacket> incomeQueue;
    private final ConcurrentLinkedQueue<OutcomeGamePacket> outcomeQueue;

    // TODO: abolish that later. It's a temporary solution
    private int entity_id = 4;

    public GameRoom(List<RoomPlayerDTO> players, UserService userService, String password, ObjectConfig objectConfig) {
        this.players = players;
        this.userService = userService;
        this.password = password;
        this.incomeQueue = new ConcurrentLinkedQueue<>();
        this.outcomeQueue = new ConcurrentLinkedQueue<>();
        this.decks = new ConcurrentHashMap<>();
        this.callbacks = new ConcurrentHashMap<>();
        this.globalState = GameState.CreateGame;
        this.runningState = RunningState.INITIAL_DEAL;
        this.objectConfig = objectConfig;
        log.info("GameRoom players: {}", this.players);
        this.start(); // TODO: debug mode should stop that
    }

    private void processIncomePackets() {
        if( this.incomeQueue.isEmpty() ) return;
        IncomeGamePacket packet = this.incomeQueue.peek();
        if( findPlayerById(packet.getPlayerId()).isEmpty() ) return;
        processIncomePacket(packet);
        this.incomeQueue.poll();
    }

    private void processOutcomePackets() {
        if( this.outcomeQueue.isEmpty() ) return;
        OutcomeGamePacket packet = this.outcomeQueue.peek();
        GamePacketCallback callback = this.callbacks.get(packet.getPlayerId());
        if (callback == null) return;
        callback.onMessage(packet.getPacket());
        this.outcomeQueue.poll();
    }

    private void processIncomePacket(IncomeGamePacket packet) {
        try {
            switch (PegasusPacketType.fromInt(packet.getPacket().getType())) {
                case SendChoices -> processChoices(packet.getPlayerId(), PegasusGame.ChooseEntities.parseFrom((byte[]) packet.getPacket().getBody()));
                case SendEmoteOrUI -> processEmoteOrUI(packet.getPlayerId(), PegasusGame.UserUI.parseFrom((byte[]) packet.getPacket().getBody()));
                default -> log.warn("processIncomePacket: {}", packet.getPacket());
            }
        } catch (InvalidProtocolBufferException e) {
            log.error(e.getMessage());
        }
    }

    private void processChoices(final Long id, PegasusGame.ChooseEntities packet) {
        if(this.globalState != GameState.Running || this.runningState != RunningState.INITIAL_DEAL || this.findPlayerById(id).isEmpty()) return;
        // initial_deal state:
        List<Integer> redeal = this.decks.get(id).getHandCards()
                .stream()
                .map(PegasusGame.PowerHistoryEntity.Builder::getEntity)
                .filter(entity -> !packet.getEntitiesList().contains(entity))
                .collect(Collectors.toList());

        PegasusGame.PowerHistory.Builder pw = PegasusGame.PowerHistory.newBuilder();

        pw.addList(PegasusGame.PowerHistoryData.newBuilder()
                .setTagChange(PegasusGame.PowerHistoryTagChange.newBuilder()
                        .setEntity(this.findPlayerById(id).get().getController()+1)
                        .setTag(GameTag.MULLIGAN_STATE.getValue())
                        .setValue(TagMulligan.DEALING.ordinal())
                )
        );

        Queue<Integer> handPoses = new ArrayDeque<>();

        IntStream.range(0, this.decks.get(id).getSize())
                .boxed()
                .filter(c -> redeal.contains(this.decks.get(id).getCard(c).getEntity()))
                .forEach(i -> {
                    PegasusGame.PowerHistoryEntity.Builder b = this.decks.get(id).getCard(i)
                            .addTags(PegasusGame.Tag.newBuilder()
                                    .setName(GameTag.ZONE.getValue())
                                    .setValue(TagZone.DECK.ordinal())
                            );
                    handPoses.add(
                            b.getTagsList()
                                    .stream()
                                    .filter(t -> t.getName() == GameTag.ZONE_POSITION.getValue())
                                    .map(PegasusGame.Tag::getValue)
                                    .findFirst()
                                    .orElse(0)
                    );
                    pw.addList(PegasusGame.PowerHistoryData.newBuilder()
                            .setTagChange(PegasusGame.PowerHistoryTagChange.newBuilder()
                                    .setEntity(this.decks.get(id).getCard(i).getEntity())
                                    .setTag(GameTag.ZONE.getValue())
                                    .setValue(TagZone.DECK.ordinal())
                            )
                    );
                    this.decks.get(id).updateCard(b, i);
                });
        new Random()
                .ints(handPoses.size(), 0, this.decks.get(id).getSize())
                .boxed()
                .forEach(i -> {
                    var l = this.decks.get(id).getCard(i);
                    IntStream.range(0, l.getTagsCount())
                            .boxed()
                            .filter(h -> l.getTags(h).getName() == GameTag.ZONE_POSITION.getValue())
                            .forEach(l::removeTags);
                    PegasusGame.Tag t = PegasusGame.Tag.newBuilder()
                            .setName(GameTag.ZONE_POSITION.getValue())
                            .setValue(handPoses.poll())
                            .build();
                    l.addTags(t);
                    pw.addList(PegasusGame.PowerHistoryData.newBuilder()
                            .setTagChange(PegasusGame.PowerHistoryTagChange.newBuilder()
                                    .setEntity(l.getEntity())
                                    .setTag(GameTag.ZONE_POSITION.getValue())
                                    .setValue(t.getValue())
                            )
                    );
                    pw.addList(PegasusGame.PowerHistoryData.newBuilder()
                            .setTagChange(PegasusGame.PowerHistoryTagChange.newBuilder()
                                    .setEntity(l.getEntity())
                                    .setTag(GameTag.ZONE.getValue())
                                    .setValue(TagZone.HAND.ordinal())
                            )
                    );
                    IntStream.range(0, l.getTagsCount())
                            .boxed()
                            .filter(h -> l.getTags(h).getName() == GameTag.ZONE.getValue())
                            .forEach(h -> l.setTags(h, l.getTags(h).toBuilder().setValue(TagZone.HAND.ordinal())));
                    this.decks.get(id).updateCard(l, i);
        });

        pw.addList(PegasusGame.PowerHistoryData.newBuilder()
                .setTagChange(PegasusGame.PowerHistoryTagChange.newBuilder()
                        .setEntity(this.findPlayerById(id).get().getController()+1)
                        .setTag(GameTag.MULLIGAN_STATE.getValue())
                        .setValue(TagMulligan.WAITING.ordinal())
                )
        );

        pw.addList(PegasusGame.PowerHistoryData.newBuilder()
                .setTagChange(PegasusGame.PowerHistoryTagChange.newBuilder()
                        .setEntity(this.findPlayerById(id).get().getController()+1)
                        .setTag(GameTag.MULLIGAN_STATE.getValue())
                        .setValue(TagMulligan.DONE.ordinal())
                )
        );

        pw.addList(PegasusGame.PowerHistoryData.newBuilder()
                .setTagChange(PegasusGame.PowerHistoryTagChange.newBuilder()
                        .setEntity(1)
                        .setTag(GameTag.NEXT_STEP.getValue())
                        .setValue(TagStep.MAIN_READY.ordinal())
                )
        );

        this.broadcast(new PegasusPacket(19, 0, pw.build().toByteArray()));
       // log.info("Initial_deal choices: player_id={}; packet={}", id, packet);
    }

    private void processEmoteOrUI(final Long id, PegasusGame.UserUI packet) {
        this.players.stream()
                .filter(p -> p.getUserId().longValue() != id.longValue())
                .forEach(p -> this.outcomeQueue.add(
                        new OutcomeGamePacket(
                                p.getUserId(),
                                new PegasusPacket( 15, 0, packet.toBuilder().setPlayerId(id.intValue()).build().toByteArray())
                        )
                    )
                );
    }

    private void updateGameState() {
        // giant switch
        switch (this.globalState) {
            case CreateGame -> createGameEntity();
            case CreateDecks -> createDeckEntities();
            case CreateHeroes -> createPlayerHeroEntity();
        }
    }

    public void updateCallback(Long playerId, GamePacketCallback callback) {
        this.callbacks.put(playerId, callback);
    }

    @Override
    public void run() {
        while(true) {
            processIncomePackets();
            updateGameState();
            processOutcomePackets();
        }
    }

    @Override
    public void createGameEntity() {
        // TODO: rewrite board generation, and send each packet not via broadcast, but via target send.
        log.info("create Game Entity");
        for( RoomPlayerDTO player : this.players ) {
            PegasusGame.PowerHistory powerHistory = PegasusGame.PowerHistory.newBuilder()
                    .addList(PegasusGame.PowerHistoryData.newBuilder()
                            .setCreateGame(PegasusGame.PowerHistoryCreateGame.newBuilder()
                                    .setGameEntity(PegasusGame.Entity.newBuilder()
                                            .setId(1)
                                            .addTags(PegasusGame.Tag.newBuilder()
                                                    .setName(10)
                                                    .setValue(85)
                                            )
                                            .addTags(PegasusGame.Tag.newBuilder()
                                                    .setName(GameTag.CARDTYPE.getValue())
                                                    .setValue(TagCardType.GAME.ordinal())
                                            )
                                            .addTags(PegasusGame.Tag.newBuilder()
                                                    .setName(GameTag.TURN.getValue())
                                                    .setValue(1)
                                            )
                                            .addTags(PegasusGame.Tag.newBuilder()
                                                    .setName(GameTag.STATE.getValue())
                                                    .setValue(TagState.RUNNING.ordinal())
                                            )
                                            .addTags(PegasusGame.Tag.newBuilder()
                                                    .setName(GameTag.ZONE.getValue())
                                                    .setValue(TagZone.PLAY.ordinal())
                                            )
                                            .addTags(PegasusGame.Tag.newBuilder()
                                                    .setName(GameTag.ENTITY_ID.getValue())
                                                    .setValue(1)
                                            )
                                            .addTags(PegasusGame.Tag.newBuilder()
                                                    .setName(GameTag.STEP.getValue())
                                                    .setValue(TagStep.BEGIN_MULLIGAN.ordinal()) // 10
                                            )
                                            .addTags(PegasusGame.Tag.newBuilder()
                                                    .setName(GameTag.NEXT_STEP.getValue())
                                                    .setValue(TagStep.BEGIN_MULLIGAN.ordinal()) // 12
                                            )
                                    )
                                    .addAllPlayers(createPlayersEntity(player))
                            )
                    ).build();

            this.send(player.getUserId(), new PegasusPacket(19, 0, powerHistory.toByteArray()));
        }

        this.nextGlobalState();
    }

    public List<PegasusGame.Player> createPlayersEntity(RoomPlayerDTO player) {
        log.info("create Players entities");
        List<PegasusGame.Player> pls = new ArrayList<>();
        List<RoomPlayerDTO> rel_players = new ArrayList<>(Collections.singletonList(player));
        players.stream().filter(p -> p.getUserId().longValue() != p.getUserId().longValue()).forEach(rel_players::add);
        for( int i = 2, pi = 0; i < 4; i++, pi++ ) {
            final PegasusGame.Entity.Builder entity = PegasusGame.Entity.newBuilder().setId(i == 2 ? 3 : 2);

            if( pi < rel_players.size() ) {
                rel_players.get(pi).setController(i == 2 ? 2 : 1);
                GameDeck gd = new GameDeck();
                userService.getDeckContentForDeckId(rel_players.get(pi).getUserId(), rel_players.get(pi).getDeckId())
                        .stream()
                        .map(d -> card2Entity(d.getAssetId()).get())
                        .forEach(gd::addCard);
                decks.put(rel_players.get(pi).getUserId(), gd);
            }

            if( i == 2 ) {
                entity.addTags(PegasusGame.Tag.newBuilder()
                        .setName(GameTag.FIRST_PLAYER.getValue())
                        .setValue(1)
                );
                entity.addTags(PegasusGame.Tag.newBuilder()
                        .setName(GameTag.CURRENT_PLAYER.getValue())
                        .setValue(1)
                );
                entity.addTags(PegasusGame.Tag.newBuilder()
                        .setName(GameTag.RESOURCES.getValue())
                        .setValue(1)
                );
            }

            entity.addTags(PegasusGame.Tag.newBuilder()
                    .setName(GameTag.HERO_ENTITY.getValue())
                    .setValue(i == 2 ? 66 : 64)
            );

            entity.addTags(PegasusGame.Tag.newBuilder()
                    .setName(GameTag.PLAYSTATE.getValue())
                    .setValue(TagPlayState.PLAYING.ordinal())
            );

            entity.addTags(PegasusGame.Tag.newBuilder()
                    .setName(GameTag.TEAM_ID.getValue())
                    .setValue(i == 2 ? 2 : 1)
            );

            entity.addTags(PegasusGame.Tag.newBuilder()
                    .setName(GameTag.PLAYER_ID.getValue())
                    .setValue(i == 2 ? 2 : 1)
            );

            entity.addTags(PegasusGame.Tag.newBuilder()
                    .setName(GameTag.MAXHANDSIZE.getValue())
                    .setValue(10)
            );

            entity.addTags(PegasusGame.Tag.newBuilder()
                    .setName(GameTag.STARTHANDSIZE.getValue())
                    .setValue(4)
            );

            entity.addTags(PegasusGame.Tag.newBuilder()
                    .setName(GameTag.NUM_TURNS_LEFT.getValue())
                    .setValue(1)
            );

            entity.addTags(PegasusGame.Tag.newBuilder()
                    .setName(GameTag.CARDTYPE.getValue())
                    .setValue(TagCardType.PLAYER.ordinal())
            );

            entity.addTags(PegasusGame.Tag.newBuilder()
                    .setName(GameTag.ZONE.getValue())
                    .setValue(TagZone.PLAY.ordinal())
            );

            entity.addTags(PegasusGame.Tag.newBuilder()
                    .setName(GameTag.TIMEOUT.getValue())
                    .setValue(75)
            );

            entity.addTags(PegasusGame.Tag.newBuilder()
                    .setName(GameTag.MAXRESOURCES.getValue())
                    .setValue(10)
            );

            entity.addTags(PegasusGame.Tag.newBuilder()
                    .setName(GameTag.ENTITY_ID.getValue())
                    .setValue(i == 2 ? 3 : 2)
            );

            entity.addTags(PegasusGame.Tag.newBuilder()
                    .setName(GameTag.CONTROLLER.getValue())
                    .setValue(i == 2 ? 2 : 1)
            );

            entity.addTags(PegasusGame.Tag.newBuilder()
                    .setName(GameTag.MULLIGAN_STATE.getValue())
                    .setValue(TagMulligan.INPUT.ordinal())
            );

            entity.addTags(PegasusGame.Tag.newBuilder()
                    .setName(GameTag.NUM_CARDS_DRAWN_THIS_TURN.getValue())
                    .setValue(i == 2 ? 1 : 4)
            );

            // TODO: make custom cardback selection
            pls.add(
                    PegasusGame.Player.newBuilder()
                        .setId(i)
                        .setGameAccountId(bnets.get(i-2))
                        .setCardBack(pi < rel_players.size() ? userService.getCardBackById(rel_players.get(pi).getUserId()).getBackId() : 0)
                        .setEntity(entity)
                        .build()
            );
        }
        return pls;
    }

    @Override
    public void createDeckEntities() {
        log.info("create Deck Entities");
        this.players.forEach(this::generateDeck);
    }

    private void generateDeck(RoomPlayerDTO p1) {
        PegasusGame.PowerHistory.Builder pw = PegasusGame.PowerHistory.newBuilder();

        List<RoomPlayerDTO> rel_players = new ArrayList<>(Collections.singletonList(p1));
        players.stream().filter(p -> p.getUserId().longValue() != p.getUserId().longValue()).forEach(rel_players::add);

        for( RoomPlayerDTO player : rel_players ) {
            int di = 0;
            for (int i = 0; i < 26; i++, this.entity_id++, di++) {
                PegasusGame.PowerHistoryEntity.Builder b = di < this.decks.get(player.getUserId()).getSize() ? this.decks.get(player.getUserId()).getCard(di) : PegasusGame.PowerHistoryEntity.newBuilder();
                pw.addList(PegasusGame.PowerHistoryData.newBuilder()
                        .setFullEntity(b
                                .setEntity(this.entity_id)
                                .addTags(PegasusGame.Tag.newBuilder()
                                        .setName(GameTag.ENTITY_ID.getValue())
                                        .setValue(this.entity_id)
                                )
                                .addTags(PegasusGame.Tag.newBuilder()
                                        .setName(GameTag.CONTROLLER.getValue())
                                        .setValue(player.getController())
                                )
                                .addTags(PegasusGame.Tag.newBuilder()
                                        .setName(GameTag.ZONE.getValue())
                                        .setValue(TagZone.DECK.ordinal())
                                )
                        )
                );
                this.decks.get(player.getUserId()).updateCard(b, di);
            }

            for (int i = 1; i <= 3; i++, this.entity_id++, di++) {
                PegasusGame.PowerHistoryEntity.Builder b = di < this.decks.get(player.getUserId()).getSize() ? this.decks.get(player.getUserId()).getCard(di) : PegasusGame.PowerHistoryEntity.newBuilder();
                pw.addList(PegasusGame.PowerHistoryData.newBuilder()
                        .setFullEntity(b
                                .setEntity(this.entity_id)
                                .addTags(PegasusGame.Tag.newBuilder()
                                        .setName(GameTag.ENTITY_ID.getValue())
                                        .setValue(this.entity_id)
                                )
                                .addTags(PegasusGame.Tag.newBuilder()
                                        .setName(GameTag.CONTROLLER.getValue())
                                        .setValue(player.getController())
                                )
                                .addTags(PegasusGame.Tag.newBuilder()
                                        .setName(GameTag.ZONE_POSITION.getValue())
                                        .setValue(i)
                                )
                                .addTags(PegasusGame.Tag.newBuilder()
                                        .setName(GameTag.ZONE.getValue())
                                        .setValue(TagZone.HAND.ordinal())
                                )
                        )
                );
                this.decks.get(player.getUserId()).updateCard(b, di);
            }

            // there was a place for hero & hero_power

            pw.addList(PegasusGame.PowerHistoryData.newBuilder()
                    .setFullEntity(card2Entity("CS2_103e2").get()
                            .setEntity(this.entity_id)
                            .addTags(PegasusGame.Tag.newBuilder()
                                    .setName(GameTag.ENTITY_ID.getValue())
                                    .setValue(this.entity_id)
                            )
                            .addTags(PegasusGame.Tag.newBuilder()
                                    .setName(GameTag.CONTROLLER.getValue())
                                    .setValue(player.getController())
                            )
                            .addTags(PegasusGame.Tag.newBuilder()
                                    .setName(GameTag.ZONE.getValue())
                                    .setValue(TagZone.SETASIDE.ordinal())
                            )
                            .addTags(PegasusGame.Tag.newBuilder()
                                    .setName(GameTag.ZONE_POSITION.getValue())
                                    .setValue(0)
                            )
                    )
            );

            this.entity_id++;
        }

        this.send(p1.getUserId(), new PegasusPacket(19, 0, pw.build().toByteArray()));

        this.nextGlobalState();
    }

    @Override
    public void createPlayerHeroEntity() {
        log.info("Player hero entity");
        this.players.forEach(this::generateHeroes);
    }

    private void generateHeroes(RoomPlayerDTO p1) {
        PegasusGame.PowerHistory.Builder pw = PegasusGame.PowerHistory.newBuilder();

        List<RoomPlayerDTO> rel_players = new ArrayList<>(Collections.singletonList(p1));
        players.stream().filter(p -> p.getUserId().longValue() != p.getUserId().longValue()).forEach(rel_players::add);

        for(RoomPlayerDTO player : rel_players) {
            Optional<Deck> deck = userService.getDecksForUser(player.getUserId())
                    .stream()
                    .filter(d -> d.getId().longValue() == player.getDeckId().longValue())
                    .findFirst();

            pw.addList(PegasusGame.PowerHistoryData.newBuilder()
                    .setFullEntity(card2Entity(deck.get().getHero().longValue()).get()
                            .setEntity(this.entity_id)
                            .addTags(PegasusGame.Tag.newBuilder()
                                    .setName(GameTag.ENTITY_ID.getValue())
                                    .setValue(this.entity_id)
                            )
                            .addTags(PegasusGame.Tag.newBuilder()
                                    .setName(GameTag.CONTROLLER.getValue())
                                    .setValue(player.getController())
                            )
                            .addTags(PegasusGame.Tag.newBuilder()
                                    .setName(GameTag.ZONE.getValue())
                                    .setValue(TagZone.PLAY.ordinal())
                            )
                    )
            );
            //this.hero_p2 = this.entity_id;
            this.entity_id++;


            pw.addList(PegasusGame.PowerHistoryData.newBuilder()
                    .setFullEntity(getHeroPowerFromHeroId(deck.get().getHero().longValue()).get()
                            .setEntity(this.entity_id)
                            .addTags(PegasusGame.Tag.newBuilder()
                                    .setName(GameTag.ENTITY_ID.getValue())
                                    .setValue(this.entity_id)
                            )
                            .addTags(PegasusGame.Tag.newBuilder()
                                    .setName(GameTag.CONTROLLER.getValue())
                                    .setValue(player.getController())
                            )
                            .addTags(PegasusGame.Tag.newBuilder()
                                    .setName(GameTag.ZONE.getValue())
                                    .setValue(TagZone.PLAY.ordinal())
                            )
                    )
            );
        }

        pw.addList(PegasusGame.PowerHistoryData.newBuilder()
                .setTagChange(PegasusGame.PowerHistoryTagChange.newBuilder()
                        .setEntity(2)
                        .setTag(GameTag.MULLIGAN_STATE.getValue())
                        .setValue(TagMulligan.INPUT.ordinal())
                )
        );

        pw.addList(PegasusGame.PowerHistoryData.newBuilder()
                .setTagChange(PegasusGame.PowerHistoryTagChange.newBuilder()
                        .setEntity(3)
                        .setTag(GameTag.MULLIGAN_STATE.getValue())
                        .setValue(TagMulligan.INPUT.ordinal())
                )
        );

        PegasusGame.EntityChoices entityChoices = PegasusGame.EntityChoices.newBuilder()
                .setId(1)
                .setChoiceType(1)
                .setCountMin(0)
                .setCountMax(3)
                .addAllEntities(decks.get(rel_players.get(0).getUserId())
                        .getHandCards()
                        .stream()
                        .map(PegasusGame.PowerHistoryEntity.Builder::getEntity)
                        .collect(Collectors.toList())
                )
                .setSource(1)
                .setPlayerId(2)
                .build();

        this.send(p1.getUserId(), new PegasusPacket( 17, 0, entityChoices.toByteArray()));

        this.send(p1.getUserId(), new PegasusPacket(19, 0, pw.build().toByteArray()));

        this.entity_id++;

      /*  PegasusGame.AllOptions allOptions = PegasusGame.AllOptions.newBuilder()
                .setId(1)
                .addOptions(PegasusGame.Option.newBuilder()
                        .setType(END_TURN)
                )
                .addOptions(PegasusGame.Option.newBuilder()
                        .setType(PegasusGame.Option.Type.POWER)
                        .setMainOption(PegasusGame.SubOption.newBuilder()
                                .setId(this.hero_p2)
                                .addTargets(this.hero_p1)
                        )
                )
                .build();

        this.broadcast(new PegasusPacket( 14, 0, allOptions.toByteArray()));*/

        this.nextGlobalState();
    }

    private Optional<RoomPlayerDTO> findPlayerById(Long id) {
        return players.stream().filter(p -> p.getUserId().longValue() == id.longValue()).findFirst();
    }

    private void broadcast(PegasusPacket packet) {
        this.players.forEach(p -> this.outcomeQueue.add(new OutcomeGamePacket(p.getUserId(), packet)));
    }

    private void send(Long playerId, PegasusPacket packet) {
        this.outcomeQueue.add(new OutcomeGamePacket(playerId, packet));
    }

    private void nextGlobalState() {
        this.globalState = GameState.next(this.globalState);
    }

    private Optional<PegasusGame.PowerHistoryEntity.Builder> card2Entity(String cardId) {
        return this.objectConfig.getFormattedCards()
                .getObjects()
                .stream()
                .filter(c -> c.getEntity().getCardId().equals(cardId))
                .map(c ->
                    PegasusGame.PowerHistoryEntity.newBuilder()
                            .setName(c.getEntity().getCardId())
                            .addAllTags(c.getEntity().getTags().stream()
                                    .filter(t -> !t.getType().equals("String"))
                                    .map(t ->
                                            PegasusGame.Tag.newBuilder()
                                                    .setName(t.getEnumID())
                                                    .setValue(t.getValue())
                                                    .build()
                                    )
                                    .collect(Collectors.toList()))
                )
                .findFirst();
    }

    private Optional<PegasusGame.PowerHistoryEntity.Builder> card2Entity(Long cardId) {
        return this.objectConfig.getFormattedCards()
                .getObjects()
                .stream()
                .filter(o -> o.getDbfCard().findField("ID").isPresent() && Long.parseLong(o.getDbfCard().findField("ID").get().getVal()) == cardId)
                .map(c ->
                        PegasusGame.PowerHistoryEntity.newBuilder()
                                .setName(c.getEntity().getCardId())
                                .addAllTags(c.getEntity().getTags().stream()
                                        .filter(t -> !t.getType().equals("String"))
                                        .map(t ->
                                                PegasusGame.Tag.newBuilder()
                                                        .setName(t.getEnumID())
                                                        .setValue(t.getValue())
                                                        .build()
                                        )
                                        .collect(Collectors.toList()))
                )
                .findFirst();
    }

    private Optional<PegasusGame.PowerHistoryEntity.Builder> getHeroPowerFromHeroId(Long cardId) {
        return card2Entity(Long.parseLong(this.objectConfig.getFormattedCards()
                .getObjects()
                .stream()
                .filter(o -> o.getDbfCard().findField("ID").isPresent() && Long.parseLong(o.getDbfCard().findField("ID").get().getVal()) == cardId)
                .findFirst()
                .get()
                .getDbfCard()
                .findField("HERO_POWER_ID")
                .get()
                .getVal()));
    }
}