/*
 * Copyright (C) 2016-2022 ActionTech.
 * based on code by MyCATCopyrightHolder Copyright (c) 2013, OpenCloudDB/MyCAT.
 * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher.
 */
package com.actiontech.dble.net.mysql;

import com.actiontech.dble.net.connection.AbstractConnection;
import com.actiontech.dble.net.service.AbstractService;
import com.actiontech.dble.net.service.ResultFlag;
import com.actiontech.dble.net.service.WriteFlag;
import com.actiontech.dble.net.service.WriteFlags;
import com.actiontech.dble.services.mysqlsharding.MySQLResponseService;

import java.nio.ByteBuffer;
import java.util.EnumSet;
import java.util.HashMap;

/**
 * @author mycat
 */
public abstract class MySQLPacket {
    /**
     * none, this is an internal thread state
     */
    public static final byte COM_SLEEP = 0;

    /**
     * mysql_close
     */
    public static final byte COM_QUIT = 1;

    /**
     * mysql_select_db
     */
    public static final byte COM_INIT_DB = 2;

    /**
     * mysql_real_query
     */
    public static final byte COM_QUERY = 3;

    /**
     * mysql_list_fields
     */
    public static final byte COM_FIELD_LIST = 4;

    /**
     * mysql_create_db (deprecated)
     */
    public static final byte COM_CREATE_DB = 5;

    /**
     * mysql_drop_db (deprecated)
     */
    public static final byte COM_DROP_DB = 6;

    /**
     * mysql_refresh
     */
    public static final byte COM_REFRESH = 7;

    /**
     * mysql_shutdown
     */
    public static final byte COM_SHUTDOWN = 8;

    /**
     * mysql_stat
     */
    public static final byte COM_STATISTICS = 9;

    /**
     * mysql_list_processes
     */
    public static final byte COM_PROCESS_INFO = 10;

    /**
     * none, this is an internal thread state
     */
    public static final byte COM_CONNECT = 11;

    /**
     * mysql_kill
     */
    public static final byte COM_PROCESS_KILL = 12;

    /**
     * mysql_dump_debug_info
     */
    public static final byte COM_DEBUG = 13;

    /**
     * mysql_ping
     */
    public static final byte COM_PING = 14;

    /**
     * none, this is an internal thread state
     */
    public static final byte COM_TIME = 15;

    /**
     * none, this is an internal thread state
     */
    public static final byte COM_DELAYED_INSERT = 16;

    /**
     * mysql_change_user
     */
    public static final byte COM_CHANGE_USER = 17;

    /**
     * used by slave server mysqlbinlog
     */
    public static final byte COM_BINLOG_DUMP = 18;

    /**
     * used by slave server to get master table
     */
    public static final byte COM_TABLE_DUMP = 19;

    /**
     * used by slave to log connection to master
     */
    public static final byte COM_CONNECT_OUT = 20;

    /**
     * used by slave to register to master
     */
    public static final byte COM_REGISTER_SLAVE = 21;

    /**
     * mysql_stmt_prepare
     */
    public static final byte COM_STMT_PREPARE = 22;

    /**
     * mysql_stmt_execute
     */
    public static final byte COM_STMT_EXECUTE = 23;

    /**
     * mysql_stmt_send_long_data
     */
    public static final byte COM_STMT_SEND_LONG_DATA = 24;

    /**
     * mysql_stmt_close
     */
    public static final byte COM_STMT_CLOSE = 25;

    /**
     * mysql_stmt_reset
     */
    public static final byte COM_STMT_RESET = 26;

    /**
     * mysql_set_server_option
     */
    public static final byte COM_SET_OPTION = 27;

    /**
     * mysql_stmt_fetch
     */
    public static final byte COM_STMT_FETCH = 28;
    /**
     * mysql-reset-connection
     */
    public static final byte COM_RESET_CONNECTION = 31;

    /**
     * heartbeat
     */
    public static final byte COM_HEARTBEAT = 64;

