package net.sf.jabref.logic.importer.util;

import java.util.ArrayList;
import java.util.List;

import net.sf.jabref.model.entry.BibEntry;
import net.sf.jabref.model.entry.FieldName;
import net.sf.jabref.model.entry.MonthUtil;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.json.JSONArray;
import org.json.JSONObject;

public class JSONEntryParser {

    private static final Log LOGGER = LogFactory.getLog(JSONEntryParser.class);

    /**
     * Convert a JSONObject containing a bibJSON entry to a BibEntry
     *
     * @param bibJsonEntry The JSONObject to convert
     * @return the converted BibEntry
     */
    public BibEntry parseBibJSONtoBibtex(JSONObject bibJsonEntry, Character keywordSeparator) {
        // Fields that are directly accessible at the top level BibJson object
        String[] singleFieldStrings = {FieldName.YEAR, FieldName.TITLE, FieldName.ABSTRACT, FieldName.MONTH};

        // Fields that are accessible in the journal part of the BibJson object
        String[] journalSingleFieldStrings = {FieldName.PUBLISHER, FieldName.NUMBER, FieldName.VOLUME};



        BibEntry entry = new BibEntry();
        entry.setType("article");

        // Authors
        if (bibJsonEntry.has("author")) {
            JSONArray authors = bibJsonEntry.getJSONArray("author");
            List<String> authorList = new ArrayList<>();
            for (int i = 0; i < authors.length(); i++) {
                if (authors.getJSONObject(i).has("name")) {
                    authorList.add(authors.getJSONObject(i).getString("name"));
                } else {
                    LOGGER.info("Empty author name.");
                }
            }
            entry.setField(FieldName.AUTHOR, String.join(" and ", authorList));
        } else {
            LOGGER.info("No author found.");
        }

        // Direct accessible fields
        for (String field : singleFieldStrings) {
            if (bibJsonEntry.has(field)) {
                entry.setField(field, bibJsonEntry.getString(field));
            }
        }

        // Page numbers
        if (bibJsonEntry.has("start_page")) {
            if (bibJsonEntry.has("end_page")) {
                entry.setField(FieldName.PAGES,
                        bibJsonEntry.getString("start_page") + "--" + bibJsonEntry.getString("end_page"));
            } else {
                entry.setField(FieldName.PAGES, bibJsonEntry.getString("start_page"));
            }
        }

        // Journal
        if (bibJsonEntry.has("journal")) {
            JSONObject journal = bibJsonEntry.getJSONObject("journal");
            // Journal title
            if (journal.has("title")) {
                entry.setField(FieldName.JOURNAL, journal.getString("title"));
            } else {
                LOGGER.info("No journal title found.");
            }
            // Other journal related fields
            for (String field : journalSingleFieldStrings) {
                if (journal.has(field)) {
                    entry.setField(field, journal.getString(field));
                }
            }
        } else {
            LOGGER.info("No journal information found.");
        }

        // Keywords
        if (bibJsonEntry.has("keywords")) {
            JSONArray keywords = bibJsonEntry.getJSONArray("keywords");
            for (int i = 0; i < keywords.length(); i++) {
                if (!keywords.isNull(i)) {
                    entry.addKeyword(keywords.getString(i), keywordSeparator);
                }
            }
        }

        // Identifiers
        if (bibJsonEntry.has("identifier")) {
            JSONArray identifiers = bibJsonEntry.getJSONArray("identifier");
            for (int i = 0; i < identifiers.length(); i++) {
                String type = identifiers.getJSONObject(i).getString("type");
                if ("doi".equals(type)) {
                    entry.setField(FieldName.DOI, identifiers.getJSONObject(i).getString("id"));
                } else if ("pissn".equals(type)) {
                    entry.setField(FieldName.ISSN, identifiers.getJSONObject(i).getString("id"));
                } else if ("eissn".equals(type)) {
                    entry.setField(FieldName.ISSN, identifiers.getJSONObject(i).getString("id"));
                }
            }
        }

        // Links
        if (bibJsonEntry.has("link")) {
            JSONArray links = bibJsonEntry.getJSONArray("link");
            for (int i = 0; i < links.length(); i++) {
                if (links.getJSONObject(i).has("type")) {
                    String type = links.getJSONObject(i).getString("type");
                    if ("fulltext".equals(type) && links.getJSONObject(i).has("url")) {
                        entry.setField(FieldName.URL, links.getJSONObject(i).getString("url"));
                    }
                }
            }
        }

        return entry;
    }

