package com.adobe.primetime.reference.workflows.fox;

import android.content.Context;

import com.adobe.mediacore.MediaPlayerItem;
import com.adobe.mediacore.MediaPlayerItemConfig;
import com.adobe.mediacore.metadata.AdSignalingMode;
import com.adobe.mediacore.metadata.AuditudeSettings;
import com.adobe.mediacore.metadata.Metadata;
import com.adobe.mediacore.metadata.MetadataKeys;
import com.adobe.mediacore.metadata.TimedMetadata;
import com.adobe.mediacore.timeline.Opportunity;
import com.adobe.mediacore.timeline.Placement;
import com.adobe.mediacore.timeline.generators.OpportunityGenerator;
import com.adobe.mediacore.timeline.generators.OpportunityGeneratorClient;
import com.adobe.mediacore.utils.NumberUtils;
import com.adobe.mediacore.utils.TimeRange;

import java.util.ArrayList;
import java.util.List;

/**
 * Scte35 opportunity generator to support TVSDK 2.5.6.88
 */
public class FoxScte35OpportunityGenerator implements OpportunityGenerator {

    private static final String OPPORTUNITY_DURATION_KEY = "DURATION";
    private static final String PSDK_AVAIL_DURATON = "PSDK_AVAIL_DURATION";
    private static final String OPPORTUNITY_ID_KEY = "ID";
    private static final String OPPORTUNITY_TYPE_KEY = "TYPE";
    private static final String SCTE35 = "scte35";
    private static final String SCTE = "scte";
    private static final String OPPORTUNITY_ELAPSED_KEY = "ELAPSED";
    public static final String METADATA_ASSET_ID_KEY = "X-ASSET-ID";
    public static final String METADATA_SLOT_ID_KEY = "X-SLOT-ID";
    public static final String METADATA_TIME_POS_KEY = "X-PROGRAM-TIME-POSITION";

    private OpportunityGeneratorClient client;
    private MediaPlayerItemConfig itemConfig;
    private MediaPlayerItem item;
    double lastProcessedTime;

    public FoxScte35OpportunityGenerator()
    {
    }

    @Override
    public void configure(MediaPlayerItem item,
                          Context context,
                          OpportunityGeneratorClient client,
                          AdSignalingMode mode,
                          long playhead,
                          TimeRange playbackRange) {
        this.client = client;
        itemConfig = item.getConfig();
        this.item = item;
        this.lastProcessedTime = -1.0;
    }

    private boolean isAlreadyProcessed(TimedMetadata timedMetadata) {
        return timedMetadata.getTime() <= lastProcessedTime;
    }

    @Override
    public void update(long playhead, TimeRange playbackRange) {

        final AdSignalingMode mode = itemConfig.getAdSignalingMode();

        if (mode == AdSignalingMode.CUSTOM_RANGES) {
            // don't override custom ad ranges
            return;
        }

        // resolve mid-roll opportunities
        List<TimedMetadata> itemTimedMetadata = item.getTimedMetadata();
        // map each timed metadata over to an opportunity.
        for (TimedMetadata timedMetadata : itemTimedMetadata) {
            if (timedMetadata.getName() != null && timedMetadata.getName().equalsIgnoreCase("#EXT-X-CUE")) {
                if (isOpportunity(timedMetadata)) {
                    if (timedMetadata.getTime() >= playhead && !isAlreadyProcessed(timedMetadata)) {
                        Opportunity generatedOpportunity = createOpportunity(timedMetadata,
                                itemConfig.getAdvertisingMetadata());
                        client.resolve(generatedOpportunity);
                        lastProcessedTime = timedMetadata.getTime();
                    }
                }
            }
        }
    }

    @Override
    public void cleanup() {
    }

    private boolean isOpportunity(TimedMetadata timedMetadata) {
        Metadata metadata = timedMetadata.getMetadata();
        if (metadata != null && metadata.containsKey(OPPORTUNITY_TYPE_KEY)) {
            String type = metadata.getValue(OPPORTUNITY_TYPE_KEY);
            if ((type.equalsIgnoreCase(SCTE35) || type.equalsIgnoreCase(SCTE))) {
                if (metadata.containsKey(OPPORTUNITY_ELAPSED_KEY) &&
                        NumberUtils.parseNumber(metadata.getValue(OPPORTUNITY_ELAPSED_KEY), 0) == 0) {
                    return true;
                }
            }

        }
        return false;
    }

    private Opportunity createOpportunity(TimedMetadata timedMetadata, Metadata metadata) {
        long time = timedMetadata.getTime();
        long duration = 0L;
        String id = null;
        Metadata itemMetadata = timedMetadata.getMetadata();
        updateTargetingParams(timedMetadata, (Metadata) metadata);

        if (itemMetadata.containsKey(OPPORTUNITY_DURATION_KEY)) {
            //CHECKSTYLE:OFF
            duration = NumberUtils.parseNumber(itemMetadata.getValue(OPPORTUNITY_DURATION_KEY),
                    0L) * 1000L;
            //CHECKSTYLE:ON
        }

        if (itemMetadata.containsKey(OPPORTUNITY_ID_KEY)) {
            id = itemMetadata.getValue(OPPORTUNITY_ID_KEY);
        }

        if (id != null && duration > 0L) {
            final Placement placementInfo =
                    new Placement(Placement.Type.MID_ROLL, time, duration, Placement.Mode.DEFAULT);
            /*
            * Per Adobe, we need to externally set the available duration on the metadata before
            * returning the Opportunity, as this was previously done internally via the TVSDK
            */
            Metadata customParams = ((AuditudeSettings) metadata).getCustomParameters();
            customParams.setValue(PSDK_AVAIL_DURATON, String.valueOf(duration / 1000.0));

            return new Opportunity(id, placementInfo, metadata, null);
        } else {
            return null;
        }
    }

    private void updateTargetingParams(final TimedMetadata timedMetadata, final Metadata metadata) {
        final String assetId = timedMetadata.getMetadata().getValue(METADATA_ASSET_ID_KEY);
        final String midrollId = timedMetadata.getMetadata().getValue(METADATA_SLOT_ID_KEY);
        final String timePos = timedMetadata.getMetadata().getValue(METADATA_TIME_POS_KEY);

        final AuditudeSettings auditudeSettings = (AuditudeSettings) metadata;
        final Metadata customParameters = auditudeSettings.getCustomParameters();

        customParameters.setValue("PSDK_AVAIL_ID", timedMetadata.getMetadata().getValue(OPPORTUNITY_ID_KEY));
        customParameters.setValue("X-ASSET-ID", assetId);
        customParameters.setValue("slid", midrollId);
        customParameters.setValue("tpos", timePos);

        for (String key : timedMetadata.getMetadata().keySet()) {
            customParameters.setValue(key, timedMetadata.getMetadata().getValue(key));
        }

        customParameters.setValue("FW_ASSET_ID", assetId);
        customParameters.setValue("MIDROLL_ID", midrollId);
        customParameters.setValue("TIME_POSITION", timePos);

        auditudeSettings.setCustomParameters(customParameters);
        auditudeSettings.setTargetingInfo(customParameters);
    }
}
