package org.jabref.logic.openoffice.action;

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.openoffice.style.OOProcess;
import org.jabref.model.database.BibDatabase;
import org.jabref.model.openoffice.ootext.OOText;
import org.jabref.model.openoffice.style.Citation;
import org.jabref.model.openoffice.style.CitationGroup;
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.OOResult;

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.text.XTextRange;
import com.sun.star.uno.XComponentContext;

public class EditSeparate {

    private EditSeparate() {
    }

    public static OOResult<Boolean, JabRefException> separateCitations(XTextDocument doc,
                                                                       XComponentContext context,
                                                                       OOFrontend frontend,
                                                                       List<BibDatabase> databases,
                                                                       JStyle style) {
        try {
            boolean madeModifications = false;

            // To reduce surprises in JabRef52 mode, impose localOrder to
            // decide the visually last Citation in the group. Unless the
            // style changed since refresh this is the last on the screen
            // as well.
            frontend.citationGroups.lookupCitations(databases);
            frontend.citationGroups.imposeLocalOrder(OOProcess.comparatorForMulticite(style));

            List<CitationGroup> groups = frontend.citationGroups.getCitationGroupsUnordered();

            try {
                UnoScreenRefresh.lockControllers(doc);

                for (CitationGroup group : groups) {
                    Optional<XTextRange> markRange = frontend.getMarkRange(doc, group);
                    if (markRange.isEmpty()) {
                        return OOResult.error(new JabRefException("Could not find mark range for citation group"));
                    }
                    XTextRange textRange = markRange.get();
                    XTextCursor textCursor = textRange.getText().createTextCursorByRange(textRange);

                    List<Citation> citations = group.citationsInStorageOrder;
                    if (citations.size() <= 1) {
                        continue;
                    }

                    frontend.removeCitationGroup(group, doc);
                    // Now we own the content of citations

                    // Create a citation group for each citation.
                    final int last = citations.size() - 1;
                    for (int i = 0; i < citations.size(); i++) {
                        boolean insertSpaceBefore = false;
                        boolean insertSpaceAfter = i != last;
                        Citation citation = citations.get(i);

                        UpdateCitationMarkers.createAndFillCitationGroup(frontend,
                                doc,
                                context,
                                List.of(citation.citationKey),
                                List.of(citation.getPageInfo()),
                                group.citationType,
                                OOText.fromString(citation.citationKey),
                                textCursor,
                                style,
                                insertSpaceBefore,
                                insertSpaceAfter);

                        textCursor.collapseToEnd();
                    }

                    madeModifications = true;
                }
            } finally {
                UnoScreenRefresh.unlockControllers(doc);
            }
            return OOResult.ok(madeModifications);
        } catch (CreationException | IllegalTypeException | NoDocumentException | NotRemoveableException | PropertyVetoException | WrappedTargetException | IllegalArgumentException e) {
            return OOResult.error(new JabRefException(e.getMessage(), e));
        }
    }
}
