package eu.dnetlib.wds.proto;

import com.googlecode.protobuf.format.JsonFormat;
import eu.dnetlib.data.graph.model.DNGFRelDecoder;
import eu.dnetlib.data.graph.utils.RelDescriptor;
import eu.dnetlib.data.proto.*;
import eu.dnetlib.data.proto.DatasetProtos.Dataset;
import eu.dnetlib.data.proto.PublicationProtos.Publication;
import eu.dnetlib.data.proto.WDSIndexItemProtos;
import eu.dnetlib.data.proto.WDSIndexItemProtos.GeoLocation;
import eu.dnetlib.data.proto.WDSIndexItemProtos.Item;
import eu.dnetlib.data.proto.WdsDatasetProtos.WdsDataset;
import eu.dnetlib.data.proto.WdsDatasetProtos.otherRelation;
import eu.dnetlib.data.proto.WdsPublicationProtos.WdsPublication;
import org.apache.commons.lang3.StringUtils;

import java.util.List;

/**
 * Created by miriam on 12/12/2017.
 */
public class DNGFWDSIndexConverter {

    public Item.Builder convert(final DNGFProtos.DNGF mainDNGFEntity) {
        if (!isValid(mainDNGFEntity)) {
            return null;
        }
        final DNGFProtos.DNGFEntity entity = mainDNGFEntity.getEntity();
        final Item.Builder indexItem = Item.newBuilder();
        final TypeProtos.Type itemType = entity.getType();

        indexItem.setId(entity.getId());
        addIdentifier(indexItem, entity.getPidList());
        indexItem.setDateofcollection(entity.getDateofcollection());

        switch (itemType) {
            case dataset:
                setDatasetValues(entity, indexItem);
                break;
            case publication:
                setPublicationValues(entity, indexItem);
                break;
        }

        return indexItem;
    }

    public void addRelation(final Item.Builder item, final DNGFProtos.DNGFRel rel) {
        final WDSIndexItemProtos.RelationType.Builder currentRel = WDSIndexItemProtos.RelationType.newBuilder();
        RelDescriptor relDescriptor = DNGFRelDecoder.decode(rel).getRelDescriptor();
        currentRel.setSemantic(relDescriptor.getTermCode());
        currentRel.setId(WDSIndexItemProtos.Identifier.newBuilder()
                .setId(relDescriptor.getTargetId())
                .setType("dnet")
                .build());
        item.addRelations(currentRel);
    }

    public static String convertAsJson(final Item.Builder currentItem) {
        return JsonFormat.printToString(currentItem.build());
    }

    private void setPublicationValues(final DNGFProtos.DNGFEntity entity, final Item.Builder indexItem) {
        final Publication p = entity.getPublication();
        final Publication.Metadata p_metadata = p.getMetadata();

        indexItem.setType("publication");
        addSubject(indexItem, p_metadata.getSubjectList());
        addTitle(indexItem, p_metadata.getTitleList());
        addDex(indexItem, p_metadata.getDescriptionList());

        final String dateOfAcceptance = p_metadata.getDateofacceptance().getValue();
        if (dateOfAcceptance != null)
            addDate(indexItem, dateOfAcceptance, "date of acceptance");

        final String embargoEndDate = p_metadata.getEmbargoenddate().getValue();
        if (embargoEndDate != null)
            addDate(indexItem, embargoEndDate, "embargo end date");

        final List<FieldTypeProtos.StructuredProperty> relevantDate = p_metadata.getRelevantdateList();
        if (relevantDate != null)
            relevantDate.forEach(it -> addDate(indexItem, it.getValue(), "relevant date"));

        addAuthors(indexItem, p.getAuthorList());
        addContributors(indexItem, p_metadata.getContributorList());
        addProjectRels(indexItem, p_metadata.getExtension(WdsPublication.projects));

    }

    private void setDatasetValues(DNGFProtos.DNGFEntity entity, final Item.Builder indexItem) {

        final Dataset ds = entity.getDataset();
        final Dataset.Metadata ds_metadata = ds.getMetadata();

        indexItem.setType("dataset");
        List<FieldTypeProtos.StructuredProperty> subjectList = ds_metadata.getSubjectList();
        addSubject(indexItem, subjectList);


        List<FieldTypeProtos.StructuredProperty> titleList = ds_metadata.getTitleList();
        addTitle(indexItem, titleList);


        List<FieldTypeProtos.StringField> descriptionList = ds_metadata.getDescriptionList();
        addDex(indexItem, descriptionList);

        final String dateOfAcceptance = ds_metadata.getDateofacceptance().getValue();

        addDate(indexItem, dateOfAcceptance, "date of acceptance");

        final String embargoEndDate = ds_metadata.getEmbargoenddate().getValue();

        addDate(indexItem, embargoEndDate, "embargo end date");

        final List<FieldTypeProtos.StructuredProperty> relevantDate = ds_metadata.getRelevantdateList();

        relevantDate.forEach(it -> addDate(indexItem, it.getValue(), "relevant date"));

        String value = ds_metadata.getSize().getValue();

        indexItem.setSize(value);

        if (ds_metadata.getFormatList() != null && ds_metadata.getFormatList().size() > 0)
            indexItem.setFormat(ds_metadata.getFormat(0).getValue());

        if (ds_metadata.getVersion() != null)
            indexItem.setVersion(ds_metadata.getVersion().getValue());

        List<PersonProtos.Person> authorList = ds.getAuthorList();
        if (authorList != null)
            addAuthors(indexItem, authorList);

        List<FieldTypeProtos.StringField> contributorList = ds_metadata.getContributorList();
        if (contributorList != null)
            addContributors(indexItem, contributorList);

        List<WdsDataset.GeoLocation> geopoints = ds_metadata.getExtension(WdsDataset.geolocation);
        if (geopoints != null) {
            geopoints.forEach(it -> addGeolocation(indexItem, it));
        }

        addRelations(indexItem, ds_metadata.getExtension(WdsDataset.otherRels));
        addProjectRels(indexItem, ds_metadata.getExtension(WdsDataset.projects));

    }

