package com.alterdekim.hearthhack.component;

import com.alterdekim.PegasusGame;
import com.alterdekim.hearthhack.util.PegasusPacket;
import com.alterdekim.hearthhack.util.Util;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

@Slf4j
@RequiredArgsConstructor
public class GameConnection extends Thread {
    private final Socket client;
    private OutputStream outToClient;

    private Long id;

    private boolean isLoggedIn = false;

    public void stopListeningAndDisconnect() {
        try {
            this.client.close();
            this.interrupt();
        } catch (IOException e) {
            log.error(e.getMessage());
        }
    }

    private void processPacket( PegasusPacket packet ) throws Exception {
        log.info("PegasusPacket: type={}, context={}, size={}", packet.getType(), packet.getContext(), packet.getSize());
        if( !isLoggedIn && packet.getType() != 168 ) stopListeningAndDisconnect();
        switch (packet.getType()) {
            case 168:
                log.info("Handshake: {}", PegasusGame.Handshake.parseFrom((byte[]) packet.getBody()));
                // game_handle: gameId
                // client_handle: userId
                // password: roomPassword
                PegasusGame.GameSetup setup = PegasusGame.GameSetup.newBuilder()
                        .setBoard(2)
                        .setMaxFriendlyMinionsPerPlayer(7)
                        .setMaxSecretsPerPlayer(5)
                        .setKeepAliveFrequencySeconds(30)
                        .setDisconnectWhenStuckSeconds(500)
                        .build();
                PegasusPacket result = new PegasusPacket(16, 0, setup);
                this.send(result);
                this.isLoggedIn = true;
                break;
            case 1:
                // GetGameState
                PegasusGame.GetGameState state = PegasusGame.GetGameState.parseFrom((byte[]) packet.getBody());
                log.info("GetGameState: {}", state);
                this.send(new PegasusPacket( 19, 0, Util.hexStringToByteArray(" ")));

                this.send(new PegasusPacket( 19, 0, Util.hexStringToByteArray("0A0822060801100A18550A092207080110CA0118010A0822060801101418010A092207080110CC0118020A0822060801103118010A092207080110C60118040A0822060801103518010A0822060801101318040A0822060802101B18400A0822060802101118010A0822060802103118010A0822060802101F18010A0822060802101E18010A0822060802101C180A0A0822060802101D18040A092207080210900218010A092207080210CA0118020A08220608021007184B0A092207080210B001180A0A0922070802108F0318040A0822060802103518020A0822060802103218010A092207080210B10218010A0822060803101818010A0822060803101B18420A0822060803101118010A0822060803101718010A0822060803101F18020A0822060803101E18020A0822060803101C180A0A0822060803101D18040A092207080310900218010A092207080310CA0118020A0822060803103118010A08220608031007184B0A092207080310B001180A0A0922070803108F0318030A0822060803103518030A0822060803103218020A133211080510FFFFFFFFFFFFFFFFFF0118012000")));

                this.send(new PegasusPacket( 17, 0, Util.hexStringToByteArray("0801100120002803320336262538014002")));

                this.send(new PegasusPacket( 17, 0, Util.hexStringToByteArray("08011001200028053205441A15110B38014001")));
                this.send(new PegasusPacket( 19, 0, Util.hexStringToByteArray("0A092207080310B1021801")));

                this.send(new PegasusPacket( 15, 0, Util.hexStringToByteArray("0A0A08001000181A2000280010001801")));
                this.send(new PegasusPacket( 15, 0, Util.hexStringToByteArray("0A0A0800100018152000280010001801")));
                this.send(new PegasusPacket( 15, 0, Util.hexStringToByteArray("0A0A08001000181A2000280010001801")));
                this.send(new PegasusPacket( 15, 0, Util.hexStringToByteArray("0A0A0800100018112000280010001801")));
                this.send(new PegasusPacket( 15, 0, Util.hexStringToByteArray("0A0A08001000180B2000280010001801")));
                this.send(new PegasusPacket( 15, 0, Util.hexStringToByteArray("0A0A0800100018112000280010001801")));
                this.send(new PegasusPacket( 15, 0, Util.hexStringToByteArray("0A0A0800100018002000280010001801")));
                this.send(new PegasusPacket( 19, 0, Util.hexStringToByteArray("0A092207080310B10218020A0A320808051006180320000A092207080310B10218030A023A000A0A320808051007180320000A092207080310B10218040A023A00")));
                this.send(new PegasusPacket( 19, 0, Util.hexStringToByteArray("0A092207080210B10218020A0A320808051006180220000A092207080210B10218030A023A000A0A320808051007180220000A092207080210B10218040A092207080110C60118060A023A00")));
                this.send(new PegasusPacket( 19, 0, Util.hexStringToByteArray("0A0822060801101318060A0A320808051001180320000A0822060803101A18010A0922070803108F0318000A092207080110C60118110A023A000A0822060801101318110A0A320808051008180320000A092207080110C60118090A023A000A0822060801101318090A0A320808051000180320000A441242083C12074756475F3031301A0508C10210011A0508B701100D1A0508CA0210001A0508CB0210001A04083010001A0508C70110061A0508CA0110051A0508CB0110010A082206083C103118030A092207083C10870218040A0922070803108F0318010A092207080110C601180A0A023A000A08220608011013180A0A0A320808051002180320000A092207080110C601180C0A023A00")));
                this.send(new PegasusPacket( 14, 0, Util.hexStringToByteArray("080112020802120A0803120608431A024042120608031202083C12060803120208361206080312020826")));
                this.send(new PegasusPacket( 15, 0, Util.hexStringToByteArray("0A0A0800100018402000280010001801")));
                this.send(new PegasusPacket( 15, 0, Util.hexStringToByteArray("0A0A0800100018002000280010001801")));
                this.send(new PegasusPacket( 19, 0, Util.hexStringToByteArray("0A0A320808071000182620000A0822060826102B18010A092207082510870218020A092207083610870218010A092207083C10870218030A133211080310FFFFFFFFFFFFFFFFFF01180020000A023A000A3E0A3C084512094353325F31303365321A04083510451A04083210021A0508CB0210001A0508CA0210001A0508C701100A1A0508CA0110061A0508B70110020A0822060845102818260A092207084510870218000A0822060845103118060A0822060826103118010A092207082610870218010A0822060826102B18010A023A00")));
                this.send(new PegasusPacket( 14, 0, Util.hexStringToByteArray("080112020802120B0803120708431A03404226120608031202083C1206080312020836")));
                break;
            case 115:
                pong();
                break;
            default:
                break;
        }
    }

