package io.delphiplatform.api.v3.bigtable.processing;

import org.openapitools.jackson.nullable.JsonNullable;
import org.springframework.stereotype.Service;

import java.util.List;

import io.delphiplatform.api.util.model.ModelUtils;
import io.delphiplatform.api.v3.model.AppleAgeBand;
import io.delphiplatform.api.v3.model.Gender;
import io.delphiplatform.api.v3.model.SpotifyAgeBand;
import io.delphiplatform.api.v3.model.StreamModel;
import io.delphiplatform.api.v3.model.amazon.AmazonDeviceType;
import io.delphiplatform.api.v3.model.amazon.AmazonEngagement;
import io.delphiplatform.api.v3.model.amazon.AmazonOperatingSystem;
import io.delphiplatform.api.v3.model.amazon.AmazonReferralSourceType;
import io.delphiplatform.api.v3.model.amazon.AmazonSelectionSourceType;
import io.delphiplatform.api.v3.model.amazon.AmazonStreamsInfo;
import io.delphiplatform.api.v3.model.amazon.AmazonSubscriptionType;
import io.delphiplatform.api.v3.model.apple.AppleContainerSubType;
import io.delphiplatform.api.v3.model.apple.AppleContainerType;
import io.delphiplatform.api.v3.model.apple.AppleDeviceType;
import io.delphiplatform.api.v3.model.apple.AppleListenerEngagement;
import io.delphiplatform.api.v3.model.apple.AppleOs;
import io.delphiplatform.api.v3.model.apple.AppleSource;
import io.delphiplatform.api.v3.model.apple.AppleStreamsInfo;
import io.delphiplatform.api.v3.model.apple.AppleUserType;
import io.delphiplatform.api.v3.model.spotify.SpotifyDeviceType;
import io.delphiplatform.api.v3.model.spotify.SpotifyEngagement;
import io.delphiplatform.api.v3.model.spotify.SpotifyOperatingSystem;
import io.delphiplatform.api.v3.model.spotify.SpotifySaves;
import io.delphiplatform.api.v3.model.spotify.SpotifySource;
import io.delphiplatform.api.v3.model.spotify.SpotifyStreamsInfo;

import static io.delphiplatform.api.util.model.ModelUtils.addAndReplaceZeroWithNull;
import static io.delphiplatform.api.util.model.ModelUtils.merge;

@Service
public class StreamsMergingService {

    public JsonNullable<AppleAgeBand> mergeAppleAgeBand(List<StreamModel> appleAgeBandStream) {
        return appleAgeBandStream.parallelStream()
            .map(StreamModel::getAppleAgeBands)
            .reduce(JsonNullable.undefined(), (a, b) -> merge(a, b, this::add));
    }

    public JsonNullable<SpotifyAgeBand> mergeSpotifyAgeBand(List<StreamModel> appleAgeBandStream) {
        return appleAgeBandStream.parallelStream()
            .map(StreamModel::getSpotifyAgeBands)
            .reduce(JsonNullable.undefined(), (a, b) -> merge(a, b, this::add));
    }

    public JsonNullable<Gender> mergeGender(List<StreamModel> appleAgeBandStream) {
        return appleAgeBandStream.parallelStream()
            .map(StreamModel::getGenders)
            .reduce(JsonNullable.undefined(), (a, b) -> merge(a, b, this::add));
    }

    public JsonNullable<AppleStreamsInfo> mergeAppleStreamInfo(List<StreamModel> group) {
        return group.parallelStream()
            .map(StreamModel::getAppleStreamsInfo)
            .reduce(JsonNullable.undefined(), (a, b) -> merge(a, b, this::add));
    }

    public JsonNullable<AmazonStreamsInfo> mergeAmazonStreamInfo(List<StreamModel> group) {
        return group.parallelStream()
            .map(StreamModel::getAmazonStreamsInfo)
            .reduce(JsonNullable.undefined(), (a, b) -> merge(a, b, this::add));
    }

    public JsonNullable<SpotifyStreamsInfo> mergeSpotifyStreamInfo(List<StreamModel> group) {
        return group.parallelStream()
            .map(StreamModel::getSpotifyStreamsInfo)
            .reduce(JsonNullable.undefined(), (a, b) -> merge(a, b, this::add));
    }

    public JsonNullable<SpotifySaves> mergeSpotifySaves(List<StreamModel> group) {
        return group.stream()
            .map(s -> s.getSpotifyStreamsInfo().orElse(new SpotifyStreamsInfo()).getSaves())
            .reduce(JsonNullable.undefined(), (a, b) -> merge(a, b, this::add));
    }

    public JsonNullable<Long> mergeSpotifySkips(List<StreamModel> group) {
        return group.stream()
            .map(s -> s.getSpotifyStreamsInfo().orElse(new SpotifyStreamsInfo()).getSkips())
            .reduce(JsonNullable.undefined(), (a, b) -> merge(a, b, ModelUtils::addAndReplaceZeroWithNull));
    }

    public JsonNullable<Long> mergeAppleSkips(List<StreamModel> group) {
        return group.stream()
            .map(s -> s.getAppleStreamsInfo().orElse(new AppleStreamsInfo()).getSkips())
            .reduce(JsonNullable.undefined(), (a, b) -> merge(a, b, ModelUtils::addAndReplaceZeroWithNull));
    }

