package de.nbscloud.files;

import de.nbscloud.files.config.FilesConfig;
import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.MathContext;
import java.math.RoundingMode;

@Component
public class FilesFormatter {
    enum Units {
        P(FileUtils.ONE_KB * FileUtils.ONE_KB * FileUtils.ONE_KB * FileUtils.ONE_KB * FileUtils.ONE_KB, null),
        T(FileUtils.ONE_KB * FileUtils.ONE_KB * FileUtils.ONE_KB * FileUtils.ONE_KB, P),
        G(FileUtils.ONE_KB * FileUtils.ONE_KB * FileUtils.ONE_KB, T),
        M(FileUtils.ONE_KB * FileUtils.ONE_KB, G),
        K(FileUtils.ONE_KB, M);

        final long minByteSize;
        final Units immediatlyHigherUnit;

        private Units(long minByteSize, Units immediatlyHigherUnit) {
            this.minByteSize = minByteSize;
            this.immediatlyHigherUnit = immediatlyHigherUnit;
        }
    }

    private static final MathContext MC_BYTE_TO_HUMAN_LESS_100 = new MathContext(2, RoundingMode.UP);
    private static final MathContext MC_BYTE_TO_HUMAN_LESS_1000 = new MathContext(3, RoundingMode.UP);
    private static final MathContext MC_BYTE_TO_HUMAN_MORE_1000 = new MathContext(4, RoundingMode.UP);

    @Autowired
    private FilesConfig filesConfig;

    public String truncateFilename(String name) {
        String filename = name;
        final int lastDotIndex = filename.lastIndexOf(".");
        final int configMaxLength = this.filesConfig.getTruncateFileNameChars() - 5; // -5 for ellipses " ... "
        final int filenameLength = filename.length();

        if (lastDotIndex <= 0) {
            if ((configMaxLength + 5) >= filenameLength) {
                return filename;
            } else {
                return filename.substring(0, configMaxLength) + "...";
            }
        }

        final String suffix = filename.substring(lastDotIndex, filenameLength);

        if ((configMaxLength + 5) >= filenameLength) {
            return filename;
        } else {
            filename = filename.replace(suffix, "");

            return filename.substring(0, configMaxLength - suffix.length()) + " ... " + suffix;
        }
    }

    public boolean needsTruncate(String name) {
        return !name.equals(truncateFilename(name));
    }

    // Taken from: https://issues.apache.org/jira/secure/attachment/12670724/byteCountToHumanReadableGnu.patch
    public String formatSize(final long size) {
        String humanReadableOutput = null;
        for (Units u : Units.values()) {
            if (size >= u.minByteSize) {
                double numOfUnits = (double) size / (double) u.minByteSize;

                // if there is no decimals and the number is less than ten
                // then we want to format as X.0; numOfUnits.toString() does it for us
                if (Math.ceil(numOfUnits) == numOfUnits && numOfUnits < 10d) {
                    humanReadableOutput = numOfUnits + u.toString();
                } else {
                    // we need to do some rounding
                    BigDecimal bdNumbOfUnits = new BigDecimal(numOfUnits);

                    if (numOfUnits < 100d) {
                        bdNumbOfUnits = bdNumbOfUnits.round(MC_BYTE_TO_HUMAN_LESS_100);
                    } else if (numOfUnits < 1000d) {
                        bdNumbOfUnits = bdNumbOfUnits.round(MC_BYTE_TO_HUMAN_LESS_1000);
                    } else {
                        bdNumbOfUnits = bdNumbOfUnits.round(MC_BYTE_TO_HUMAN_MORE_1000);
                        // special case, if we get 1024, we should display one of the higher unit!!
                        if (bdNumbOfUnits.longValue() == FileUtils.ONE_KB && u.immediatlyHigherUnit != null) {
                            humanReadableOutput = "1.0" + u.immediatlyHigherUnit.toString();
                            break;
                        }
                    }

                    humanReadableOutput = bdNumbOfUnits.toPlainString() + u.toString();
                }
            }
            // exit the loop
            if (humanReadableOutput != null) {
                break;
            }
        }

        if (humanReadableOutput == null) {
            humanReadableOutput = "" + size;
        }
        return humanReadableOutput;
    }

}
