/* @@@@@APPLICATION@@@@@ - Copyright (c) 2025 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.dom;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
import net.fichotheque.SubsetKey;
import net.fichotheque.corpus.fiche.AttConsumer;
import net.fichotheque.corpus.fiche.Cdatadiv;
import net.fichotheque.corpus.fiche.Code;
import net.fichotheque.corpus.fiche.Div;
import net.fichotheque.corpus.fiche.FicheBlock;
import net.fichotheque.corpus.fiche.H;
import net.fichotheque.corpus.fiche.Insert;
import net.fichotheque.corpus.fiche.Li;
import net.fichotheque.corpus.fiche.Ln;
import net.fichotheque.corpus.fiche.P;
import net.fichotheque.corpus.fiche.S;
import net.fichotheque.corpus.fiche.Table;
import net.fichotheque.corpus.fiche.Td;
import net.fichotheque.corpus.fiche.TextContentBuilder;
import net.fichotheque.corpus.fiche.Tr;
import net.fichotheque.corpus.fiche.Ul;
import net.fichotheque.corpus.fiche.ZoneBlock;
import net.mapeadores.util.html.HtmlCleaner;
import net.mapeadores.util.localisation.Lang;
import net.mapeadores.util.logging.MessageHandler;
import net.mapeadores.util.xml.XMLUtils;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;


/**
 *
 * @author Vincent Calame
 */
public class FicheBlockDOMReader {

    private final HtmlCleaner htmlCleaner;
    private final MessageHandler messageHandler;

    public FicheBlockDOMReader(HtmlCleaner htmlCleaner, MessageHandler messageHandler) {
        this.htmlCleaner = htmlCleaner;
        this.messageHandler = messageHandler;
    }

    public List<FicheBlock> readFicheBlockList(Element element) {
        List<FicheBlock> list = new ArrayList<FicheBlock>();
        NodeList liste = element.getChildNodes();
        for (int j = 0; j < liste.getLength(); j++) {
            if (liste.item(j).getNodeType() == Node.ELEMENT_NODE) {
                Element child = (Element) liste.item(j);
                FicheBlock ficheBlock = readFicheBlock(child);
                if (ficheBlock != null) {
                    list.add(ficheBlock);
                }
            }
        }
        return list;
    }

    public FicheBlock readFicheBlock(Element element) {
        switch (element.getTagName()) {
            case "p":
                return readP(element);
            case "ul":
                return readUl(element);
            case "h":
                return readH(element);
            case "code":
                return readCode(element);
            case "table":
                return readTable(element);
            case "insert":
                return readInsert(element);
            case "div":
                return readDiv(element);
            case "cdatadiv":
                return readCdatadiv(element);
            default:
                return null;
        }
    }

    public P readP(Element element) {
        short type = P.typeToShort(element.getAttribute("type"));
        P p = new P(type);
        p.setSource(element.getAttribute("source"));
        readAtts(element, p);
        readContentList(p, element);
        return p;
    }

    public H readH(Element element) {
        int level = 1;
        try {
            level = Integer.parseInt(element.getAttribute("level"));
        } catch (NumberFormatException nfe) {
        }
        H h = new H(level);
        readAtts(element, h);
        readContentList(h, element);
        return h;
    }

    public Ul readUl(Element element) {
        Ul ul = null;
        NodeList nodeList = element.getChildNodes();
        for (int j = 0; j < nodeList.getLength(); j++) {
            if (nodeList.item(j).getNodeType() == Node.ELEMENT_NODE) {
                Element child = (Element) nodeList.item(j);
                String tagname = child.getTagName();
                if (tagname.equals("li")) {
                    Li li = readLi(child);
                    if (li != null) {
                        if (ul == null) {
                            ul = new Ul(li);
                        } else {
                            ul.add(li);
                        }
                    }
                }
            }
        }
        if (ul != null) {
            readAtts(element, ul);
        }
        return ul;
    }