    private AppleStreamsInfo add(AppleStreamsInfo info1, AppleStreamsInfo info2) {
        AppleStreamsInfo sum = new AppleStreamsInfo();
        sum.setOfflinePlay(addAndReplaceZeroWithNull(info1.getOfflinePlay(), info2.getOfflinePlay()));
        sum.setCompletionPlay(addAndReplaceZeroWithNull(info1.getCompletionPlay(), info2.getCompletionPlay()));
        sum.setSkips(addAndReplaceZeroWithNull(info1.getSkips(), info2.getSkips()));
        sum.setStreams(addAndReplaceZeroWithNull(info1.getStreams(), info2.getStreams()));
        sum.setNonRoyaltyStreams(addAndReplaceZeroWithNull(info1.getNonRoyaltyStreams(), info2.getNonRoyaltyStreams()));
        sum.setListeners(addAndReplaceZeroWithNull(info1.getListeners(), info2.getListeners()));
        sum.setSource(merge(info1.getSource(), info2.getSource(), this::add));
        sum.setContainerType(merge(info1.getContainerType(), info2.getContainerType(), this::add));
        sum.setContainerSubType(merge(info1.getContainerSubType(), info2.getContainerSubType(), this::add));
        sum.setDeviceType(merge(info1.getDeviceType(), info2.getDeviceType(), this::add));
        sum.setOs(merge(info1.getOs(), info2.getOs(), this::add));
        sum.setUserType(merge(info1.getUserType(), info2.getUserType(), this::add));
        sum.setListenerEngagement(merge(info1.getListenerEngagement(), info2.getListenerEngagement(), this::add));
        return sum;
    }

    private AmazonStreamsInfo add(AmazonStreamsInfo info1, AmazonStreamsInfo info2) {
        AmazonStreamsInfo sum = new AmazonStreamsInfo();
        sum.setNonRoyaltyStreams(addAndReplaceZeroWithNull(info1.getNonRoyaltyStreams(), info2.getNonRoyaltyStreams()));
        sum.setDeviceType(merge(info1.getDeviceType(), info2.getDeviceType(), this::add));
        sum.setEngagement(merge(info1.getEngagement(), info2.getEngagement(), this::add));
        sum.setOperatingSystem(merge(info1.getOperatingSystem(), info2.getOperatingSystem(), this::add));
        sum.setReferralSourceType(merge(info1.getReferralSourceType(), info2.getReferralSourceType(), this::add));
        sum.setSelectionSourceType(merge(info1.getSelectionSourceType(), info2.getSelectionSourceType(), this::add));
        sum.setSubscriptionType(merge(info1.getSubscriptionType(), info2.getSubscriptionType(), this::add));
        return sum;
    }

    private SpotifyStreamsInfo add(SpotifyStreamsInfo info1, SpotifyStreamsInfo info2) {
        SpotifyStreamsInfo sum = new SpotifyStreamsInfo();

        sum.setFreeStreams(addAndReplaceZeroWithNull(info1.getFreeStreams(), info2.getFreeStreams()));
        sum.setPaidStreams(addAndReplaceZeroWithNull(info1.getPaidStreams(), info2.getPaidStreams()));
        sum.setRepeatPlay(addAndReplaceZeroWithNull(info1.getRepeatPlay(), info2.getRepeatPlay()));
        sum.setShufflePlay(addAndReplaceZeroWithNull(info1.getShufflePlay(), info2.getShufflePlay()));
        sum.setOfflinePlay(addAndReplaceZeroWithNull(info1.getOfflinePlay(), info2.getOfflinePlay()));
        sum.setCompletionPlay(addAndReplaceZeroWithNull(info1.getCompletionPlay(), info2.getCompletionPlay()));
        sum.setSkips(addAndReplaceZeroWithNull(info1.getSkips(), info2.getSkips()));

        sum.setSource(merge(info1.getSource(), info2.getSource(), this::add));
        sum.setDeviceType(merge(info1.getDeviceType(), info2.getDeviceType(), this::add));
        sum.setOperatingSystem(merge(info1.getOperatingSystem(), info2.getOperatingSystem(), this::add));
        sum.setEngagement(merge(info1.getEngagement(), info2.getEngagement(), this::add));
        sum.setSaves(merge(info1.getSaves(), info2.getSaves(), this::add));

        return sum;
    }

    private AppleSource add(AppleSource info1, AppleSource info2) {
        AppleSource sum = new AppleSource();
        sum.setLibrary(addAndReplaceZeroWithNull(info1.getLibrary(), info2.getLibrary()));
        sum.setSearch(addAndReplaceZeroWithNull(info1.getSearch(), info2.getSearch()));
        sum.setDiscovery(addAndReplaceZeroWithNull(info1.getDiscovery(), info2.getDiscovery()));
        sum.setMusicKit(addAndReplaceZeroWithNull(info1.getMusicKit(), info2.getMusicKit()));
        sum.setExternal(addAndReplaceZeroWithNull(info1.getExternal(), info2.getExternal()));
        sum.setNowPlaying(addAndReplaceZeroWithNull(info1.getNowPlaying(), info2.getNowPlaying()));
        sum.setVoice(addAndReplaceZeroWithNull(info1.getVoice(), info2.getVoice()));
        sum.setOther(addAndReplaceZeroWithNull(info1.getOther(), info2.getOther()));
        return sum;
    }

