package com.csforge.sstable;

import org.apache.cassandra.config.CFMetaData;
import org.apache.cassandra.config.ColumnDefinition;
import org.apache.cassandra.cql3.ColumnSpecification;
import org.apache.cassandra.cql3.ResultSet;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.commons.lang3.StringUtils;

import java.io.PrintStream;
import java.nio.ByteBuffer;
import java.util.EnumSet;
import java.util.List;

public class TableTransformer {

    public static final String ANSI_RESET = "\u001B[0m";
    public static final String ANSI_BLACK = "\u001B[30m";
    public static final String ANSI_RED = "\u001B[31m";
    public static final String ANSI_GREEN = "\u001B[32m";
    public static final String ANSI_YELLOW = "\u001B[33m";
    public static final String ANSI_BLUE = "\u001B[34m";
    public static final String ANSI_PURPLE = "\u001B[35m";
    public static final String ANSI_CYAN = "\u001B[36m";
    public static final String ANSI_WHITE = "\u001B[37m";

    public static String colValue(ResultSet results, List<ByteBuffer> row, int i) throws Exception {
        ByteBuffer v = row.get(i).duplicate();
        String ret = "null";
        if (v != null) {
            EnumSet<ResultSet.Flag> flags = (EnumSet<ResultSet.Flag>) CassandraUtils.readPrivate(results.metadata, "flags");
            if (flags.contains(ResultSet.Flag.NO_METADATA)) {
                ret = "0x" + ByteBufferUtil.bytesToHex(v);
            } else if (results.metadata.names.get(i).type.isCollection()) {
                ret = results.metadata.names.get(i).type.getSerializer().deserialize(v).toString();
            } else {
                ret = results.metadata.names.get(i).type.getString(v);
            }
        }
        return ret;
    }

    private static void printLine(char left, char mid, char right, char cross, int[] padding, PrintStream out) {
        out.print(" " + left);
        for (int i = 0; i < padding.length; i++) {
            out.print(StringUtils.repeat(mid, padding[i]) + ((i == (padding.length - 1)) ? right : cross));
        }
    }

    public static void dumpResults(CFMetaData cfm, ResultSet results, PrintStream out) throws Exception {
        if(results.rows.isEmpty()) return; // empty
        // find spacing
        int[] padding = new int[results.rows.get(0).size()];
        for (int i = 0; i < results.rows.get(0).size(); i++) {
            padding[i] = 3 + results.metadata.names.get(i).name.toString().length();
        }
        for (List<ByteBuffer> row : results.rows) {
            for (int i = 0; i < row.size(); i++) {
                padding[i] = Math.max(padding[i], colValue(results, row, i).length());
            }
        }

        // headers
        out.print(ANSI_WHITE);
        printLine('┌', '─', '┐', '┬', padding, out);
        out.println();
        out.print(" ");
        for (int i = 0; i < results.metadata.names.size(); i++) {
            ColumnSpecification spec = results.metadata.names.get(i);
            out.print(ANSI_WHITE + "│" + ANSI_RESET);

            ColumnDefinition def = cfm.getColumnDefinition(spec.name);
            if (def != null && def.isPartitionKey()) {
                out.print(ANSI_RED);
            } else if (def != null && def.isClusteringColumn()) {
                out.print(ANSI_CYAN);
            }
            out.print(String.format("%-" + padding[i] + "s", spec.name));
            out.print(ANSI_RESET);
        }
        out.println(ANSI_WHITE + "│");
        printLine('╞', '═', '╡', '╪', padding, out);
        out.println(ANSI_RESET);
        out.print(" ");

        // data
        for (int r = 0; r < results.rows.size(); r++) {
            if (r > 0) {
                out.print(" ");
            }
            List<ByteBuffer> row = results.rows.get(r);
            for (int i = 0; i < row.size(); i++) {
                out.print(ANSI_WHITE + "│" + ANSI_RESET);
                out.print(String.format("%-" + padding[i] + "s", colValue(results, row, i)));
            }

            out.println(ANSI_WHITE + "│");
            if (r == results.rows.size() - 1) {
                printLine('└', '─', '┘', '┴', padding, out);
            } else {
                printLine('├', '─', '┤', '┼', padding, out);
            }
            out.println(ANSI_RESET);
        }
        out.flush();
    }
}