    public Li readLi(Element element) {
        List<FicheBlock> list = readFicheBlockList(element);
        int size = list.size();
        if (size == 0) {
            return null;
        }
        Li li;
        FicheBlock firstFicheBlock = list.get(0);
        if (firstFicheBlock instanceof P) {
            li = new Li((P) firstFicheBlock);
        } else {
            li = new Li(new P());
            if (li.isValidFicheBlock(firstFicheBlock)) {
                li.add(firstFicheBlock);
            }
        }
        readAtts(element, li);
        for (int i = 1; i < size; i++) {
            FicheBlock ficheBlock = list.get(i);
            if (li.isValidFicheBlock(ficheBlock)) {
                li.add(ficheBlock);
            }
        }
        return li;
    }

    public Code readCode(Element element) {
        short type = Code.typeToShort(element.getAttribute("type"));
        Code code = new Code(type);
        NodeList nodeList = element.getChildNodes();
        for (int j = 0; j < nodeList.getLength(); j++) {
            if (nodeList.item(j).getNodeType() == Node.ELEMENT_NODE) {
                Element child = (Element) nodeList.item(j);
                String tagname = child.getTagName();
                if (tagname.equals("ln")) {
                    code.add(readLn(child));
                }
            }
        }
        readZoneBlock(code, element);
        return code;
    }

    public Table readTable(Element element) {
        Table table = new Table();
        NodeList nodeList = element.getChildNodes();
        for (int j = 0; j < nodeList.getLength(); j++) {
            if (nodeList.item(j).getNodeType() == Node.ELEMENT_NODE) {
                Element child = (Element) nodeList.item(j);
                String tagname = child.getTagName();
                if (tagname.equals("tr")) {
                    table.add(readTr(child));
                } else if (tagname.equals("caption")) {
                    String caption = XMLUtils.getData(child);
                    table.getLegendeBuilder().addText(caption);
                }
            }
        }
        readZoneBlock(table, element);
        return table;
    }

    public Tr readTr(Element element) {
        Tr tr = new Tr();
        readAtts(element, tr);
        NodeList nodeList = element.getChildNodes();
        for (int j = 0; j < nodeList.getLength(); j++) {
            if (nodeList.item(j).getNodeType() == Node.ELEMENT_NODE) {
                Element child = (Element) nodeList.item(j);
                String tagname = child.getTagName();
                if (tagname.equals("td")) {
                    tr.add(readTd(child));
                }
            }
        }
        return tr;
    }

    public Ln readLn(Element element) {
        int indentation = 0;
        try {
            indentation = Integer.parseInt(element.getAttribute("indent"));
            if (indentation < 0) {
                indentation = 0;
            }
        } catch (NumberFormatException nfe) {
        }
        Ln ln = new Ln(XMLUtils.getData(element, false), indentation);
        readAtts(element, ln);
        return ln;
    }

    public Td readTd(Element element) {
        String typeString = element.getAttribute("type");
        Td td = new Td(Td.typeToShort(typeString));
        readAtts(element, td);
        readContentList(td, element);
        return td;
    }