    private AppleContainerType add(AppleContainerType info1, AppleContainerType info2) {
        AppleContainerType sum = new AppleContainerType();
        sum.setSingleTrack(addAndReplaceZeroWithNull(info1.getSingleTrack(), info2.getSingleTrack()));
        sum.setRadio(addAndReplaceZeroWithNull(info1.getRadio(), info2.getRadio()));
        sum.setPlaylist(addAndReplaceZeroWithNull(info1.getPlaylist(), info2.getPlaylist()));
        sum.setAlbum(addAndReplaceZeroWithNull(info1.getAlbum(), info2.getAlbum()));
        return sum;
    }

    private AppleContainerSubType add(AppleContainerSubType info1, AppleContainerSubType info2) {
        AppleContainerSubType sum = new AppleContainerSubType();
        sum.setNotApplicable(addAndReplaceZeroWithNull(info1.getNotApplicable(), info2.getNotApplicable()));
        sum.setPrivateUserPlaylist(
            addAndReplaceZeroWithNull(info1.getPrivateUserPlaylist(), info2.getPrivateUserPlaylist()));
        sum.setEditorialPlaylist(addAndReplaceZeroWithNull(info1.getEditorialPlaylist(), info2.getEditorialPlaylist()));
        sum.setArtistPlaylist(addAndReplaceZeroWithNull(info1.getArtistPlaylist(), info2.getArtistPlaylist()));
        sum.setCuratorPlaylist(addAndReplaceZeroWithNull(info1.getCuratorPlaylist(), info2.getCuratorPlaylist()));
        sum.setSeededByArtistSong(
            addAndReplaceZeroWithNull(info1.getSeededByArtistSong(), info2.getSeededByArtistSong()));
        sum.setFormatStation(addAndReplaceZeroWithNull(info1.getFormatStation(), info2.getFormatStation()));
        sum.setEditorialStation(addAndReplaceZeroWithNull(info1.getEditorialStation(), info2.getEditorialStation()));
        sum.setPersonalMixPlaylist(
            addAndReplaceZeroWithNull(info1.getPersonalMixPlaylist(), info2.getPersonalMixPlaylist()));
        sum.setNewMusicMix(addAndReplaceZeroWithNull(info1.getNewMusicMix(), info2.getNewMusicMix()));
        sum.setFavoritesMix(addAndReplaceZeroWithNull(info1.getFavoritesMix(), info2.getFavoritesMix()));
        sum.setChillMix(addAndReplaceZeroWithNull(info1.getChillMix(), info2.getChillMix()));
        sum.setGetUpMix(addAndReplaceZeroWithNull(info1.getGetUpMix(), info2.getGetUpMix()));
        sum.setFriendsMix(addAndReplaceZeroWithNull(info1.getFriendsMix(), info2.getFriendsMix()));
        sum.setAlgorithmStation(addAndReplaceZeroWithNull(info1.getAlgorithmStation(), info2.getAlgorithmStation()));
        sum.setReplayMix(addAndReplaceZeroWithNull(info1.getReplayMix(), info2.getReplayMix()));
        sum.setAutoPlayStation(addAndReplaceZeroWithNull(info1.getAutoPlayStation(), info2.getAutoPlayStation()));
        sum.setChartsPlaylist(addAndReplaceZeroWithNull(info1.getChartsPlaylist(), info2.getChartsPlaylist()));
        sum.setArtistStation(addAndReplaceZeroWithNull(info1.getArtistStation(), info2.getArtistStation()));
        sum.setPersonalStation(addAndReplaceZeroWithNull(info1.getPersonalStation(), info2.getPersonalStation()));
        sum.setAlbum(addAndReplaceZeroWithNull(info1.getAlbum(), info2.getAlbum()));
        return sum;
    }

    private AppleDeviceType add(AppleDeviceType info1, AppleDeviceType info2) {
        AppleDeviceType sum = new AppleDeviceType();
        sum.setMobile(addAndReplaceZeroWithNull(info1.getMobile(), info2.getMobile()));
        sum.setDesktop(addAndReplaceZeroWithNull(info1.getDesktop(), info2.getDesktop()));
        sum.setVoice(addAndReplaceZeroWithNull(info1.getVoice(), info2.getVoice()));
        sum.setOther(addAndReplaceZeroWithNull(info1.getOther(), info2.getOther()));
        return sum;
    }

    private AppleOs add(AppleOs info1, AppleOs info2) {
        AppleOs sum = new AppleOs();
        sum.setIos(addAndReplaceZeroWithNull(info1.getIos(), info2.getIos()));
        sum.setMac(addAndReplaceZeroWithNull(info1.getMac(), info2.getMac()));
        sum.setAndroid(addAndReplaceZeroWithNull(info1.getAndroid(), info2.getAndroid()));
        sum.setWindows(addAndReplaceZeroWithNull(info1.getWindows(), info2.getWindows()));
        sum.setTvos(addAndReplaceZeroWithNull(info1.getTvos(), info2.getTvos()));
        sum.setSonos(addAndReplaceZeroWithNull(info1.getSonos(), info2.getSonos()));
        sum.setOther(addAndReplaceZeroWithNull(info1.getOther(), info2.getOther()));
        return sum;
    }