    //HEADER_SIZE
    public static final int PACKET_HEADER_SIZE = 4;
    //2^24-1
    public static final int MAX_PACKET_SIZE = 16777215;

    public static final int MAX_EOF_SIZE = 9;


    protected int packetLength;
    protected byte packetId;

    /**
     * writeDirectly to buffer ,if writeSocketIfFull writeDirectly the buffer data to FrontendConnection
     */
    public ByteBuffer write(ByteBuffer buffer, AbstractService service, boolean writeSocketIfFull) {
        throw new UnsupportedOperationException();
    }

    /**
     * writeDirectly to backend connection
     */
    public void write(MySQLResponseService service) {
        this.write(service.getConnection());
    }

    /**
     * writeDirectly to a net connection
     */
    public final void write(AbstractConnection connection) {
        connection.getService().write(this);
    }


    public void bufferWrite(AbstractConnection connection) {
        ByteBuffer buffer = connection.allocate();
        buffer = this.write(buffer, connection.getService(), true);
        connection.getService().writeDirectly(buffer, getLastWriteFlag(), getResultFlag());
    }


    public void bufferWrite(AbstractService service) {
        ByteBuffer buffer = service.allocate();
        buffer = this.write(buffer, service, true);
        service.writeDirectly(buffer, getLastWriteFlag());
    }


    public final EnumSet<WriteFlag> getLastWriteFlag() {
        if (isEndOfSession()) {
            return WriteFlags.SESSION_END;
        } else if (isEndOfQuery()) {
            return WriteFlags.QUERY_END;
        } else {
            return WriteFlags.PART;
        }
    }

    public ResultFlag getResultFlag() {
        return ResultFlag.OTHER;
    }

    /**
     * calcPacketSize,not contains header size
     */
    public abstract int calcPacketSize();

    /**
     * getPacketInfo
     */
    protected abstract String getPacketInfo();


    @Override
    public String toString() {
        return getPacketInfo() + "{length=" + packetLength + ",id=" +
                packetId + '}';
    }

    public void setPacketLength(int packetLength) {
        this.packetLength = packetLength;
    }

    public byte getPacketId() {
        return packetId;
    }

    public void setPacketId(int packetID) {
        this.packetId = (byte) packetID;
    }

    public abstract boolean isEndOfQuery();

    public boolean isEndOfSession() {
        return false;
    }

    public void markMoreResultsExists() {
    }

    public static final HashMap<Byte, String> TO_STRING = new HashMap(18);

    static {
        // see: mysql-5.7.20/sql/sql_parse.cc
        TO_STRING.put(COM_SLEEP, "Sleep");
        TO_STRING.put(COM_QUIT, "Quit");
        TO_STRING.put(COM_INIT_DB, "Init DB");
        TO_STRING.put(COM_QUERY, "Query");
        TO_STRING.put(COM_FIELD_LIST, "Field List");
        TO_STRING.put(COM_CONNECT, "Connect");
        TO_STRING.put(COM_PROCESS_KILL, "Kill");
        TO_STRING.put(COM_PING, "Ping");
        TO_STRING.put(COM_DELAYED_INSERT, "Delayed insert");
        TO_STRING.put(COM_CHANGE_USER, "Change user");
        TO_STRING.put(COM_REGISTER_SLAVE, "Register Slave");
        TO_STRING.put(COM_STMT_PREPARE, "Prepare");
        TO_STRING.put(COM_STMT_EXECUTE, "Execute");
        TO_STRING.put(COM_STMT_SEND_LONG_DATA, "Long Data");
        TO_STRING.put(COM_STMT_CLOSE, "Close stmt");
        TO_STRING.put(COM_STMT_RESET, "Reset stmt");
        TO_STRING.put(COM_SET_OPTION, "Set option");
        TO_STRING.put(COM_RESET_CONNECTION, "Reset Connection");
        TO_STRING.put(COM_HEARTBEAT, "Heartbeat");
    }
}
