/*
 * Copyright 2015 Kakao Corporation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.kakao.hbase.manager.command;

import com.kakao.hbase.common.Args;
import com.kakao.hbase.common.Constant;
import com.kakao.hbase.specific.CommandAdapter;
import com.kakao.hbase.stat.load.TableInfo;
import org.apache.commons.codec.DecoderException;
import org.apache.hadoop.hbase.HRegionInfo;
import org.apache.hadoop.hbase.client.HBaseAdmin;
import org.apache.hadoop.hbase.util.Bytes;

import java.io.PrintWriter;
import java.util.Set;

@SuppressWarnings("unused")
public class ExportKeys implements Command {
    static final String DELIMITER = "-";
    private final HBaseAdmin admin;
    private final Args args;
    private final String outputFileName;
    private final int exportThreshold;

    public ExportKeys(HBaseAdmin admin, Args args) {
        this.admin = admin;
        this.args = args;

        if (args.getTableName().equals(Args.ALL_TABLES))
            throw new IllegalArgumentException(Args.INVALID_ARGUMENTS);

        if (args.getOptionSet().nonOptionArguments().size() != 3)
            throw new IllegalArgumentException(Args.INVALID_ARGUMENTS);
        outputFileName = (String) args.getOptionSet().nonOptionArguments().get(2);

        if (args.has(Args.OPTION_OPTIMIZE)) {
            exportThreshold = parseExportThreshold();
            System.out.println("exporting threshold: " + exportThreshold + " MB");
        } else {
            exportThreshold = 0;
            System.out.println("exporting threshold is not set");
        }
    }

    public static String usage() {
        return "Export startkeys and endkeys of regions of a table in hexstr format.\n"
                + "usage: " + ExportKeys.class.getSimpleName().toLowerCase()
                + " <zookeeper quorum> <table regex> <output file> [options]\n"
                + "  options:" + "\n"
                + "    --optimize=<threshold(MB)> : skip exporting keys of small regions below the threshold" + "\n"
                + Args.commonUsage();
    }

    @Override
    public void run() throws Exception {
        try (PrintWriter writer = new PrintWriter(outputFileName, Constant.CHARSET.name())) {
            writer.print("");

            TableInfo tableInfo = new TableInfo(admin, args.getTableName(), args);
            tableInfo.refresh();
            Set<HRegionInfo> regions = tableInfo.getRegionInfoSet();

            int exported = 0, storeFileSizeMBSum = 0;
            for (HRegionInfo regionInfo : regions) {
                System.out.println(regionInfo.toString());

                if (args.getOptionSet().has(Args.OPTION_OPTIMIZE)) {
                    storeFileSizeMBSum += tableInfo.getRegionLoad(regionInfo).getStorefileSizeMB();
                    if (storeFileSizeMBSum < exportThreshold) {
                        continue;
                    }
                }
                if (export(writer, regionInfo)) exported++;
                storeFileSizeMBSum = 0;
            }

            System.out.println("\n" + regions.size() + " regions are scanned.");
            System.out.println(exported + " startkeys are exported.");
            System.out.println("\n" + outputFileName + " file is successfully created.");
        }
    }

    private int parseExportThreshold() {
        String optionValue = (String) args.getOptionSet().valueOf(Args.OPTION_OPTIMIZE);
        if (optionValue.toUpperCase().endsWith("G")) {
            return (int) (Double.valueOf(optionValue.substring(0, optionValue.length() - 1)) * 1024);
        } else if (optionValue.toUpperCase().endsWith("GB")) {
            return (int) (Double.valueOf(optionValue.substring(0, optionValue.length() - 2)) * 1024);
        } else {
            return Double.valueOf(optionValue).intValue();
        }
    }

    private boolean export(PrintWriter writer, HRegionInfo regionInfo) throws DecoderException {
        String tableName = CommandAdapter.getTableName(regionInfo);
        String startKeyHexStr = Bytes.toStringBinary(regionInfo.getStartKey());
        String endKeyHexStr = Bytes.toStringBinary(regionInfo.getEndKey());

        if (startKeyHexStr.length() > 0 || endKeyHexStr.length() > 0) {
            writer.println(tableName + DELIMITER + " " + startKeyHexStr + DELIMITER + " " + endKeyHexStr);
            return true;
        } else {
            return false;
        }
    }
}