    private AppleUserType add(AppleUserType info1, AppleUserType info2) {
        AppleUserType sum = new AppleUserType();
        sum.setTrial(addAndReplaceZeroWithNull(info1.getTrial(), info2.getTrial()));
        sum.setPaid(addAndReplaceZeroWithNull(info1.getPaid(), info2.getPaid()));
        sum.setUnknown(addAndReplaceZeroWithNull(info1.getUnknown(), info2.getUnknown()));
        return sum;
    }

    private AppleListenerEngagement add(AppleListenerEngagement info1, AppleListenerEngagement info2) {
        AppleListenerEngagement sum = new AppleListenerEngagement();
        sum.setLeanBack(addAndReplaceZeroWithNull(info1.getLeanBack(), info2.getLeanBack()));
        sum.setLeanForward(addAndReplaceZeroWithNull(info1.getLeanForward(), info2.getLeanForward()));
        sum.setUnknown(addAndReplaceZeroWithNull(info1.getUnknown(), info2.getUnknown()));
        return sum;
    }

    private AmazonDeviceType add(AmazonDeviceType info1, AmazonDeviceType info2) {
        AmazonDeviceType sum = new AmazonDeviceType();
        sum.setDesktop(addAndReplaceZeroWithNull(info1.getDesktop(), info2.getDesktop()));
        sum.setCellPhoneAppMobileWeb(
            addAndReplaceZeroWithNull(info1.getCellPhoneAppMobileWeb(), info2.getCellPhoneAppMobileWeb()));
        sum.setTabletApp(addAndReplaceZeroWithNull(info1.getTabletApp(), info2.getTabletApp()));
        sum.setInDashCarIntegration(
            addAndReplaceZeroWithNull(info1.getInDashCarIntegration(), info2.getInDashCarIntegration()));
        sum.setMobileToCarTether(addAndReplaceZeroWithNull(info1.getMobileToCarTether(), info2.getMobileToCarTether()));
        sum.setConnectedHomeTv(addAndReplaceZeroWithNull(info1.getConnectedHomeTv(), info2.getConnectedHomeTv()));
        sum.setConnectedHomeAudioTether(
            addAndReplaceZeroWithNull(info1.getConnectedHomeAudioTether(), info2.getConnectedHomeAudioTether()));
        sum.setGamingSystem(addAndReplaceZeroWithNull(info1.getGamingSystem(), info2.getGamingSystem()));
        sum.setVoiceControlledDevice(
            addAndReplaceZeroWithNull(info1.getVoiceControlledDevice(), info2.getVoiceControlledDevice()));
        sum.setOther(addAndReplaceZeroWithNull(info1.getOther(), info2.getOther()));
        return sum;
    }

    private AmazonEngagement add(AmazonEngagement info1, AmazonEngagement info2) {
        AmazonEngagement sum = new AmazonEngagement();
        sum.setLeanForward(addAndReplaceZeroWithNull(info1.getLeanForward(), info2.getLeanForward()));
        sum.setLeanBack(addAndReplaceZeroWithNull(info1.getLeanBack(), info2.getLeanBack()));
        return sum;
    }

    private AmazonOperatingSystem add(AmazonOperatingSystem info1, AmazonOperatingSystem info2) {
        AmazonOperatingSystem sum = new AmazonOperatingSystem();
        sum.setAlexa(addAndReplaceZeroWithNull(info1.getAlexa(), info2.getAlexa()));
        sum.setAndroid(addAndReplaceZeroWithNull(info1.getAndroid(), info2.getAndroid()));
        sum.setFireOs(addAndReplaceZeroWithNull(info1.getFireOs(), info2.getFireOs()));
        sum.setIos(addAndReplaceZeroWithNull(info1.getIos(), info2.getIos()));
        sum.setMacOsx(addAndReplaceZeroWithNull(info1.getMacOsx(), info2.getMacOsx()));
        sum.setThirdPartyDeviceOs(
            addAndReplaceZeroWithNull(info1.getThirdPartyDeviceOs(), info2.getThirdPartyDeviceOs()));
        sum.setUnknown(addAndReplaceZeroWithNull(info1.getUnknown(), info2.getUnknown()));
        sum.setWindows(addAndReplaceZeroWithNull(info1.getWindows(), info2.getWindows()));
        return sum;
    }

