/* FichothequeLib_Tools - Copyright (c) 2020-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.format;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import net.fichotheque.corpus.fiche.Code;
import net.fichotheque.corpus.fiche.Div;
import net.fichotheque.corpus.fiche.FicheBlock;
import net.fichotheque.corpus.fiche.FicheBlocks;
import net.fichotheque.corpus.fiche.H;
import net.fichotheque.corpus.fiche.Li;
import net.fichotheque.corpus.fiche.Ln;
import net.fichotheque.corpus.fiche.P;
import net.fichotheque.corpus.fiche.ParagraphBlock;
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;


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

    private final static String LI_PREFIX = "\u2022 ";

    private FicheBlockFormat() {

    }

    public static void textFormat(Appendable appendable, FicheBlocks ficheBlocks, String separator) throws IOException {
        boolean next = false;
        for (FicheBlock ficheBlock : ficheBlocks) {
            textFormat(appendable, ficheBlock, separator, next);
            next = true;
        }
    }

    private static void textFormat(Appendable buf, FicheBlock ficheBlock, String separator, boolean next) throws IOException {
        if (next) {
            buf.append(separator);
        }
        if (ficheBlock instanceof P) {
            addParagraphBlock(buf, (P) ficheBlock, null);
        } else if (ficheBlock instanceof H) {
            addParagraphBlock(buf, (H) ficheBlock, (next) ? separator : null);
            buf.append(separator);
        } else if (ficheBlock instanceof Code) {
            Code code = (Code) ficheBlock;
            int size2 = code.size();
            buf.append(separator);
            buf.append("+++++");
            buf.append(separator);
            for (int j = 0; j < size2; j++) {
                if (j > 0) {
                    buf.append(separator);
                }
                addLn(buf, code.get(j));
            }
            buf.append(separator);
            buf.append("+++++");
            buf.append(separator);
        } else if (ficheBlock instanceof Table) {
            Table table = (Table) ficheBlock;
            buf.append(separator);
            buf.append("=====");
            buf.append(separator);
            int size2 = table.size();
            for (int j = 0; j < size2; j++) {
                if (j > 0) {
                    buf.append(separator);
                }
                Tr tr = table.get(j);
                addTr(buf, tr);
            }
            buf.append(separator);
            buf.append("=====");
            buf.append(separator);

        } else if (ficheBlock instanceof Ul) {
            addUl(buf, (Ul) ficheBlock, 0, separator);
        } else if (ficheBlock instanceof Div) {
            buf.append(separator);
            buf.append("!!!!!");
            buf.append(separator);
            textFormat(buf, ((Div) ficheBlock), separator);
            buf.append(separator);
            buf.append("!!!!!");
            buf.append(separator);
        }
    }

    public static List<String> valuesFormat(FicheBlocks ficheBlocks) {
        List<String> values = new ArrayList<String>();
        ValuesBuilder vb = new ValuesBuilder(values);
        vb.addAll(ficheBlocks);
        return values;
    }

    private static void addLn(Appendable buf, Ln ln) throws IOException {
        int indentation = ln.getIndentation();
        for (int i = 0; i < indentation; i++) {
            buf.append('\u00A0');
        }
        buf.append(ln.getValue());
    }

    private static void addUl(Appendable buf, Ul ul, int depth, String separator) throws IOException {
        int liSize = ul.size();
        for (int j = 0; j < liSize; j++) {
            if (j > 0) {
                buf.append(separator);
            }
            Li li = ul.get(j);
            addLi(buf, li, depth, separator);
        }
    }

    private static void addTr(Appendable buf, Tr tr) throws IOException {
        buf.append('|');
        for (Td td : tr) {
            for (Object obj : td) {
                buf.append(obj.toString());
            }
            buf.append('|');
        }
    }

    private static void addLi(Appendable buf, Li li, int depth, String separator) throws IOException {
        int liSize = li.size();
        boolean premier = true;
        StringBuilder preBuf = new StringBuilder();
        for (int i = 0; i < depth; i++) {
            preBuf.append('\u00A0');
        }
        String pre = preBuf.toString();
        for (int j = 0; j < liSize; j++) {
            if (j > 0) {
                buf.append(separator);
            }
            FicheBlock prg = li.get(j);
            if (prg instanceof P) {
                if (premier) {
                    premier = false;
                    addParagraphBlock(buf, (P) prg, pre + "– ");
                } else {
                    addParagraphBlock(buf, (P) prg, pre + "\u00A0 ");
                }
            } else if (prg instanceof Ul) {
                addUl(buf, (Ul) prg, depth + 1, separator);
            }
        }
    }

    private static void addParagraphBlock(Appendable buf, ParagraphBlock paragraphBlock, String pre) throws IOException {
        if (pre != null) {
            buf.append(pre);
        }
        for (Object obj : paragraphBlock) {
            buf.append(obj.toString());
        }
    }


    private static class ValuesBuilder {

        private final List<String> values;

        private ValuesBuilder(List<String> values) {
            this.values = values;
        }

        private void addAll(FicheBlocks ficheBlocks) {
            for (FicheBlock ficheBlock : ficheBlocks) {
                if (ficheBlock instanceof P) {
                    addTextContent((P) ficheBlock, "");
                } else if (ficheBlock instanceof H) {
                    addTextContent((H) ficheBlock, "");
                } else if (ficheBlock instanceof Code) {
                    for (Ln ln : (Code) ficheBlock) {
                        addTextContent(ln, "");
                    }
                } else if (ficheBlock instanceof Table) {
                    for (Tr tr : (Table) ficheBlock) {
                        addTr(tr);
                    }
                } else if (ficheBlock instanceof Ul) {
                    addUl((Ul) ficheBlock, 0);
                } else if (ficheBlock instanceof Div) {
                    addAll((Div) ficheBlock);
                }
                if (ficheBlock instanceof ZoneBlock) {
                    addTextContent(((ZoneBlock) ficheBlock).getLegende(), "");
                }
            }
        }

        private void addTextContent(TextContent textContent, String prefix) {
            if (textContent.isEmpty()) {
                return;
            }
            StringBuilder buf = new StringBuilder();
            int length = prefix.length();
            if (length > 0) {
                buf.append(prefix);
            }
            for (Object obj : textContent) {
                buf.append(obj.toString());
            }
            if (buf.length() > length) {
                values.add(buf.toString());
            }
        }

        private void addTr(Tr tr) {
            StringBuilder buf = new StringBuilder();
            boolean done = false;
            buf.append('|');
            for (Td td : tr) {
                for (Object obj : td) {
                    String s = obj.toString();
                    if (s.length() > 0) {
                        buf.append(s);
                        done = true;
                    }
                }
                buf.append('|');
            }
            if (done) {
                values.add(buf.toString());
            }
        }

        private void addUl(Ul ul, int depth) {
            for (Li li : ul) {
                addLi(li, depth);
            }
        }

        private void addLi(Li li, int depth) {
            for (FicheBlock ficheBlock : li) {
                if (ficheBlock instanceof P) {
                    addTextContent((P) ficheBlock, LI_PREFIX);
                } else if (ficheBlock instanceof Ul) {
                    addUl((Ul) ficheBlock, depth + 1);
                }
            }
        }

    }

}
