package org.wikidata.wdtk.datamodel.helpers;

/*-
 * #%L
 * Wikidata Toolkit Data Model
 * %%
 * Copyright (C) 2014 - 2022 Wikidata Toolkit Developers
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */

import com.fasterxml.jackson.core.JacksonException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.fasterxml.jackson.databind.exc.InvalidFormatException;
import com.fasterxml.jackson.databind.node.NullNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.type.TypeFactory;
import org.wikidata.wdtk.datamodel.implementation.*;
import org.wikidata.wdtk.datamodel.interfaces.FormDocument;
import org.wikidata.wdtk.datamodel.interfaces.LexemeDocument;
import org.wikidata.wdtk.datamodel.interfaces.MonolingualTextValue;
import org.wikidata.wdtk.datamodel.interfaces.SenseDocument;

import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * Custom deserializer, very close to the default serializer generated by Jackson,
 * only to also accept empty json objects ({}) in place of empty lists for the list of senses.
 * https://github.com/Wikidata/Wikidata-Toolkit/issues/568
 */
public class LexemeDeserializer extends StdDeserializer<LexemeDocumentImpl> {

    public LexemeDeserializer() {
        super(LexemeDocumentImpl.class);
    }

    @Override
    public LexemeDocumentImpl deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JacksonException {
        JsonNode node = jsonParser.getCodec().readTree(jsonParser);
        if (! (node instanceof ObjectNode)) {
            throw new IOException("Deserializing a lexeme can only be done from a JSON object");
        }
        ObjectNode object = (ObjectNode) node;

        String jsonId = object.get("id").asText();
        String lexicalCategory = object.get("lexicalCategory").asText();
        String language = object.get("language").asText();
        TypeFactory typeFactory = deserializationContext.getTypeFactory();

        JsonNode lemmas1 = object.get("lemmas");
        Map<String, TermImpl> preLemmas = deserializationContext.readTreeAsValue(
                lemmas1,
                typeFactory.constructMapType(Map.class, String.class, TermImpl.class));
        Map<String, MonolingualTextValue> lemmas = null;
        if (preLemmas != null) {
            lemmas = preLemmas.entrySet()
                    .stream()
                    .collect(Collectors.toMap(Map.Entry::getKey, v -> v.getValue()));
        }

        Map<String, List<StatementImpl.PreStatement>> claims = deserializationContext.readTreeAsValue(
                object.get("claims"),
                typeFactory.constructMapType(Map.class, typeFactory.constructType(String.class),
                        typeFactory.constructCollectionType(List.class, StatementImpl.PreStatement.class)));

        List<FormDocument> forms;
        if (object.get("forms") instanceof ObjectNode && ((ObjectNode)object.get("forms")).isEmpty()) {
            forms = Collections.emptyList();
        } else {
            forms = deserializationContext.readTreeAsValue(
                    object.get("forms"),
                    typeFactory.constructCollectionType(List.class, FormDocumentImpl.class));
        }

        List<SenseDocument> senses;
        if (object.get("senses") instanceof ObjectNode && ((ObjectNode)object.get("senses")).isEmpty()) {
            // case for https://github.com/Wikidata/Wikidata-Toolkit/issues/568
            senses = Collections.emptyList();
        } else {
            senses = deserializationContext.readTreeAsValue(
                    object.get("senses"),
                    typeFactory.constructCollectionType(List.class, SenseDocumentImpl.class));
        }

        long lastrevid = 0;
        if (object.has("lastrevid")) {
            lastrevid = object.get("lastrevid").asLong();
        }
        String siteIri = (String) deserializationContext.findInjectableValue("siteIri", null , null);
        return new LexemeDocumentImpl(
                jsonId,
                lexicalCategory,
                language,
                lemmas,
                claims,
                forms,
                senses,
                lastrevid,
                siteIri
        );
    }
}