    public Insert readInsert(Element element) {
        Insert insert;
        try {
            short type = Insert.typeToShort(element.getAttribute("type"));
            insert = new Insert(type);
        } catch (IllegalArgumentException iae) {
            return null;
        }
        insert.setSrc(element.getAttribute("src"));
        insert.setRef(element.getAttribute("ref"));
        String creditString = element.getAttribute("credit");
        if (creditString.length() > 0) {
            insert.getCreditBuilder().addText(creditString);
        }
        insert.setPosition(Insert.positionToString(element.getAttribute("position")));
        try {
            int width = Integer.parseInt(element.getAttribute("width"));
            insert.setWidth(width);
        } catch (NumberFormatException nfe) {
        }
        try {
            int height = Integer.parseInt(element.getAttribute("height"));
            insert.setHeight(height);
        } catch (NumberFormatException nfe) {
        }
        String albumName = element.getAttribute("album");
        if (albumName.length() > 0) {
            try {
                SubsetKey albumKey = SubsetKey.parse(SubsetKey.CATEGORY_ALBUM, albumName);
                String idString = element.getAttribute("id");
                if (idString.length() == 0) {
                    idString = element.getAttribute("idalbum");
                }
                if (idString.length() > 0) {
                    try {
                        int illustrationId = Integer.parseInt(idString);
                        String albumDimName = element.getAttribute("albumdim");
                        insert.setSubsetItem(albumKey, illustrationId, albumDimName);
                    } catch (NumberFormatException nfe) {
                    }
                }
            } catch (ParseException pe) {
            }
        } else {
            String addendaName = element.getAttribute("addenda");
            if (addendaName.length() > 0) {
                try {
                    SubsetKey addendaKey = SubsetKey.parse(SubsetKey.CATEGORY_ADDENDA, addendaName);
                    String idString = element.getAttribute("id");
                    if (idString.length() > 0) {
                        try {
                            int documentId = Integer.parseInt(idString);
                            insert.setSubsetItem(addendaKey, documentId, "");
                        } catch (NumberFormatException nfe) {
                        }
                    }
                } catch (ParseException pe) {
                }
            }
        }
        readZoneBlock(insert, element);
        NodeList nodeList = element.getChildNodes();
        for (int j = 0; j < nodeList.getLength(); j++) {
            if (nodeList.item(j).getNodeType() == Node.ELEMENT_NODE) {
                Element child = (Element) nodeList.item(j);
                String tagname = child.getTagName();
                if (tagname.equals("alt")) {
                    readContentList(insert.getAltBuilder(), child);
                } else if (tagname.equals("credit")) {
                    readContentList(insert.getCreditBuilder(), child);
                }
            }
        }
        return insert;
    }

    public Div readDiv(Element element) {
        Div div = new Div();
        String langString = element.getAttribute("xml:lang");
        if (langString.length() > 0) {
            try {
                Lang lang = Lang.parse(langString);
                div.setLang(lang);
            } catch (ParseException pe) {
            }
        }
        readZoneBlock(div, element);
        NodeList nodeList = element.getChildNodes();
        for (int j = 0; j < nodeList.getLength(); j++) {
            if (nodeList.item(j).getNodeType() == Node.ELEMENT_NODE) {
                Element child = (Element) nodeList.item(j);
                String tagname = child.getTagName();
                if (tagname.equals("fbl")) {
                    List<FicheBlock> list = readFicheBlockList(child);
                    for (FicheBlock ficheBlock : list) {
                        div.add(ficheBlock);
                    }
                }
            }
        }
        return div;
    }

    public Cdatadiv readCdatadiv(Element element) {
        Cdatadiv cdatadiv = new Cdatadiv();
        readZoneBlock(cdatadiv, element);
        NodeList nodeList = element.getChildNodes();
        for (int j = 0; j < nodeList.getLength(); j++) {
            if (nodeList.item(j).getNodeType() == Node.ELEMENT_NODE) {
                Element child = (Element) nodeList.item(j);
                String tagname = child.getTagName();
                if (tagname.equals("cdata")) {
                    String cdata = XMLUtils.getRawData(child);
                    cdatadiv.setCdata(htmlCleaner.cleanHtml(cdata));
                    break;
                }
            }
        }
        return cdatadiv;
    }

    private void readZoneBlock(ZoneBlock zoneBlock, Element element) {
        readAtts(element, zoneBlock);
        NodeList liste = element.getChildNodes();
        for (int j = 0; j < liste.getLength(); j++) {
            if (liste.item(j).getNodeType() == Node.ELEMENT_NODE) {
                Element child = (Element) liste.item(j);
                String tagname = child.getTagName();
                if (tagname.equals("numero")) {
                    readContentList(zoneBlock.getNumeroBuilder(), child);
                } else if (tagname.equals("legende")) {
                    readContentList(zoneBlock.getLegendeBuilder(), child);
                }
            }
        }
    }

