package org.jabref.logic.openoffice.action;

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

import org.jabref.logic.JabRefException;
import org.jabref.logic.openoffice.frontend.OOFrontend;
import org.jabref.logic.openoffice.frontend.UpdateCitationMarkers;
import org.jabref.logic.openoffice.style.JStyle;
import org.jabref.logic.util.strings.StringUtil;
import org.jabref.model.database.BibDatabase;
import org.jabref.model.entry.BibEntry;
import org.jabref.model.openoffice.ootext.OOText;
import org.jabref.model.openoffice.style.Citation;
import org.jabref.model.openoffice.style.CitationMarkerEntry;
import org.jabref.model.openoffice.style.CitationType;
import org.jabref.model.openoffice.style.NonUniqueCitationMarker;
import org.jabref.model.openoffice.style.OODataModel;
import org.jabref.model.openoffice.uno.CreationException;
import org.jabref.model.openoffice.uno.NoDocumentException;
import org.jabref.model.openoffice.uno.UnoScreenRefresh;
import org.jabref.model.openoffice.util.OOListUtil;
import org.jabref.model.openoffice.util.OOVoidResult;

import com.sun.star.beans.IllegalTypeException;
import com.sun.star.beans.NotRemoveableException;
import com.sun.star.beans.PropertyVetoException;
import com.sun.star.lang.WrappedTargetException;
import com.sun.star.text.XTextCursor;
import com.sun.star.text.XTextDocument;
import com.sun.star.uno.XComponentContext;

public class EditInsert {

    private EditInsert() {
    }

    /// In insertEntry we receive BibEntry values from the GUI.
    ///
    /// In the document we store citations by their citation key.
    ///
    /// If the citation key is missing, the best we can do is to notify the user. Or the programmer, that we cannot accept such input.
    private static String insertEntryGetCitationKey(BibEntry entry) {
        Optional<String> key = entry.getCitationKey();
        if (key.isEmpty()) {
            throw new IllegalArgumentException("insertEntryGetCitationKey: cannot cite entries without citation key");
        }
        return key.get();
    }

    /// @param cursor   Where to insert.
    /// @param pageInfo A single pageInfo for a list of entries. This is what we get from the GUI.
    public static OOVoidResult<JabRefException> insertCitationGroup(XTextDocument doc,
                                                                    XComponentContext context,
                                                                    OOFrontend frontend,
                                                                    XTextCursor cursor,
                                                                    List<BibEntry> entries,
                                                                    BibDatabase database,
                                                                    JStyle style,
                                                                    CitationType citationType,
                                                                    String pageInfo,
                                                                    boolean insertSpaceBefore,
                                                                    boolean insertSpaceAfter) {
        List<String> citationKeys = OOListUtil.map(entries, EditInsert::insertEntryGetCitationKey);

        final int totalEntries = entries.size();
        List<Optional<OOText>> pageInfos = OODataModel.fakePageInfos(pageInfo, totalEntries);

        List<CitationMarkerEntry> citations = new ArrayList<>(totalEntries);
        for (int i = 0; i < totalEntries; i++) {
            Citation cit = new Citation(citationKeys.get(i));
            cit.lookupInDatabases(List.of(database));
            cit.setPageInfo(pageInfos.get(i));
            citations.add(cit);
        }

        // The text we insert
        OOText citeText;
        if (style.isNumberEntries()) {
            citeText = OOText.fromString("[-]"); // A dash only. Only refresh later.
        } else {
            citeText = style.createCitationMarker(citations,
                    citationType,
                    NonUniqueCitationMarker.FORGIVEN);
        }

        if (StringUtil.isBlank(OOText.toString(citeText))) {
            citeText = OOText.fromString("[?]");
        }

        try {
            UnoScreenRefresh.lockControllers(doc);
            UpdateCitationMarkers.createAndFillCitationGroup(frontend,
                    doc,
                    context,
                    citationKeys,
                    pageInfos,
                    citationType,
                    citeText,
                    cursor,
                    style,
                    insertSpaceBefore,
                    insertSpaceAfter);
            return OOVoidResult.ok();
        } catch (NoDocumentException | NotRemoveableException | WrappedTargetException | PropertyVetoException | CreationException | IllegalTypeException e) {
            return OOVoidResult.error(new JabRefException(e.getMessage(), e));
        } finally {
            UnoScreenRefresh.unlockControllers(doc);
        }
    }
}
