package org.jabref.logic.importer.fileformat;

import java.io.BufferedReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Pattern;

import org.jabref.logic.importer.Importer;
import org.jabref.logic.importer.ParserResult;
import org.jabref.logic.util.StandardFileType;
import org.jabref.model.entry.AuthorList;
import org.jabref.model.entry.BibEntry;
import org.jabref.model.entry.field.Field;
import org.jabref.model.entry.field.StandardField;
import org.jabref.model.entry.types.EntryType;
import org.jabref.model.entry.types.EntryTypeFactory;
import org.jabref.model.entry.types.StandardEntryType;

/**
 * Imports a SilverPlatter exported file. This is a poor format to parse, so it currently doesn't handle everything correctly.
 */
public class SilverPlatterImporter extends Importer {

    private static final Pattern START_PATTERN = Pattern.compile("Record.*INSPEC.*");

    @Override
    public String getName() {
        return "SilverPlatter";
    }

    @Override
    public StandardFileType getFileType() {
        return StandardFileType.SILVER_PLATTER;
    }

    @Override
    public String getDescription() {
        return "Imports a SilverPlatter exported file.";
    }

    @Override
    public boolean isRecognizedFormat(BufferedReader reader) throws IOException {
        // This format is very similar to Inspec, so we have a two-fold strategy:
        // If we see the flag signaling that it is an Inspec file, return false.
        // This flag should appear above the first entry and prevent us from
        // accepting the Inspec format. Then we look for the title entry.
        String str;
        while ((str = reader.readLine()) != null) {
            if (START_PATTERN.matcher(str).find()) {
                return false; // This is an Inspec file, so return false.
            }

            if ((str.length() >= 5) && "TI:  ".equals(str.substring(0, 5))) {
                return true;
            }
        }
        return false;
    }

    @Override
    public ParserResult importDatabase(BufferedReader reader) throws IOException {
        List<BibEntry> bibitems = new ArrayList<>();
        boolean isChapter = false;
        String str;
        StringBuilder sb = new StringBuilder();
        while ((str = reader.readLine()) != null) {
            if (str.length() < 2) {
                sb.append("__::__").append(str);
            } else {
                sb.append("__NEWFIELD__").append(str);
            }
        }
        String[] entries = sb.toString().split("__::__");
        EntryType type = StandardEntryType.Misc;
        Map<Field, String> h = new HashMap<>();
        for (String entry : entries) {
            if (entry.trim().length() < 6) {
                continue;
            }
            h.clear();
            String[] fields = entry.split("__NEWFIELD__");
            for (String field : fields) {
                if (field.length() < 6) {
                    continue;
                }
                String f3 = field.substring(0, 2);
                String frest = field.substring(5);
                switch (f3) {
                    case "TI" ->
                            h.put(StandardField.TITLE, frest);
                    case "AU" -> {
                        if (frest.trim().endsWith("(ed)")) {
                            String ed = frest.trim();
                            ed = ed.substring(0, ed.length() - 4);
                            h.put(StandardField.EDITOR,
                                    AuthorList.fixAuthorLastNameFirst(ed.replace(",-", ", ").replace(";", " and ")));
                        } else {
                            h.put(StandardField.AUTHOR,
                                    AuthorList.fixAuthorLastNameFirst(frest.replace(",-", ", ").replace(";", " and ")));
                        }
                    }
                    case "AB" ->
                            h.put(StandardField.ABSTRACT, frest);
                    case "DE" -> {
                        String kw = frest.replace("-;", ",").toLowerCase(Locale.ROOT);
                        h.put(StandardField.KEYWORDS, kw.substring(0, kw.length() - 1));
                    }
                    case "SO" -> {
                        int m = frest.indexOf('.');
                        if (m >= 0) {
                            String jr = frest.substring(0, m);
                            h.put(StandardField.JOURNAL, jr.replace("-", " "));
                            frest = frest.substring(m);
                            m = frest.indexOf(';');
                            if (m >= 5) {
                                String yr = frest.substring(m - 5, m).trim();
                                h.put(StandardField.YEAR, yr);
                                frest = frest.substring(m);
                                m = frest.indexOf(':');
                                int issueIndex = frest.indexOf('(');
                                int endIssueIndex = frest.indexOf(')');
                                if (m >= 0) {
                                    String pg = frest.substring(m + 1).trim();
                                    h.put(StandardField.PAGES, pg);
                                    h.put(StandardField.VOLUME, frest.substring(1, issueIndex).trim());
                                    h.put(StandardField.ISSUE, frest.substring(issueIndex + 1, endIssueIndex).trim());
                                }
                            }
                        }
                    }
                    case "PB" -> {
                        int m = frest.indexOf(':');
                        if (m >= 0) {
                            String jr = frest.substring(0, m);
                            h.put(StandardField.PUBLISHER, jr.replace("-", " ").trim());
                            frest = frest.substring(m);
                            m = frest.indexOf(", ");
                            if ((m + 2) < frest.length()) {
                                String yr = frest.substring(m + 2).trim();
                                try {
                                    Integer.parseInt(yr);
                                    h.put(StandardField.YEAR, yr);
                                } catch (NumberFormatException ex) {
                                    // Let's assume that this wasn't a number, since it
                                    // couldn't be parsed as an integer.
                                }
                            }
                        }
                    }
                    case "AF" ->
                            h.put(StandardField.SCHOOL, frest.trim());
                    case "DT" -> {
                        frest = frest.trim();
                        if ("Monograph".equals(frest)) {
                            type = StandardEntryType.Book;
                        } else if (frest.startsWith("Dissertation")) {
                            type = StandardEntryType.PhdThesis;
                        } else if (frest.toLowerCase(Locale.ROOT).contains(StandardField.JOURNAL.getName())) {
                            type = StandardEntryType.Article;
                        } else if ("Contribution".equals(frest) || "Chapter".equals(frest)) {
                            type = StandardEntryType.InCollection;
                            // This entry type contains page numbers and booktitle in the
                            // title field.
                            isChapter = true;
                        } else {
                            type = EntryTypeFactory.parse(frest.replace(" ", ""));
                        }
                    }
                }
            }

            if (isChapter) {
                String titleO = h.get(StandardField.TITLE);
                if (titleO != null) {
                    String title = titleO.trim();
                    int inPos = title.indexOf("\" in ");
                    if (inPos > 1) {
                        h.put(StandardField.TITLE, title.substring(0, inPos));
                    }
                }
            }

            BibEntry b = new BibEntry(type);
            // create one here
            b.setField(h);

            bibitems.add(b);
        }

        return new ParserResult(bibitems);
    }
}
