/* FichothequeLib_Tools - Copyright (c) 2015-2023 Vincent Calame - Exemole
 * Logiciel libre donné sous triple licence :
 * 1) selon les termes de la CeCILL V2
 * 2) selon les termes de l’EUPL V.1.1
 * 3) selon les termes de la GNU GPLv3
 * Voir le fichier licences.txt
 */


package net.fichotheque.tools.permission;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import net.fichotheque.Fichotheque;
import net.fichotheque.Subset;
import net.fichotheque.SubsetItem;
import net.fichotheque.SubsetKey;
import net.fichotheque.corpus.Corpus;
import net.fichotheque.corpus.FicheMeta;
import net.fichotheque.permission.PermissionEntry;
import net.fichotheque.permission.PermissionSummary;
import net.fichotheque.junction.Junctions;


/**
 *
 * @author Vincent Calame
 */
public final class ListFilterEngine {

    private final static List<PermissionEntry> EMPTY_PERMISSIONENTRYLIST = Collections.emptyList();

    private ListFilterEngine() {

    }

    public static List<PermissionEntry> filter(Subset subset, List<SubsetItem> subsetItemList, PermissionSummary permissionSummary) {
        SubsetKey subsetKey = subset.getSubsetKey();
        if (subsetItemList.isEmpty()) {
            return EMPTY_PERMISSIONENTRYLIST;
        }
        int readLevel = permissionSummary.getReadLevel(subsetKey);
        if (readLevel == PermissionSummary.SUMMARYLEVEL_0_NONE) {
            return EMPTY_PERMISSIONENTRYLIST;
        }
        if (readLevel == PermissionSummary.SUMMARYLEVEL_5_ADMIN) {
            return simpleList(subsetItemList, true);
        }
        int writeLevel = permissionSummary.getWriteLevel(subsetKey);
        if (readLevel == PermissionSummary.SUMMARYLEVEL_4_ALL) {
            if (writeLevel == PermissionSummary.SUMMARYLEVEL_4_ALL) {
                return simpleList(subsetItemList, true);
            } else if (writeLevel == PermissionSummary.SUMMARYLEVEL_0_NONE) {
                return simpleList(subsetItemList, false);
            }
        }
        if (subsetKey.isCorpusSubset()) {
            return corpusFilter(subsetItemList, permissionSummary);
        } else if ((subsetKey.isAddendaSubset()) || (subsetKey.isAlbumSubset())) {
            return junctionTestFilter(subset, subsetItemList, permissionSummary);
        } else {
            throw new IllegalStateException("Sphere and thesaurus has 0, 4 or 5 level only");
        }
    }

    private static List<PermissionEntry> simpleList(List<SubsetItem> list, boolean editable) {
        List<PermissionEntry> permissionEntryList = new ArrayList<PermissionEntry>();
        for (SubsetItem subsetItem : list) {
            permissionEntryList.add(new InternalPermissionEntry(subsetItem, editable));
        }
        return permissionEntryList;
    }

    private static List<PermissionEntry> corpusFilter(List<SubsetItem> list, PermissionSummary permissionSummary) {
        List<PermissionEntry> permissionEntryList = new ArrayList<PermissionEntry>();
        for (SubsetItem subsetItem : list) {
            FicheMeta ficheMeta = (FicheMeta) subsetItem;
            if (permissionSummary.canRead(ficheMeta)) {
                boolean editable = permissionSummary.canWrite(ficheMeta);
                permissionEntryList.add(new InternalPermissionEntry(ficheMeta, editable));
            }
        }
        return permissionEntryList;
    }

    private static List<PermissionEntry> junctionTestFilter(Subset subset, List<SubsetItem> list, PermissionSummary permissionSummary) {
        Fichotheque fichotheque = subset.getFichotheque();
        List<Corpus> corpusList = fichotheque.getCorpusList();
        int corpusLength = corpusList.size();
        Corpus[] corpusArray = new Corpus[corpusLength];
        boolean[] writableArray = new boolean[corpusLength];
        int p = 0;
        for (int i = 0; i < corpusLength; i++) {
            Corpus corpus = corpusList.get(i);
            int readLevel = permissionSummary.getReadLevel(corpus.getSubsetKey());
            if (readLevel > PermissionSummary.SUMMARYLEVEL_0_NONE) {
                corpusArray[p] = corpus;
                writableArray[p] = (permissionSummary.getWriteLevel(corpus.getSubsetKey()) != PermissionSummary.SUMMARYLEVEL_0_NONE);
                p++;
            }
        }
        if (p == 0) {
            return EMPTY_PERMISSIONENTRYLIST;
        }
        corpusLength = p;
        List<PermissionEntry> permissionEntryList = new ArrayList<PermissionEntry>();
        for (SubsetItem subsetItem : list) {
            boolean accept = false;
            boolean editable = false;
            for (int j = 0; j < corpusLength; j++) {
                Junctions junctions = fichotheque.getJunctions(subsetItem, corpusArray[j]);
                int k = -1;
                for (Junctions.Entry entry : junctions.getEntryList()) {
                    k++;
                    FicheMeta ficheMeta = (FicheMeta) entry.getSubsetItem();
                    if (!accept) {
                        if (permissionSummary.canRead(ficheMeta)) {
                            accept = true;
                        } else {
                            continue;
                        }
                    }
                    if (!writableArray[k]) {
                        break;
                    } else if (permissionSummary.canWrite(ficheMeta)) {
                        editable = true;
                        break;
                    }
                }
                if (editable) {
                    break;
                }
            }
            if (accept) {
                permissionEntryList.add(new InternalPermissionEntry(subsetItem, editable));
            }
        }
        return permissionEntryList;
    }


    private static class InternalPermissionEntry implements PermissionEntry {

        private final SubsetItem subsetItem;
        private final boolean editable;

        private InternalPermissionEntry(SubsetItem subsetItem, boolean editable) {
            this.subsetItem = subsetItem;
            this.editable = editable;
        }

        @Override
        public SubsetItem getSubsetItem() {
            return subsetItem;
        }

        @Override
        public boolean isEditable() {
            return editable;
        }

    }

}
