/* BdfServer - Copyright (c) 2022 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 fr.exemole.bdfserver.tools.diagnostic.urlscan;

import fr.exemole.bdfserver.api.BdfServer;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import net.fichotheque.corpus.Corpus;
import net.fichotheque.corpus.FicheMeta;
import net.fichotheque.corpus.Fiches;
import net.fichotheque.corpus.fiche.FicheAPI;
import net.fichotheque.corpus.fiche.FicheBlock;
import net.fichotheque.corpus.fiche.FicheBlocks;
import net.fichotheque.corpus.fiche.FicheItem;
import net.fichotheque.corpus.fiche.FicheItems;
import net.fichotheque.corpus.fiche.Insert;
import net.fichotheque.corpus.fiche.Li;
import net.fichotheque.corpus.fiche.LinkItem;
import net.fichotheque.corpus.fiche.ParagraphBlock;
import net.fichotheque.corpus.fiche.S;
import net.fichotheque.corpus.fiche.Table;
import net.fichotheque.corpus.fiche.Td;
import net.fichotheque.corpus.fiche.TextContent;
import net.fichotheque.corpus.fiche.Tr;
import net.fichotheque.corpus.fiche.Ul;
import net.fichotheque.corpus.fiche.ZoneBlock;
import net.fichotheque.corpus.metadata.CorpusField;
import net.fichotheque.corpus.metadata.CorpusMetadata;
import net.fichotheque.corpus.metadata.FieldKey;
import net.fichotheque.utils.CorpusMetadataUtils;


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

    private final Map<String, UrlInfo> infoMap = new HashMap<String, UrlInfo>();
    private final BdfServer bdfServer;
    private final UrlReportCache cache;
    private final Map<String, UrlReport> urlReportMap;

    private UrlScanEngine(BdfServer bdfServer) {
        this.bdfServer = bdfServer;
        this.cache = new UrlReportCache(bdfServer);
        this.urlReportMap = cache.read();
    }

    private void add(String url, FicheMeta ficheMeta, FieldKey fieldKey) {
        UrlInfo info = infoMap.get(url);
        if (info == null) {
            UrlReport urlReport = urlReportMap.get(url);
            info = UrlInfo.initWithFicheInfo(url, urlReport);
            infoMap.put(url, info);
        }
        info.add(ficheMeta, fieldKey);
    }

    private void scanCorpus(Corpus corpus, List<FicheMeta> ficheMetaList) {
        CorpusInfo corpusInfo = new CorpusInfo(corpus);
        if (!corpusInfo.isEmpty()) {
            for (FicheMeta ficheMeta : ficheMetaList) {
                corpusInfo.scanFiche(ficheMeta);
            }
        }
    }

    private void cacheResult() {
        cache.cache(urlReportMap.values(), false);
    }

    public static Map<String, UrlInfo> run(BdfServer bdfServer) {
        UrlScanEngine urlScanEngine = new UrlScanEngine(bdfServer);
        for (Corpus corpus : bdfServer.getFichotheque().getCorpusList()) {
            urlScanEngine.scanCorpus(corpus, corpus.getFicheMetaList());
        }
        urlScanEngine.cacheResult();
        return urlScanEngine.infoMap;
    }

    public static Map<String, UrlInfo> run(BdfServer bdfServer, Fiches fiches) {
        UrlScanEngine urlScanEngine = new UrlScanEngine(bdfServer);
        for (Fiches.Entry entry : fiches.getEntryList()) {
            urlScanEngine.scanCorpus(entry.getCorpus(), entry.getFicheMetaList());
        }
        urlScanEngine.cacheResult();
        return urlScanEngine.infoMap;
    }


    private class CorpusInfo {

        private final String corpusName;
        private final Corpus corpus;
        private final FieldKey[] linkFieldArray;
        private final FieldKey[] sectionFieldArray;
        private final boolean withSection;
        private FicheMeta currentFicheMeta;
        private FieldKey currentFieldKey;


        private CorpusInfo(Corpus corpus) {
            this.corpus = corpus;
            this.corpusName = corpus.getSubsetName();
            CorpusMetadata corpusMetadata = corpus.getCorpusMetadata();
            List<CorpusField> fieldList = CorpusMetadataUtils.getCorpusFieldListByFicheItemType(corpusMetadata, CorpusField.LINK_FIELD, false);
            this.linkFieldArray = CorpusMetadataUtils.toFieldKeyArray(fieldList);
            this.sectionFieldArray = CorpusMetadataUtils.toFieldKeyArray(corpusMetadata.getSectionList());
            this.withSection = (this.sectionFieldArray.length != 0);
        }

        private boolean isEmpty() {
            return ((linkFieldArray.length == 0) && (!withSection));
        }

        private void scanFiche(FicheMeta ficheMeta) {
            currentFicheMeta = ficheMeta;
            FicheAPI ficheAPI = ficheMeta.getFicheAPI(withSection);
            for (FieldKey fieldKey : linkFieldArray) {
                currentFieldKey = fieldKey;
                Object value = ficheAPI.getValue(fieldKey);
                if (value != null) {
                    if (value instanceof FicheItem) {
                        testFicheItem((FicheItem) value);
                    } else if (value instanceof FicheItems) {
                        for (FicheItem ficheItem : (FicheItems) value) {
                            testFicheItem(ficheItem);
                        }
                    }
                }
            }
            if (withSection) {
                for (FieldKey fieldKey : sectionFieldArray) {
                    currentFieldKey = fieldKey;
                    Object value = ficheAPI.getValue(fieldKey);
                    if (value != null) {
                        testFicheBlocks((FicheBlocks) value);
                    }
                }
            }
        }

        private void testFicheItem(FicheItem ficheItem) {
            if (ficheItem instanceof LinkItem) {
                LinkItem link = ((LinkItem) ficheItem);
                String href = link.getHref();
                if (!href.isEmpty()) {
                    add(href, currentFicheMeta, currentFieldKey);
                }
            }
        }

        private void testFicheBlocks(FicheBlocks ficheBlocks) {
            for (FicheBlock ficheBlock : ficheBlocks) {
                if (ficheBlock instanceof ParagraphBlock) {
                    testTextContent((TextContent) ficheBlock);
                } else if (ficheBlock instanceof Ul) {
                    for (Li li : ((Ul) ficheBlock)) {
                        testFicheBlocks(li);
                    }
                } else if (ficheBlock instanceof ZoneBlock) {
                    ZoneBlock zoneBlock = (ZoneBlock) ficheBlock;
                    testTextContent(zoneBlock.getLegende());
                    testTextContent(zoneBlock.getLegende());
                    if (zoneBlock instanceof Insert) {
                        Insert insert = (Insert) zoneBlock;
                        testTextContent(insert.getAlt());
                        testTextContent(insert.getCredit());
                    } else if (zoneBlock instanceof Table) {
                        Table table = (Table) zoneBlock;
                        for (Tr tr : (Table) zoneBlock) {
                            for (Td td : tr) {
                                testTextContent(td);
                            }
                        }
                    }
                }
            }
        }

        private void testTextContent(TextContent textContent) {
            for (Object object : textContent) {
                if (object instanceof S) {
                    S span = (S) object;
                    testSpan(span);
                }
            }
        }

        private void testSpan(S span) {
            switch (span.getType()) {
                case S.ANCHOR:
                case S.IREF:
                case S.MOTCLE:
                case S.FICHE:
                    break;
                default:
                    String ref = span.getRef();
                    if ((!ref.isEmpty()) && (!ref.startsWith("#"))) {
                        add(ref, currentFicheMeta, currentFieldKey);
                    }
            }
        }


    }


}
