/* BdfServer_DirectoryStorage - Copyright (c) 2010-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 fr.exemole.bdfserver.storage.directory.jdbm;

import fr.exemole.bdfserver.api.storage.BdfStorageException;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.text.ParseException;
import java.util.HashMap;
import java.util.Map;
import jdbm.RecordManager;
import jdbm.helper.Serializer;
import net.fichotheque.corpus.fiche.Info;
import net.fichotheque.corpus.fiche.Prop;
import net.fichotheque.corpus.fiche.Section;
import net.fichotheque.corpus.metadata.FieldKey;


/**
 *
 * @author Vincent Calame
 */
public final class FieldKeyCoder implements Serializable {

    private static final ClassSerializer classSerializer = new ClassSerializer();
    private final Map<FieldKey, Integer> codeMap = new HashMap<FieldKey, Integer>();
    private final Map<Integer, FieldKey> fieldKeyMap = new HashMap<Integer, FieldKey>();
    private int lastCode = 0;
    private boolean withChange = false;

    private FieldKeyCoder() {
    }

    public int askForCode(Prop prop) {
        FieldKey fieldKey = prop.getFieldKey();
        Integer itg = codeMap.get(fieldKey);
        if (itg == null) {
            itg = createCode(fieldKey);
        }
        return itg;
    }

    public int askForCode(Info info) {
        FieldKey fieldKey = info.getFieldKey();
        Integer itg = codeMap.get(fieldKey);
        if (itg == null) {
            itg = createCode(fieldKey);
        }
        return itg;
    }

    public int askForCode(Section section) {
        FieldKey fieldKey = section.getFieldKey();
        Integer itg = codeMap.get(fieldKey);
        if (itg == null) {
            itg = createCode(fieldKey);
        }
        return itg;
    }

    public int askForCode(FieldKey fieldKey) {
        Integer itg = codeMap.get(fieldKey);
        if (itg == null) {
            itg = createCode(fieldKey);
        }
        return itg;
    }

    private Integer createCode(FieldKey fieldKey) {
        lastCode++;
        Integer itg = lastCode;
        codeMap.put(fieldKey, itg);
        fieldKeyMap.put(itg, fieldKey);
        withChange = true;
        return itg;
    }

    public FieldKey getFieldKey(int code) {
        return fieldKeyMap.get(code);
    }

    public boolean isWithChange() {
        return withChange;
    }

    public void save(RecordManager recordManager, String objectName) throws IOException {
        long recordId = recordManager.getNamedObject(objectName);
        recordManager.update(recordId, this, classSerializer);
        withChange = false;
    }

    private void add(FieldKey fieldKey, int code) {
        lastCode = Math.max(lastCode, code);
        Integer itg = code;
        codeMap.put(fieldKey, itg);
        fieldKeyMap.put(itg, fieldKey);
    }

    private byte[] serialize() throws IOException {
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        DataOutputStream dataOutput = new DataOutputStream(output);
        dataOutput.writeInt(codeMap.size());
        for (Map.Entry<FieldKey, Integer> entry : codeMap.entrySet()) {
            dataOutput.writeUTF(entry.getKey().getKeyString());
            dataOutput.writeInt(entry.getValue());
        }
        dataOutput.flush();
        return output.toByteArray();
    }

    public static FieldKeyCoder init(RecordManager recordManager, String objectName) throws IOException {
        long recordId = recordManager.getNamedObject(objectName);
        if (recordId == 0) {
            FieldKeyCoder coder = new FieldKeyCoder();
            recordId = recordManager.insert(coder, classSerializer);
            recordManager.setNamedObject(objectName, recordId);
            return coder;
        } else {
            FieldKeyCoder coder = (FieldKeyCoder) recordManager.fetch(recordId, classSerializer);
            return coder;
        }
    }


    private static class ClassSerializer implements Serializer {

        private ClassSerializer() {

        }

        @Override
        public byte[] serialize(Object obj) throws IOException {
            FieldKeyCoder coder = (FieldKeyCoder) obj;
            return coder.serialize();
        }

        @Override
        public Object deserialize(byte[] serialized) {
            ByteArrayInputStream input = new ByteArrayInputStream(serialized);
            DataInputStream dataInput = new DataInputStream(input);
            try {
                FieldKeyCoder coder = new FieldKeyCoder();
                int size = dataInput.readInt();
                for (int i = 0; i < size; i++) {
                    String fieldKeyString = dataInput.readUTF();
                    try {
                        FieldKey fieldKey = FieldKey.parse(fieldKeyString);
                        int code = dataInput.readInt();
                        coder.add(fieldKey, code);
                    } catch (ParseException pe) {
                        throw new BdfStorageException(pe);
                    }
                }
                return coder;
            } catch (IOException ioe) {
                throw new BdfStorageException(ioe);
            }
        }

    }

}