    public S readS(Element element) {
        short type;
        try {
            type = S.typeToShort(element.getAttribute("type"));
        } catch (IllegalArgumentException iae) {
            return null;
        }
        S s = new S(type);
        s.setValue(XMLUtils.getData(element, false));
        String ref = element.getAttribute("ref");
        if (type == S.IMAGE) {
            String src = element.getAttribute("src");
            if (src.length() > 0) {
                s.setRef(src);
                if (ref.length() > 0) {
                    s.putAtt("href", ref);
                }
            } else {
                s.setRef(ref);
            }
        } else {
            s.setRef(ref);
        }
        readSAtts(element, s);
        return s;
    }

    public void readContentList(TextContentBuilder pb, Element element) {
        NodeList list = element.getChildNodes();
        for (int i = 0; i < list.getLength(); i++) {
            short type = list.item(i).getNodeType();
            if (type == Node.TEXT_NODE) {
                String data = ((Text) list.item(i)).getData();
                data = XMLUtils.cleanString(data, false);
                if (data.length() > 0) {
                    pb.addText(data);
                }
            } else if (type == Node.ELEMENT_NODE) {
                Element child = (Element) list.item(i);
                if (child.getTagName().equals("s")) {
                    S span = readS(child);
                    if (span != null) {
                        pb.addS(span);
                    } else {
                        String data = XMLUtils.getData(child, false);
                        if (data.length() > 0) {
                            pb.addText(data);
                        }
                    }
                }
            }
        }
    }

    private void readAtts(Element element, AttConsumer attConsumer) {
        NamedNodeMap nodeMap = element.getAttributes();
        if (nodeMap != null) {
            int length = nodeMap.getLength();
            for (int i = 0; i < length; i++) {
                Attr attr = (Attr) nodeMap.item(i);
                String name = attr.getName();
                if ((name.startsWith("att-")) && (name.length() > 4)) {
                    attConsumer.putAtt(name.substring(4), attr.getValue());
                } else if (name.equals("class")) {
                    attConsumer.putAtt("class", attr.getValue());
                } else if (name.equals("htmlid")) {
                    attConsumer.putAtt("id", attr.getValue());
                } else if (name.equals("rowspan")) {
                    attConsumer.putAtt("rowspan", attr.getValue());
                } else if (name.equals("colspan")) {
                    attConsumer.putAtt("colspan", attr.getValue());
                }
            }
        }
    }

    private void readSAtts(Element element, AttConsumer attConsumer) {
        StringBuilder classBuf = new StringBuilder();
        NamedNodeMap nodeMap = element.getAttributes();
        if (nodeMap != null) {
            int length = nodeMap.getLength();
            for (int i = 0; i < length; i++) {
                Attr attr = (Attr) nodeMap.item(i);
                String name = attr.getName();
                if ((name.startsWith("att-")) && (name.length() > 4)) {
                    attConsumer.putAtt(name.substring(4), attr.getValue());
                } else if (name.equals("class")) {
                    if (classBuf.length() > 0) {
                        classBuf.append(' ');
                    }
                    classBuf.append(attr.getValue());
                } else if (name.equals("subtype")) {
                    if (classBuf.length() > 0) {
                        classBuf.append(' ');
                    }
                    classBuf.append(attr.getValue());
                } else if (name.equals("htmlid")) {
                    attConsumer.putAtt("id", attr.getValue());
                } else if (name.equals("title")) {
                    attConsumer.putAtt("title", attr.getValue());
                } else if (name.equals("lang")) {
                    attConsumer.putAtt("hreflang", attr.getValue());
                }
            }
        }
        if (classBuf.length() > 0) {
            attConsumer.putAtt("class", classBuf.toString());
        }
    }

}