    private AmazonReferralSourceType add(AmazonReferralSourceType info1, AmazonReferralSourceType info2) {
        AmazonReferralSourceType sum = new AmazonReferralSourceType();
        sum.setWebsitesOrHyperlinks(
            addAndReplaceZeroWithNull(info1.getWebsitesOrHyperlinks(), info2.getWebsitesOrHyperlinks()));
        sum.setSearchResult(addAndReplaceZeroWithNull(info1.getSearchResult(), info2.getSearchResult()));
        sum.setSmePromotionalMarketingInventory(addAndReplaceZeroWithNull(info1.getSmePromotionalMarketingInventory(),
            info2.getSmePromotionalMarketingInventory()));
        sum.setSmePaidPromotion(addAndReplaceZeroWithNull(info1.getSmePaidPromotion(), info2.getSmePaidPromotion()));
        sum.setRecommendedContentPromotions(addAndReplaceZeroWithNull(info1.getRecommendedContentPromotions(),
            info2.getRecommendedContentPromotions()));
        sum.setNotAvailable(addAndReplaceZeroWithNull(info1.getNotAvailable(), info2.getNotAvailable()));
        return sum;
    }

    private AmazonSelectionSourceType add(AmazonSelectionSourceType info1, AmazonSelectionSourceType info2) {
        AmazonSelectionSourceType sum = new AmazonSelectionSourceType();
        sum.setAmfStation(addAndReplaceZeroWithNull(info1.getAmfStation(), info2.getAmfStation()));
        sum.setAmfStationSeed(addAndReplaceZeroWithNull(info1.getAmfStationSeed(), info2.getAmfStationSeed()));
        sum.setCustomMix(addAndReplaceZeroWithNull(info1.getCustomMix(), info2.getCustomMix()));
        sum.setError(addAndReplaceZeroWithNull(info1.getError(), info2.getError()));
        sum.setGenre(addAndReplaceZeroWithNull(info1.getGenre(), info2.getGenre()));
        sum.setGnoPrimePlaylist(addAndReplaceZeroWithNull(info1.getGnoPrimePlaylist(), info2.getGnoPrimePlaylist()));
        sum.setGnoUnlPlaylist(addAndReplaceZeroWithNull(info1.getGnoUnlPlaylist(), info2.getGnoUnlPlaylist()));
        sum.setGoldenPlaylist(addAndReplaceZeroWithNull(info1.getGoldenPlaylist(), info2.getGoldenPlaylist()));
        sum.setPrimeStationSeed(addAndReplaceZeroWithNull(info1.getPrimeStationSeed(), info2.getPrimeStationSeed()));
        sum.setRecentlyAdded(addAndReplaceZeroWithNull(info1.getRecentlyAdded(), info2.getRecentlyAdded()));
        sum.setRecentlyPlayed(addAndReplaceZeroWithNull(info1.getRecentlyPlayed(), info2.getRecentlyPlayed()));
        sum.setSearch(addAndReplaceZeroWithNull(info1.getSearch(), info2.getSearch()));
        sum.setSharedPlaylist(addAndReplaceZeroWithNull(info1.getSharedPlaylist(), info2.getSharedPlaylist()));
        sum.setSimilarity(addAndReplaceZeroWithNull(info1.getSimilarity(), info2.getSimilarity()));
        sum.setSimilarityStation(addAndReplaceZeroWithNull(info1.getSimilarityStation(), info2.getSimilarityStation()));
        sum.setStation(addAndReplaceZeroWithNull(info1.getStation(), info2.getStation()));
        sum.setTrack(addAndReplaceZeroWithNull(info1.getTrack(), info2.getTrack()));
        sum.setUndefined(addAndReplaceZeroWithNull(info1.getUndefined(), info2.getUndefined()));
        sum.setUnknown(addAndReplaceZeroWithNull(info1.getUnknown(), info2.getUnknown()));
        sum.setUnlStationSeed(addAndReplaceZeroWithNull(info1.getUnlStationSeed(), info2.getUnlStationSeed()));
        sum.setUnlimitedPlaylist(addAndReplaceZeroWithNull(info1.getUnlimitedPlaylist(), info2.getUnlimitedPlaylist()));
        sum.setUnlimitedStation(addAndReplaceZeroWithNull(info1.getUnlimitedStation(), info2.getUnlimitedStation()));
        sum.setUnlimitedStationSe(
            addAndReplaceZeroWithNull(info1.getUnlimitedStationSe(), info2.getUnlimitedStationSe()));
        sum.setPersonalizePlaylist(
            addAndReplaceZeroWithNull(info1.getPersonalizePlaylist(), info2.getPersonalizePlaylist()));
        sum.setAlbum(addAndReplaceZeroWithNull(info1.getAlbum(), info2.getAlbum()));
        sum.setArtist(addAndReplaceZeroWithNull(info1.getArtist(), info2.getArtist()));
        sum.setAutoPlaylist(addAndReplaceZeroWithNull(info1.getAutoPlaylist(), info2.getAutoPlaylist()));
        sum.setPrimePlaylist(addAndReplaceZeroWithNull(info1.getPrimePlaylist(), info2.getPrimePlaylist()));
        sum.setPrimeStation(addAndReplaceZeroWithNull(info1.getPrimeStation(), info2.getPrimeStation()));
        sum.setSongs(addAndReplaceZeroWithNull(info1.getSongs(), info2.getSongs()));
        sum.setUserPlaylist(addAndReplaceZeroWithNull(info1.getUserPlaylist(), info2.getUserPlaylist()));
        return sum;
    }

