/*
 * Copyright (C) 2016-2022 ActionTech.
 * License: http://www.gnu.org/licenses/gpl.html GPL version 2 or higher.
 */

package com.actiontech.dble.services.manager.response;

import com.actiontech.dble.backend.mysql.PacketUtil;
import com.actiontech.dble.config.ErrorCode;
import com.actiontech.dble.config.Fields;
import com.actiontech.dble.net.mysql.*;
import com.actiontech.dble.server.response.ShowTablesStmtInfo;
import com.actiontech.dble.services.manager.ManagerService;
import com.actiontech.dble.services.manager.handler.PackageBufINf;
import com.actiontech.dble.services.manager.information.ManagerSchemaInfo;
import com.actiontech.dble.util.StringUtil;

import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Pattern;

public final class ShowTables {
    private ShowTables() {
    }

    public static void execute(ManagerService service, String stmt) {
        ShowTablesStmtInfo info;
        try {
            info = new ShowTablesStmtInfo(stmt);
        } catch (Exception e) {
            service.writeErrMessage(ErrorCode.ER_PARSE_ERROR, e.toString());
            return;
        }
        if (info.isFull() || info.isAll()) {
            service.writeErrMessage("42000", "not allow All clause in statement", ErrorCode.ER_PARSE_ERROR);
            return;
        }
        if (info.getWhere() != null) {
            service.writeErrMessage("42000", "only allow LIKE clause in statement", ErrorCode.ER_PARSE_ERROR);
            return;
        }
        String showSchema = info.getSchema();
        if (showSchema != null) {
            showSchema = showSchema.toLowerCase();
        }
        String cSchema = showSchema == null ? service.getSchema() : showSchema;
        if (cSchema == null) {
            service.writeErrMessage("3D000", "No database selected", ErrorCode.ER_NO_DB_ERROR);
            return;
        }
        if (!cSchema.equals(ManagerSchemaInfo.SCHEMA_NAME)) {
            service.writeErrMessage("42000", "Unknown database '" + cSchema + "'", ErrorCode.ER_BAD_DB_ERROR);
            return;
        }
        responseDirect(service, cSchema, info);
    }

    private static void responseDirect(ManagerService managerService, String cSchema, ShowTablesStmtInfo info) {
        ByteBuffer buffer = managerService.allocate();
        PackageBufINf bufInf;
        String schemaColumn = cSchema;
        if (info.getLike() != null) {
            schemaColumn = schemaColumn + " (" + info.getLike() + ")";
        }

        bufInf = writeTablesHeaderAndRows(buffer, managerService, getTableSet(info), schemaColumn);

        writeRowEof(bufInf.getBuffer(), managerService, bufInf.getPacketId());
    }

    private static PackageBufINf writeTablesHeaderAndRows(ByteBuffer buffer, ManagerService service, List<String> tableNames, String cSchema) {
        int fieldCount = 1;
        ResultSetHeaderPacket header = PacketUtil.getHeader(fieldCount);
        FieldPacket[] fields = new FieldPacket[fieldCount];
        int i = 0;
        byte packetId = 0;
        header.setPacketId(++packetId);
        fields[i] = PacketUtil.getField("Tables_in_" + cSchema, Fields.FIELD_TYPE_VAR_STRING);
        fields[i].setPacketId(++packetId);

        EOFPacket eof = new EOFPacket();
        eof.setPacketId(++packetId);
        // write header
        buffer = header.write(buffer, service, true);
        // write fields
        for (FieldPacket field : fields) {
            buffer = field.write(buffer, service, true);
        }
        // write eof
        eof.write(buffer, service, true);
        for (String name : tableNames) {
            RowDataPacket row = new RowDataPacket(fieldCount);
            row.add(StringUtil.encode(name, service.getCharset().getResults()));
            row.setPacketId(++packetId);
            buffer = row.write(buffer, service, true);
        }
        PackageBufINf packBuffInfo = new PackageBufINf();
        packBuffInfo.setBuffer(buffer);
        packBuffInfo.setPacketId(packetId);
        return packBuffInfo;
    }

    private static void writeRowEof(ByteBuffer buffer, ManagerService service, byte packetId) {
        // write last eof
        EOFRowPacket lastEof = new EOFRowPacket();
        lastEof.setPacketId(++packetId);
        lastEof.write(buffer, service);
    }

    private static List<String> getTableSet(ShowTablesStmtInfo info) {
        List<String> tableNames = new ArrayList<>();
        Pattern pattern = null;
        if (null != info.getLike()) {
            String p = "^" + info.getLike().replaceAll("%", ".*");
            pattern = Pattern.compile(p, Pattern.CASE_INSENSITIVE);
        }
        for (String tbName : ManagerSchemaInfo.getInstance().getTables().keySet()) {
            if ((pattern == null || pattern.matcher(tbName).matches())) {
                tableNames.add(tbName);
            }
        }
        for (String viewName : ManagerSchemaInfo.getInstance().getViews().keySet()) {
            if ((pattern == null || pattern.matcher(viewName).matches())) {
                tableNames.add(viewName);
            }
        }
        return tableNames;
    }

}