    private void pong() throws Exception {
        this.send(new PegasusPacket(116, 0, PegasusGame.Pong.newBuilder().build()));
    }

    public void send(PegasusPacket pp) throws Exception {
        this.outToClient.write(pp.Encode());
        this.outToClient.flush();
    }

    public void sendRaw(byte[] data) throws Exception {
        this.outToClient.write(data);
        this.outToClient.flush();
    }

    @Override
    public void run() {
        try {
            InputStream is = client.getInputStream();
            outToClient = client.getOutputStream();
            while (true) {
                try {
                    int count;
                    byte[] buffer = new byte[4096]; // or 4096, or more
                    ByteArrayOutputStream baos = new ByteArrayOutputStream();
                    while ((count = is.read(buffer)) > 0) {
                        baos.write(buffer, 0, count);
                        byte[] r = baos.toByteArray();
                        PegasusPacket pp = new PegasusPacket();
                        int offset = 0;
                        while( offset < r.length ) {
                            offset += pp.Decode(r, offset, r.length);
                            processPacket(pp);
                        }
                        baos.reset();
                    }
                    baos.close();
                } catch (Exception e) {
                    log.error(e.getMessage());
                    break;
                }
            }
        } catch (Exception e) {
            log.error(e.getMessage());
        }
    }
}