    private AmazonSubscriptionType add(AmazonSubscriptionType info1, AmazonSubscriptionType info2) {
        AmazonSubscriptionType sum = new AmazonSubscriptionType();
        sum.setFree(addAndReplaceZeroWithNull(info1.getFree(), info2.getFree()));
        sum.setPaid(addAndReplaceZeroWithNull(info1.getPaid(), info2.getPaid()));
        return sum;
    }

    private SpotifySource add(SpotifySource info1, SpotifySource info2) {
        SpotifySource sum = new SpotifySource();
        sum.setAlbum(addAndReplaceZeroWithNull(info1.getAlbum(), info2.getAlbum()));
        sum.setArtist(addAndReplaceZeroWithNull(info1.getArtist(), info2.getArtist()));
        sum.setChart(addAndReplaceZeroWithNull(info1.getChart(), info2.getChart()));
        sum.setCollection(addAndReplaceZeroWithNull(info1.getCollection(), info2.getCollection()));
        sum.setOther(addAndReplaceZeroWithNull(info1.getOther(), info2.getOther()));
        sum.setOthersPlaylist(addAndReplaceZeroWithNull(info1.getOthersPlaylist(), info2.getOthersPlaylist()));
        sum.setPlayQueue(addAndReplaceZeroWithNull(info1.getPlayQueue(), info2.getPlayQueue()));
        sum.setRadio(addAndReplaceZeroWithNull(info1.getRadio(), info2.getRadio()));
        sum.setSearch(addAndReplaceZeroWithNull(info1.getSearch(), info2.getSearch()));
        sum.setDailyMix(addAndReplaceZeroWithNull(info1.getDailyMix(), info2.getDailyMix()));
        sum.setDiscoverWeekly(addAndReplaceZeroWithNull(info1.getDiscoverWeekly(), info2.getDiscoverWeekly()));
        sum.setReleaseRadar(addAndReplaceZeroWithNull(info1.getReleaseRadar(), info2.getReleaseRadar()));
        return sum;
    }

    private SpotifyDeviceType add(SpotifyDeviceType info1, SpotifyDeviceType info2) {
        SpotifyDeviceType sum = new SpotifyDeviceType();
        sum.setBuiltinCarApplication(
            addAndReplaceZeroWithNull(info1.getBuiltinCarApplication(), info2.getBuiltinCarApplication()));
        sum.setCellPhone(addAndReplaceZeroWithNull(info1.getCellPhone(), info2.getCellPhone()));
        sum.setConnectedAudioDevice(
            addAndReplaceZeroWithNull(info1.getConnectedAudioDevice(), info2.getConnectedAudioDevice()));
        sum.setGamingConsole(addAndReplaceZeroWithNull(info1.getGamingConsole(), info2.getGamingConsole()));
        sum.setOther(addAndReplaceZeroWithNull(info1.getOther(), info2.getOther()));
        sum.setPersonalComputer(addAndReplaceZeroWithNull(info1.getPersonalComputer(), info2.getPersonalComputer()));
        sum.setSmartTvDevice(addAndReplaceZeroWithNull(info1.getSmartTvDevice(), info2.getSmartTvDevice()));
        sum.setTablet(addAndReplaceZeroWithNull(info1.getTablet(), info2.getTablet()));
        sum.setWearable(addAndReplaceZeroWithNull(info1.getWearable(), info2.getWearable()));
        return sum;
    }

    private SpotifyOperatingSystem add(SpotifyOperatingSystem info1, SpotifyOperatingSystem info2) {
        SpotifyOperatingSystem sum = new SpotifyOperatingSystem();
        sum.setAndroid(addAndReplaceZeroWithNull(info1.getAndroid(), info2.getAndroid()));
        sum.setBlackberry(addAndReplaceZeroWithNull(info1.getBlackberry(), info2.getBlackberry()));
        sum.setBrowser(addAndReplaceZeroWithNull(info1.getBrowser(), info2.getBrowser()));
        sum.setIos(addAndReplaceZeroWithNull(info1.getIos(), info2.getIos()));
        sum.setLinux(addAndReplaceZeroWithNull(info1.getLinux(), info2.getLinux()));
        sum.setMac(addAndReplaceZeroWithNull(info1.getMac(), info2.getMac()));
        sum.setOther(addAndReplaceZeroWithNull(info1.getOther(), info2.getOther()));
        sum.setWindows(addAndReplaceZeroWithNull(info1.getWindows(), info2.getWindows()));
        return sum;
    }

    private SpotifyEngagement add(SpotifyEngagement info1, SpotifyEngagement info2) {
        SpotifyEngagement sum = new SpotifyEngagement();
        sum.setLeanBack(addAndReplaceZeroWithNull(info1.getLeanBack(), info2.getLeanBack()));
        sum.setLeanForward(addAndReplaceZeroWithNull(info1.getLeanForward(), info2.getLeanForward()));
        return sum;
    }

    private SpotifySaves add(SpotifySaves info1, SpotifySaves info2) {
        SpotifySaves sum = new SpotifySaves();
        sum.setFree(addAndReplaceZeroWithNull(info1.getFree(), info2.getFree()));
        sum.setPremium(addAndReplaceZeroWithNull(info1.getPremium(), info2.getPremium()));
        sum.setTotal(addAndReplaceZeroWithNull(info1.getTotal(), info2.getTotal()));
        return sum;
    }

