package org.mercury_im.messenger.data.mapping;

import org.mercury_im.messenger.util.Optional;

import lombok.NonNull;

public abstract class AbstractMapping<E, M> implements Mapping<E, M> {

    @Override
    public M toModel(E entity) {
        if (entity == null) {
            return null;
        }
        return toModel(entity, newModel(entity));
    }

    @Override
    public Optional<M> toModel(Optional<E> entity) {
        if (entity.isPresent()) {
            return new Optional<>(toModel(entity.getItem()));
        } else {
            return new Optional<>();
        }
    }

    @Override
    public E toEntity(M model) {
        if (model == null) {
            return null;
        }
        return toEntity(model, newEntity(model));
    }

    @Override
    public Optional<E> toEntity(Optional<M> model) {
        if (model.isPresent()) {
            return new Optional<>(toEntity(model.getItem()));
        } else {
            return new Optional<>();
        }
    }

    @Override
    public M toModel(E entity, M model) {
        if (entity == null) {
            return null;
        }
        if (model == null) {
            model = newModel(entity);
        }
        return mapToModel(entity, model);
    }

    @Override
    public E toEntity(M model, E entity) {
        if (model == null) {
            return null;
        }
        if (entity == null) {
            entity = newEntity(model);
        }
        return mapToEntity(model, entity);
    }

    /**
     * Return a new entity object.
     *
     * @return entity
     */
    protected abstract E newEntity(@NonNull M model);

    /**
     * Return a new database model object.
     *
     * @return model
     */
    protected abstract M newModel(@NonNull E entity);

    /**
     * Copy data from the entity to the given model.
     *
     * @param entity application entity
     * @param model database model
     * @return the database model
     */
    protected abstract M mapToModel(@NonNull E entity, @NonNull M model);

    /**
     * Copy data from the database model to the entity.
     *
     * @param model database model
     * @param entity entity
     * @return the application entity
     */
    protected abstract E mapToEntity(@NonNull M model, @NonNull E entity);
}
