
import java.io.File;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * rename files using
 *
 * https://raw.githubusercontent.com/anyascii/anyascii/refs/heads/master/unidecode/unidecode.tsv
 *
 * @author matecsaba
 */
public class renamer {

    /**
     * current year
     */
    public static int year;

    /**
     * the main
     *
     * @param args arguments
     */
    public static void main(String[] args) {
        if (args.length < 1) {
            playerUtil.put("usage: java this <directory> [predir] [prepend text] [remover text] [demo] [really] [extended] [basic] [short] [full] [seq]");
            return;
        }
        year = LocalDateTime.now().getYear();
        String prepend = "";
        String remover = "";
        boolean really = true;
        boolean basic = false;
        boolean full = false;
        boolean seq = false;
        for (int i = 1; i < args.length; i++) {
            String s = args[i].toLowerCase();
            if (s.equals("predir")) {
                prepend = new File(".").getAbsolutePath();
                int o = prepend.lastIndexOf("/");
                prepend = prepend.substring(0, o);
                o = prepend.lastIndexOf("/");
                prepend = prepend.substring(o + 1, prepend.length()) + " ";
            }
            if (s.equals("prepend")) {
                i++;
                prepend = args[i] + " ";
            }
            if (s.equals("remover")) {
                i++;
                remover = args[i] + " ";
            }
            if (s.equals("noremover")) {
                remover = "";
            }
            if (s.equals("noprepend")) {
                prepend = "";
            }
            if (s.equals("demo")) {
                really = false;
            }
            if (s.equals("really")) {
                really = true;
            }
            if (s.equals("extended")) {
                basic = false;
            }
            if (s.equals("basic")) {
                basic = true;
            }
            if (s.equals("short")) {
                full = false;
            }
            if (s.equals("full")) {
                full = true;
            }
            if (s.equals("unseq")) {
                seq = false;
            }
            if (s.equals("seq")) {
                seq = true;
            }
        }
        String s = args[0];
        playerUtil.put("reading " + s + "...");
        String[] l1 = new File(s).list();
        List<String> l2 = new ArrayList<String>();
        for (int i = 0; i < l1.length; i++) {
            l2.add(l1[i]);
        }
        Collections.sort(l2, new duplicates());
        int tried = 0;
        int failed = 0;
        for (int i = 0; i < l2.size(); i++) {
            s = l2.get(i);
            String a = s;
            if (full && (a.length() > 10)) {
                if (playerUtil.str2int(a.substring(0, 4) + a.substring(5, 7) + a.substring(8, 10)) > 0) {
                    a = a.substring(0, 4) + " " + a.substring(10, a.length());
                }
            }
            if (seq && (a.length() > 2)) {
                if (playerUtil.str2int(a.substring(0, 2)) > 0) {
                    a = a.substring(2, a.length());
                }
            }
            a = prepend + a;
            a = unidecode(a);
            a = unidecode(a);
            a = unidecode(a);
            a = guessName(a, basic);
            a = guessName(a, basic);
            a = guessName(a, basic);
            if (a == null) {
                playerUtil.put(s + " have no guess");
                continue;
            }
            if (a.startsWith(remover)) {
                a = a.substring(remover.length(), a.length()).trim();
            }
            if (a.equals(s)) {
                continue;
            }
            playerUtil.put(s + " --> " + a);
            tried++;
            if (!really) {
                continue;
            }
            try {
                new File(args[0] + s).renameTo(new File(args[0] + a));
            } catch (Exception e) {
                failed++;
            }
        }
    }

    /**
     * path separator
     */
    public final static String sep = "/";