    private AppleAgeBand add(AppleAgeBand band1, AppleAgeBand band2) {
        AppleAgeBand summedBand = new AppleAgeBand();
        summedBand.setAll017(addAndReplaceZeroWithNull(band1.getAll017(), band2.getAll017()));
        summedBand.setAll1824(addAndReplaceZeroWithNull(band1.getAll1824(), band2.getAll1824()));
        summedBand.setAll2534(addAndReplaceZeroWithNull(band1.getAll2534(), band2.getAll2534()));
        summedBand.setAll3544(addAndReplaceZeroWithNull(band1.getAll3544(), band2.getAll3544()));
        summedBand.setAll4554(addAndReplaceZeroWithNull(band1.getAll4554(), band2.getAll4554()));
        summedBand.setAll5564(addAndReplaceZeroWithNull(band1.getAll5564(), band2.getAll5564()));
        summedBand.setAll65Plus(addAndReplaceZeroWithNull(band1.getAll65Plus(), band2.getAll65Plus()));
        summedBand.setAllUnknown(addAndReplaceZeroWithNull(band1.getAllUnknown(), band2.getAllUnknown()));
        summedBand.setMale017(addAndReplaceZeroWithNull(band1.getMale017(), band2.getMale017()));
        summedBand.setMale1824(addAndReplaceZeroWithNull(band1.getMale1824(), band2.getMale1824()));
        summedBand.setMale2534(addAndReplaceZeroWithNull(band1.getMale2534(), band2.getMale2534()));
        summedBand.setMale3544(addAndReplaceZeroWithNull(band1.getMale3544(), band2.getMale3544()));
        summedBand.setMale4554(addAndReplaceZeroWithNull(band1.getMale4554(), band2.getMale4554()));
        summedBand.setMale5564(addAndReplaceZeroWithNull(band1.getMale5564(), band2.getMale5564()));
        summedBand.setMale65Plus(addAndReplaceZeroWithNull(band1.getMale65Plus(), band2.getMale65Plus()));
        summedBand.setMaleUnknown(addAndReplaceZeroWithNull(band1.getMaleUnknown(), band2.getMaleUnknown()));
        summedBand.setFemale017(addAndReplaceZeroWithNull(band1.getFemale017(), band2.getFemale017()));
        summedBand.setFemale1824(addAndReplaceZeroWithNull(band1.getFemale1824(), band2.getFemale1824()));
        summedBand.setFemale2534(addAndReplaceZeroWithNull(band1.getFemale2534(), band2.getFemale2534()));
        summedBand.setFemale3544(addAndReplaceZeroWithNull(band1.getFemale3544(), band2.getFemale3544()));
        summedBand.setFemale4554(addAndReplaceZeroWithNull(band1.getFemale4554(), band2.getFemale4554()));
        summedBand.setFemale5564(addAndReplaceZeroWithNull(band1.getFemale5564(), band2.getFemale5564()));
        summedBand.setFemale65Plus(addAndReplaceZeroWithNull(band1.getFemale65Plus(), band2.getFemale65Plus()));
        summedBand.setFemaleUnknown(addAndReplaceZeroWithNull(band1.getFemaleUnknown(), band2.getFemaleUnknown()));
        summedBand.setUnknown017(addAndReplaceZeroWithNull(band1.getUnknown017(), band2.getUnknown017()));
        summedBand.setUnknown1824(addAndReplaceZeroWithNull(band1.getUnknown1824(), band2.getUnknown1824()));
        summedBand.setUnknown2534(addAndReplaceZeroWithNull(band1.getUnknown2534(), band2.getUnknown2534()));
        summedBand.setUnknown3544(addAndReplaceZeroWithNull(band1.getUnknown3544(), band2.getUnknown3544()));
        summedBand.setUnknown4554(addAndReplaceZeroWithNull(band1.getUnknown4554(), band2.getUnknown4554()));
        summedBand.setUnknown5564(addAndReplaceZeroWithNull(band1.getUnknown5564(), band2.getUnknown5564()));
        summedBand.setUnknown65Plus(addAndReplaceZeroWithNull(band1.getUnknown65Plus(), band2.getUnknown65Plus()));
        summedBand.setUnknownUnknown(addAndReplaceZeroWithNull(band1.getUnknownUnknown(), band2.getUnknownUnknown()));
        return summedBand;
    }