    public boolean isValid(final DNGFProtos.DNGF mainEntity) {
        return mainEntity != null;//&& !(this.relatedUnknown == 0 && this.relatedDatasets == 0 && this.relatedPublications == 0);
    }

    private void addIdentifier(final Item.Builder indexItem, List<FieldTypeProtos.StructuredProperty> pidList) {
        pidList.forEach(it -> indexItem.addOriginalIdentifier(WDSIndexItemProtos.Identifier.newBuilder()
                .setId(it.getValue())
                .setType(it.getQualifier().getClassid())
                .build()
        ));
    }

    private void addSubject(final Item.Builder indexItem, final List<FieldTypeProtos.StructuredProperty> subjectList) {
        if (subjectList != null)
            subjectList.forEach(it -> indexItem.addSubjects(WDSIndexItemProtos.SubjectType.newBuilder()
                    .setValue(it.getValue())
                    .setScheme(it.getQualifier().getClassid())
                    .build()
            ));
    }

    private void addTitle(final Item.Builder indexItem, final List<FieldTypeProtos.StructuredProperty> titleList) {
        if (titleList != null) {
            titleList.forEach(it -> {
                if (StringUtils.isNotBlank(it.getValue()))
                    indexItem.addTitle(it.getValue());
            });
        }
    }

    private void addDex(final Item.Builder indexItem, final List<FieldTypeProtos.StringField> dexList) {
        if (dexList != null) {
            dexList.forEach(it -> {
                if (StringUtils.isNotBlank(it.getValue()))
                    indexItem.addDescription(it.getValue());
            });
        }
    }

    private void addDate(final Item.Builder indexItem, final String date, final String date_type) {
        if (StringUtils.isNotBlank(date))
            indexItem.addDate(WDSIndexItemProtos.DateType.newBuilder().setDate(date).setType(date_type).build());
    }

    private void addProjectRels(final Item.Builder indexItem, List<FieldTypeProtos.ProjectRelation> prjRels) {
        prjRels.forEach(it ->
                {
                    if (prjRels != null)
                        indexItem.addProjects(FieldTypeProtos.ProjectRelation.newBuilder()
                                .setGrantId(it.getGrantId())
                                .setFunder(it.getFunder())
                                .setFundingStream(it.getFundingStream())
                                .setAcronym(it.getAcronym())
                                .setName(it.getName())
                                .build());
                }
        );
    }

    private void addRelations(final Item.Builder indexItem, List<otherRelation> rels) {
        rels.forEach(it -> indexItem.addRelations(WDSIndexItemProtos.RelationType.newBuilder()
                .setSemantic(it.getRelationSemantic())
                .setId(WDSIndexItemProtos.Identifier.newBuilder()
                        .setId(it.getTarget().getId())
                        .setType(it.getTarget().getType())
                        .build())
                .build()));
    }

    private void addGeolocation(final Item.Builder indexItem, WdsDataset.GeoLocation g) {
        final GeoLocation.Builder glb = GeoLocation.newBuilder();
        if (g.hasPoint())
            glb.setPoint(g.getPoint());
        if (g.hasPlace())
            glb.setPoint(g.getPlace());
        if (g.getBoxCount() > 0)
            for (int i = 0; i < g.getBoxCount(); i++)
                glb.addBox(g.getBox(i));
        indexItem.addGeolocation(glb.build());
    }

    private void addContributors(final Item.Builder indexItem, List<FieldTypeProtos.StringField> contributors) {
        contributors.forEach(it -> indexItem.addContributor(addContributor("contributor", it.getValue())));
    }

    private void addAuthors(final Item.Builder indexItem, List<PersonProtos.Person> authors) {
        authors.forEach(it -> indexItem.addContributor(addContributor("author", it.getMetadata().getFullname().getValue())));
    }

    private WDSIndexItemProtos.ContributorType addContributor(String type, String value) {
        return WDSIndexItemProtos.ContributorType.newBuilder().setType(type).setValue(value).build();
    }
}