    /**
     * guess name
     *
     * @param s string to use
     * @param basic guess mode
     * @return guessed name
     */
    public static String guessName(String s, boolean basic) {
        if (s == null) {
            return null;
        }
        if (s.length() < 1) {
            return null;
        }
        if (s.startsWith(".")) {
            return null;
        }
        if (s.indexOf(sep) >= 0) {
            return null;
        }
        s = s.replaceAll("=", " ");
        s = s.replaceAll("_", " ");
        s = s.replaceAll("~", " ");
        s = s.replaceAll("&", " ");
        s = s.replaceAll("@", " ");
        s = s.replaceAll("#", " ");
        s = s.replaceAll("\\$", " ");
        s = s.replaceAll("%", " ");
        s = s.replaceAll("\\+", " ");
        s = s.replaceAll("\\^", " ");
        s = s.replaceAll("\\*", " ");
        s = s.replaceAll("\\(", " ");
        s = s.replaceAll("\\)", " ");
        s = s.replaceAll("\\[", " ");
        s = s.replaceAll("\\]", " ");
        s = s.replaceAll("\\{", " ");
        s = s.replaceAll("\\}", " ");
        s = s.replaceAll("\\\\", " ");
        s = s.replaceAll("<", " ");
        s = s.replaceAll(">", " ");
        s = s.replaceAll("/", " ");
        s = s.replaceAll("!", " ");
        s = s.replaceAll("\\?", " ");
        s = s.replaceAll(",", " ");
        s = s.replaceAll(":", " ");
        s = s.replaceAll(";", " ");
        s = s.replaceAll("\\.", " ");
        s = s.replaceAll("'", "");
        s = s.replaceAll("`", "");
        s = s.replaceAll("‘", "");
        s = s.replaceAll("\"", "");
        s = s.replaceAll("  ", " ");
        s = s.trim();
        String a = "";
        for (;;) {
            if (s.length() < 1) {
                break;
            }
            int i = s.indexOf(" ");
            String b;
            if (i < 1) {
                b = s;
                s = "";
            } else {
                b = s.substring(0, i);
                s = s.substring(i, s.length()).trim();
            }
            b = b.trim();
            if (b.length() < 1) {
                continue;
            }
            if (b.length() > 1) {
                b = b.substring(0, 1).toUpperCase() + b.substring(1, b.length()).toLowerCase();
            }
            a += " " + b;
        }
        s = a.trim();
        if (basic) {
            return s;
        }
        int p = s.lastIndexOf(" ");
        a = s.substring(p + 1, s.length());
        if ("|-|128|192|224|256|320|Kbps|Web|Ep|Lp|Vinyl|Dvd|Cd|Cds|2cd|3cd|4cd|5cd|6cd|7cd|7cd|9cd|Acoustic|Live|Demo|Split|Promo|Bootleg|Single|Version|Edition|Instrumental|Remastered|Rerecorded|Reissue|Re-recorded|Re-mastered|Re-issue|Compilation|Deluxe|Limited|Special|Digipack|".indexOf("|" + a + "|") >= 0) {
            s = s.substring(0, p).trim();
        }
        p = -1;
        for (int i = s.length(); i >= 0; i--) {
            if (isDate(s, i)) {
                p = i;
            }
        }
        if (p < 0) {
            return s;
        }
        a = s.substring(p, p + 4);
        s = s.substring(0, p) + sep + s.substring(p + 4, s.length());
        s = doAlign(s);
        if (s.endsWith(sep)) {
            s = s.substring(0, s.length() - 1);
            int i = s.indexOf("-");
            if (i < 1) {
                return null;
            }
            s = s.substring(0, i) + sep + s.substring(i + 1, s.length());
        }
        s = doAlign(s);
        s = s.replaceAll(sep, " - " + a + " - ").trim();
        return s;
    }

    private static String doAlign(String s) {
        s = s.trim();
        for (int i = 0; i < 5; i++) {
            s = s.replaceAll(" " + sep, sep).replaceAll(sep + " ", sep);
            s = s.replaceAll("\\[" + sep, sep).replaceAll(sep + "\\]", sep);
            s = s.replaceAll("\\(" + sep, sep).replaceAll(sep + "\\)", sep);
            s = s.replaceAll("-" + sep, sep).replaceAll(sep + "-", sep);
        }
        return s.trim();
    }

    /**
     * test if this is a date
     *
     * @param s string to use
     * @param p position
     * @return true on success
     */
    public static boolean isDate(String s, int p) {
        int i;
        try {
            s = s.substring(p, p + 4);
            i = Integer.parseInt(s);
        } catch (Exception e) {
            return false;
        }
        if (i < (year - 40)) {
            return false;
        }
        if (i > (year + 1)) {
            return false;
        }
        return true;
    }

    private static List<String> unidecoder = null;

    /**
     * use unidecode table
     *
     * @param a string
     * @return decoded
     */
    public static String unidecode(String a) {
        String b = "";
        int s = a.codePointCount(0, a.length());
        for (int i = 0; i < s; i++) {
            int o = a.codePointAt(i);
            if ((o >= 32) && (o < 127)) {
                b += (char) o;
                continue;
            }
            if (unidecoder == null) {
                unidecoder = new ArrayList<String>();
                playerLyric res = playerUtil.download("" + renamer.class.getResource("unidecode.tsv"));
                if (res == null) {
                    return a;
                }
                for (int p = 0; p < res.size(); p++) {
                    String q = res.get(p);
                    if (q.codePointCount(0, q.length()) < 3) {
                        continue;
                    }
                    if (q.codePointAt(1) != 9) {
                        continue;
                    }
                    int r = q.codePointAt(0);
                    for (; unidecoder.size() < r;) {
                        unidecoder.add(null);
                    }
                    unidecoder.add(q.substring(2, q.length()));
                }
            }
            if ((o < 0) || (o >= unidecoder.size())) {
                return a;
            }
            String q = unidecoder.get(o);
            if (q == null) {
                return a;
            }
            b += q;
        }
        return b;
    }

}
