package no.valg.valgnatt.backend.rapport;

import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.Locale;

import static java.math.BigDecimal.ROUND_HALF_UP;
import static java.math.RoundingMode.HALF_UP;

/**
 * FØLGENDE PRINSIPPER GJELDER FOR DISSE METODENE
 * <p/>
 * 1. Korte, men beskrivende navn - ok med forkortelser. Lag et system av navnene:
 * resultattypeTilleggsinfo (eks: antEndring)
 * <p/>
 * 2. Metodene skal ikke manipulere tallet (f.eks. ved å begrense oppad mot 100%), med
 * mindre dette er eksplisitt angitt i navnet. Vi vil unngå å bruke slike "rednings-
 * metoder", fordi de kan skjule feil som burde vært håndtert lenge før vi kommer
 * til rapportene.
 * <p/>
 * 3. Hver metode skal beskrives med to tre eks. på konvertering. Null-tilfellet skal
 * alltid dokumenteres med mindre input-typene er primitiver.
 * <p/>
 * 4. Formålet med disse metodene er å gjøre rapportene så enkle å lese som mulig.
 * <p/>
 * 5. Ikke introduser bruk av Primitiv-wrappere med mindre du MÅ.
 * <p/>
 * 6. Alle metoder returnerer en String
 * <p/>
 * 7. Fas ut bruk av float/double - og bruk BigDecimal istedet
 */
public class Format {

    private static final DecimalFormatSymbols dfs = new DecimalFormatSymbols(Locale.US);
    private static final DecimalFormat prosFmt = new DecimalFormat("#0.0", dfs);
    private static final DecimalFormat prosFmt5 = new DecimalFormat("#0.00000", dfs);
    private static final DecimalFormat diffprosFmt = new DecimalFormat("+#0.0;-#0.0", dfs);
    private static final DecimalFormat diffprosFmt5 = new DecimalFormat("+#0.00000;-#0.00000", dfs);
    private static final DecimalFormat kvotFmt = new DecimalFormat("0.000000000000000", dfs);
    private static final BigDecimal HUNDRE = new BigDecimal(100);
    private static final int SCALE_10 = 10;

    static {
        prosFmt.setRoundingMode(HALF_UP);
        prosFmt5.setRoundingMode(HALF_UP);
        diffprosFmt.setRoundingMode(HALF_UP);
        diffprosFmt5.setRoundingMode(HALF_UP);
        kvotFmt.setRoundingMode(HALF_UP);
    }

    private Format() {
        // Hide default constructor
    }

    static String enDesimal(double d) {
        return prosFmt.format(d);
    }

    /**
     * Eksempler:
     * <pre>
     *    0 ->   0.0
     *  0.5 ->  50.0
     * -0.5 -> -50.0
     * </pre>
     *
     * @param f
     * @return
     */
    static String pst(float f) {
        return prosFmt.format(new BigDecimal(Float.toString(f)).multiply(HUNDRE));
    }

    /**
     * Eksempler:
     * <pre>
     * null -> null
     * 0.5  -> 50.0
     * </pre>
     *
     * @param f Kan være 0
     * @return null hvis f er 0
     */
    static String pstNN(Float f) {
        if (f == null) {
            return null;
        }
        return prosFmt.format(new BigDecimal(f.toString()).multiply(HUNDRE));
    }


    /**
     * Brukes for å håndtere 0-nevner her i stedet for i rapporten - skal bare brukes
     * dersom 0-nevner er en lovlig situasjon/tilstand for systemet. Ellers bruk
     * pst(int, int) eller pst(float). NN = Nevner kan være Null (0).
     * <p>
     * Eksempler:
     * <pre>
     *  5,  0 ->  null
     *  5, 10 ->  50.0
     * -5, 10 -> -50.0
     * </pre>
     *
     * @param teller
     * @param nevner Kan være 0
     * @return null hvis nevner er 0
     */
    static String pstNN(int teller, int nevner) {
        return nevner == 0 ? null : prosFmt.format(prosent(teller, nevner));
    }

    static String pstNN5(int teller, int nevner) {
        return nevner == 0 ? null : prosFmt5.format(prosent(teller, nevner));
    }

    private static BigDecimal prosent(int teller, int nevner) {
        return new BigDecimal(teller).multiply(HUNDRE).divide(new BigDecimal(nevner), Format.SCALE_10, ROUND_HALF_UP);
    }

    /**
     * Eksempler:
     * <pre>
     * null, null ->  null
     * null, 0.5  ->  null
     * 0.5 , null ->  null
     * 0.5 , 0.6  -> -10.0
     * </pre>
     *
     * @param f1 Kan være 0
     * @param f2 kan være 0
     * @return null hvis f1 eller f2 er 0
     */
    static String pstEndringNN(Float f1, Float f2) {
        if (f1 == null || f2 == null) {
            return null;
        }
        return diffprosFmt.format((new BigDecimal(f1.toString()).subtract(new BigDecimal(f2.toString()))).multiply(HUNDRE));
    }


    /**
     * Eksempler:
     * <pre>
     * 5,  0, 7,  0 ->  null
     * 7, 10, 5, 10 -> +20.0
     * 5, 10, 7, 10 -> -20.0
     * </pre>
     *
     * @param teller1
     * @param nevner1 Kan være 0
     * @param teller2
     * @param nevner2 Kan være 0
     * @return null hvis nevner er 0
     */
    static String pstEndringNN(int teller1, int nevner1, int teller2, int nevner2) {
        return formatertPstEndring(teller1, nevner1, teller2, nevner2, diffprosFmt);
    }

    static String pstEndringNN5(int teller1, int nevner1, int teller2, int nevner2) {
        return formatertPstEndring(teller1, nevner1, teller2, nevner2, diffprosFmt5);
    }

    private static String formatertPstEndring(int teller1, int nevner1, int teller2, int nevner2, DecimalFormat decimalFormat) {
        if (nevner1 == 0 || nevner2 == 0) {
            return null;
        }
        BigDecimal b1 = prosent(teller1, nevner1);
        BigDecimal b2 = prosent(teller2, nevner2);
        return decimalFormat.format(b1.subtract(b2));
    }

    /**
     * Eksempel:
     * <pre>
     * 0.01234567890123431234 -> 0.012345678901234
     * </pre>
     *
     * @param kvot
     * @return
     */
    public static String kvotient(double kvot) {
        return kvotFmt.format(kvot);
    }


}