    /**
     * Convert a JSONObject obtained from http://api.springer.com/metadata/json to a BibEntry
     *
     * @param springerJsonEntry the JSONObject from search results
     * @return the converted BibEntry
     */
    public static BibEntry parseSpringerJSONtoBibtex(JSONObject springerJsonEntry) {
        // Fields that are directly accessible at the top level Json object
        String[] singleFieldStrings = {FieldName.ISSN, FieldName.VOLUME, FieldName.ABSTRACT, FieldName.DOI, FieldName.TITLE, FieldName.NUMBER,
                FieldName.PUBLISHER};

        BibEntry entry = new BibEntry();
        String nametype;

        // Guess publication type
        String isbn = springerJsonEntry.optString("isbn");
        if (com.google.common.base.Strings.isNullOrEmpty(isbn)) {
            // Probably article
            entry.setType("article");
            nametype = FieldName.JOURNAL;
        } else {
            // Probably book chapter or from proceeding, go for book chapter
            entry.setType("incollection");
            nametype = FieldName.BOOKTITLE;
            entry.setField(FieldName.ISBN, isbn);
        }

        // Authors
        if (springerJsonEntry.has("creators")) {
            JSONArray authors = springerJsonEntry.getJSONArray("creators");
            List<String> authorList = new ArrayList<>();
            for (int i = 0; i < authors.length(); i++) {
                if (authors.getJSONObject(i).has("creator")) {
                    authorList.add(authors.getJSONObject(i).getString("creator"));
                } else {
                    LOGGER.info("Empty author name.");
                }
            }
            entry.setField(FieldName.AUTHOR, String.join(" and ", authorList));
        } else {
            LOGGER.info("No author found.");
        }

        // Direct accessible fields
        for (String field : singleFieldStrings) {
            if (springerJsonEntry.has(field)) {
                String text = springerJsonEntry.getString(field);
                if (!text.isEmpty()) {
                    entry.setField(field, text);
                }
            }
        }

        // Page numbers
        if (springerJsonEntry.has("startingPage") && !(springerJsonEntry.getString("startingPage").isEmpty())) {
            if (springerJsonEntry.has("endPage") && !(springerJsonEntry.getString("endPage").isEmpty())) {
                entry.setField(FieldName.PAGES,
                        springerJsonEntry.getString("startingPage") + "--" + springerJsonEntry.getString("endPage"));
            } else {
                entry.setField(FieldName.PAGES, springerJsonEntry.getString("startingPage"));
            }
        }

        // Journal
        if (springerJsonEntry.has("publicationName")) {
            entry.setField(nametype, springerJsonEntry.getString("publicationName"));
        }

        // URL
        if (springerJsonEntry.has("url")) {
            JSONArray urlarray = springerJsonEntry.optJSONArray("url");
            if (urlarray == null) {
                entry.setField(FieldName.URL, springerJsonEntry.optString("url"));
            } else {
                entry.setField(FieldName.URL, urlarray.getJSONObject(0).optString("value"));
            }
        }

        // Date
        if (springerJsonEntry.has("publicationDate")) {
            String date = springerJsonEntry.getString("publicationDate");
            entry.setField(FieldName.DATE, date); // For BibLatex
            String[] dateparts = date.split("-");
            entry.setField(FieldName.YEAR, dateparts[0]);
            entry.setField(FieldName.MONTH, MonthUtil.getMonthByNumber(Integer.parseInt(dateparts[1])).bibtexFormat);
        }

        // Clean up abstract (often starting with Abstract)
        entry.getField(FieldName.ABSTRACT).ifPresent(abstractContents -> {
            if (abstractContents.startsWith("Abstract")) {
                entry.setField(FieldName.ABSTRACT, abstractContents.substring(8));
            }
        });

        return entry;
    }
}
