package com.konstantin;


import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

public class ConverterNumToWords {

    private final static String PATH_FILE = "dataName/nameUnits.txt";
    private final static int MALE_GENDER = 1;
    private final static int FEMALE_GENDER = -1;
    private final static String SEPARATOR = " ";

    private static final Map<Integer, String> nameTriad = new HashMap<Integer, String>() {{
        put(-2, "две");
        put(-1, "одна");
        put(0, "ноль");
        put(1, "один");
        put(2, "два");
        put(3, "три");
        put(4, "четыре");
        put(5, "пять");
        put(6, "шесть");
        put(7, "семь");
        put(8, "восемь");
        put(9, "девять");
        put(10, "десять");
        put(11, "одиннадцать");
        put(12, "двенадцать");
        put(13, "тринадцать");
        put(14, "четырнадцать");
        put(15, "пятнадцать");
        put(16, "шестнадцать");
        put(17, "семнадцать");
        put(18, "восемнадцать");
        put(19, "девятнадцать");
        put(20, "двадцать");
        put(30, "тридцать");
        put(40, "сорок");
        put(50, "пятьдесят");
        put(60, "шестьдесят");
        put(70, "семьдесят");
        put(80, "восемьдесят");
        put(90, "девяносто");
        put(100, "сто");
        put(200, "двести");
        put(300, "триста");
        put(400, "четыреста");
        put(500, "пятьсот");
        put(600, "шестьсот");
        put(700, "семьсот");
        put(800, "восемьсот");
        put(900, "девятьсот");

    }};

    private Map<Integer, String> nameUnits = new HashMap<Integer, String>() {{
        put(1, "тысяч");
    }};

    private final String[][] endings = {{"а", "и", ""}, {"", "а", "ов"}};


    public ConverterNumToWords() {

        readNameUnitFromFile();

    }

    private void readNameUnitFromFile() {
        try (BufferedReader br = new BufferedReader(
                new InputStreamReader(
                        new FileInputStream(PATH_FILE), "UTF8"))) {
            String tmpLineStr;
            String Units[];
            while ((tmpLineStr = br.readLine()) != null) {
                if (!Objects.equals(tmpLineStr , "")) {
                    Units = tmpLineStr.split(SEPARATOR);
                    nameUnits.put(Integer.valueOf(Units[0]), Units[1]);
                }
            }


        } catch (IOException ex) {
            throw new RuntimeException(ex);
        }
    }

    private String convertTriad(String triad, int gender) {
        int hundreds = Character.getNumericValue(triad.charAt(0));
        int dozens = Character.getNumericValue(triad.charAt(1));
        int units = Character.getNumericValue(triad.charAt(2));
        if (Objects.equals(triad, "000")) return "";
        String words = "";
        if (hundreds != 0)
            words += nameTriad.get(hundreds * 100) + SEPARATOR;
        if (dozens == 1 && units <= 9 && units >= 0)   //Для чисел от 10 до 19 ключи для мапа считаются отдельно
            words += nameTriad.get(dozens * 10 + units) + SEPARATOR;
        else {
            if (dozens != 0)
                words += nameTriad.get(dozens * 10) + SEPARATOR;
            if (units != 0 && units > 2)
                words += nameTriad.get(units) + SEPARATOR;
            else if (units != 0 && units <= 2)
                words += nameTriad.get(units * gender) + SEPARATOR;
        }
        return words;
    }

    /**
     * Получаем номер окончания  для названия из мап nameUnit, основываясь на числе n ,в массиве ending
     */
    private int selectForm(int n) {
        n = Math.abs(n) % 100;
        int n1 = n % 10;
        if (n > 10 && n < 20) return 2;
        if (n1 > 1 && n1 < 5) return 1;
        if (n1 == 1) return 0;
        return 2;
    }

    private String getFormNameUnit(int degree, String number) {
        if (nameUnits.get(degree) == null && degree != 0) throw new NullPointerException("Dont exist name " +
                degree + " thousands of degrees");
        if (Objects.equals(number, "000")) return "";
        if (degree == 1) return nameUnits.get(degree) + endings[0][selectForm(Integer.parseInt(number))] + SEPARATOR;
        if (degree > 1) return nameUnits.get(degree) + endings[1][selectForm(Integer.parseInt(number))] + SEPARATOR;
        else
            return "";
    }

    /**
     * Конвертирует число number в запись словами
     */
    public String convertNumbToWords(BigInteger number) {
        String numberStr = number.toString();

        if (Objects.equals(numberStr, "0")) return nameTriad.get(0);

        String nameNumber = "";

        if (numberStr.charAt(0) == '-') {
            nameNumber += "минус ";
            numberStr = numberStr.substring(1);
        }

        for (int i = 0; i < numberStr.length() % 3; i++)
            numberStr = "0" + numberStr;

        for (int i = 0; i < numberStr.length() / 3; i++) {
            int degree = numberStr.length() / 3 - i - 1;
            if (degree == 1)
                nameNumber += convertTriad(numberStr.substring((i * 3), i * 3 + 3), FEMALE_GENDER) +
                        getFormNameUnit(degree, numberStr.substring((i * 3), i * 3 + 3));
            else
                nameNumber += convertTriad(numberStr.substring((i * 3), i * 3 + 3), MALE_GENDER) +
                        getFormNameUnit(degree, numberStr.substring((i * 3), i * 3 + 3));
        }
        return nameNumber.trim();
    }


}