    private SpotifyAgeBand add(SpotifyAgeBand band1, SpotifyAgeBand band2) {
        SpotifyAgeBand summedBand = new SpotifyAgeBand();
        summedBand.setAll017(addAndReplaceZeroWithNull(band1.getAll017(), band2.getAll017()));
        summedBand.setAll1822(addAndReplaceZeroWithNull(band1.getAll1822(), band2.getAll1822()));
        summedBand.setAll2327(addAndReplaceZeroWithNull(band1.getAll2327(), band2.getAll2327()));
        summedBand.setAll2834(addAndReplaceZeroWithNull(band1.getAll2834(), band2.getAll2834()));
        summedBand.setAll3544(addAndReplaceZeroWithNull(band1.getAll3544(), band2.getAll3544()));
        summedBand.setAll4559(addAndReplaceZeroWithNull(band1.getAll4559(), band2.getAll4559()));
        summedBand.setAll60150(addAndReplaceZeroWithNull(band1.getAll60150(), band2.getAll60150()));
        summedBand.setAllUnknown(addAndReplaceZeroWithNull(band1.getAllUnknown(), band2.getAllUnknown()));
        summedBand.setFemale017(addAndReplaceZeroWithNull(band1.getFemale017(), band2.getFemale017()));
        summedBand.setFemale1822(addAndReplaceZeroWithNull(band1.getFemale1822(), band2.getFemale1822()));
        summedBand.setFemale2327(addAndReplaceZeroWithNull(band1.getFemale2327(), band2.getFemale2327()));
        summedBand.setFemale2834(addAndReplaceZeroWithNull(band1.getFemale2834(), band2.getFemale2834()));
        summedBand.setFemale3544(addAndReplaceZeroWithNull(band1.getFemale3544(), band2.getFemale3544()));
        summedBand.setFemale4559(addAndReplaceZeroWithNull(band1.getFemale4559(), band2.getFemale4559()));
        summedBand.setFemale60150(addAndReplaceZeroWithNull(band1.getFemale60150(), band2.getFemale60150()));
        summedBand.setFemaleUnknown(addAndReplaceZeroWithNull(band1.getFemaleUnknown(), band2.getFemaleUnknown()));
        summedBand.setMale017(addAndReplaceZeroWithNull(band1.getMale017(), band2.getMale017()));
        summedBand.setMale1822(addAndReplaceZeroWithNull(band1.getMale1822(), band2.getMale1822()));
        summedBand.setMale2327(addAndReplaceZeroWithNull(band1.getMale2327(), band2.getMale2327()));
        summedBand.setMale2834(addAndReplaceZeroWithNull(band1.getMale2834(), band2.getMale2834()));
        summedBand.setMale3544(addAndReplaceZeroWithNull(band1.getMale3544(), band2.getMale3544()));
        summedBand.setMale4559(addAndReplaceZeroWithNull(band1.getMale4559(), band2.getMale4559()));
        summedBand.setMale60150(addAndReplaceZeroWithNull(band1.getMale60150(), band2.getMale60150()));
        summedBand.setMaleUnknown(addAndReplaceZeroWithNull(band1.getMaleUnknown(), band2.getMaleUnknown()));
        summedBand.setNeutral017(addAndReplaceZeroWithNull(band1.getNeutral017(), band2.getNeutral017()));
        summedBand.setNeutral1822(addAndReplaceZeroWithNull(band1.getNeutral1822(), band2.getNeutral1822()));
        summedBand.setNeutral2327(addAndReplaceZeroWithNull(band1.getNeutral2327(), band2.getNeutral2327()));
        summedBand.setNeutral2834(addAndReplaceZeroWithNull(band1.getNeutral2834(), band2.getNeutral2834()));
        summedBand.setNeutral3544(addAndReplaceZeroWithNull(band1.getNeutral3544(), band2.getNeutral3544()));
        summedBand.setNeutral4559(addAndReplaceZeroWithNull(band1.getNeutral4559(), band2.getNeutral4559()));
        summedBand.setNeutral60150(addAndReplaceZeroWithNull(band1.getNeutral60150(), band2.getNeutral60150()));
        summedBand.setNeutralUnknown(addAndReplaceZeroWithNull(band1.getNeutralUnknown(), band2.getNeutralUnknown()));
        summedBand.setUnknown017(addAndReplaceZeroWithNull(band1.getUnknown017(), band2.getUnknown017()));
        summedBand.setUnknown1822(addAndReplaceZeroWithNull(band1.getUnknown1822(), band2.getUnknown1822()));
        summedBand.setUnknown2327(addAndReplaceZeroWithNull(band1.getUnknown2327(), band2.getUnknown2327()));
        summedBand.setUnknown2834(addAndReplaceZeroWithNull(band1.getUnknown2834(), band2.getUnknown2834()));
        summedBand.setUnknown3544(addAndReplaceZeroWithNull(band1.getUnknown3544(), band2.getUnknown3544()));
        summedBand.setUnknown4559(addAndReplaceZeroWithNull(band1.getUnknown4559(), band2.getUnknown4559()));
        summedBand.setUnknown60150(addAndReplaceZeroWithNull(band1.getUnknown60150(), band2.getUnknown60150()));
        summedBand.setUnknownUnknown(addAndReplaceZeroWithNull(band1.getUnknownUnknown(), band2.getUnknownUnknown()));
        return summedBand;
    }

    private Gender add(Gender gender1, Gender gender2) {
        Gender summed = new Gender();
        summed.setFemale(addAndReplaceZeroWithNull(gender1.getFemale(), gender2.getFemale()));
        summed.setMale(addAndReplaceZeroWithNull(gender1.getMale(), gender2.getMale()));
        summed.setUnknown(addAndReplaceZeroWithNull(gender1.getUnknown(), gender2.getUnknown()));
        summed.setNeutral(addAndReplaceZeroWithNull(gender1.getNeutral(), gender2.getNeutral()));
        return summed;
    }

}
