package com.netflix.aegisthus.io.writable;

import com.google.common.collect.ComparisonChain;
import com.netflix.Aegisthus;
import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.db.marshal.TypeParser;
import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.cassandra.exceptions.SyntaxException;
import org.apache.hadoop.conf.Configurable;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.io.WritableComparable;
import org.apache.hadoop.io.WritableComparator;

import java.nio.ByteBuffer;
import java.util.Comparator;

public class AegisthusKeySortingComparator extends WritableComparator implements Configurable {
    private AbstractType<ByteBuffer> columnNameConverter;
    private Configuration conf;
    private boolean legacyColumnNameFormatting;
    private boolean sortColumnsByName;

    private AegisthusKey key1 = new AegisthusKey();
    private AegisthusKey key2 = new AegisthusKey();

    public AegisthusKeySortingComparator() {
        super(AegisthusKey.class, false);
    }

    public static String legacyColumnNameFormat(String columnName) {
        return columnName.replaceAll("[\\s\\p{Cntrl}]", " ").replace("\\", "\\\\").replace("\"", "\\\"");
    }

    @Override
    public int compare(byte[] b1, int s1, int l1, byte[] b2, int s2, int l2) {
        Comparator<ByteBuffer> nameComparator = columnNameConverter;
        if (sortColumnsByName) {
            nameComparator = new Comparator<ByteBuffer>() {
                @Override
                public int compare(ByteBuffer o1, ByteBuffer o2) {
                    if (o1 == null || o2 == null) {
                        return ComparisonChain.start().compare(o1, o2).result();
                    }

                    String c1Name = columnNameConverter.getString(o1);
                    String c2Name = columnNameConverter.getString(o2);
                    if (legacyColumnNameFormatting) {
                        c1Name = legacyColumnNameFormat(c1Name);
                        c2Name = legacyColumnNameFormat(c2Name);
                    }

                    return c1Name.compareTo(c2Name);
                }
            };
        }

        key1.readFields(b1, s1, l1);
        key2.readFields(b2, s2, l2);

        return key1.compareTo(key2, nameComparator);
    }

    @Override
    public Configuration getConf() {
        return conf;
    }

    @Override
    public void setConf(Configuration conf) {
        this.conf = conf;
        String columnType = conf.get(Aegisthus.Feature.CONF_COLUMNTYPE, "BytesType");
        legacyColumnNameFormatting = conf.getBoolean(Aegisthus.Feature.CONF_LEGACY_COLUMN_NAME_FORMATTING, false);
        sortColumnsByName = conf.getBoolean(Aegisthus.Feature.CONF_SORT_COLUMNS_BY_NAME, false);
        try {
            //noinspection unchecked
            columnNameConverter = (AbstractType<ByteBuffer>) TypeParser.parse(columnType);
        } catch (SyntaxException | ConfigurationException e) {
            throw new RuntimeException(e);
        }
    }
}