/*----------------------------------------------------------------------
|    BLT_Decoder_GetVolume
+---------------------------------------------------------------------*/
BLT_Result
BLT_Decoder_GetVolume(BLT_Decoder* decoder, float* volume)
{
    BLT_MediaNode* output_node;
    BLT_Result     result;
    
    /* check parameters */
    if (volume == NULL) return BLT_ERROR_INVALID_PARAMETERS;
    
    /* default value */
    *volume = 0.0f;
    
    /* get the volume from the output node */
    result = BLT_Stream_GetOutputNode(decoder->stream, &output_node);
    if (BLT_SUCCEEDED(result) && output_node) {
        BLT_VolumeControl* volume_control = ATX_CAST(output_node, BLT_VolumeControl);
        if (volume_control) {
            result = BLT_VolumeControl_GetVolume(volume_control, volume);
        } else {
            result = BLT_ERROR_NOT_SUPPORTED;
        }
        ATX_RELEASE_OBJECT(output_node);
    } else {
        result = BLT_ERROR_INVALID_STATE;
    }
    
    return result;
}
/*----------------------------------------------------------------------
|    BLT_Decoder_OutputChanged
+---------------------------------------------------------------------*/
static void
BLT_Decoder_OutputChanged(BLT_Decoder* decoder, BLT_MediaNode* node)
{
    ATX_RELEASE_OBJECT(decoder->output);

    if (node) {
        decoder->output = ATX_CAST(node, BLT_OutputNode);
        if (decoder->output) {
            ATX_REFERENCE_OBJECT(decoder->output);
        }
    } else {
        decoder->output = NULL;
    }
}
/*----------------------------------------------------------------------
|    BLT_Decoder_Drain
+---------------------------------------------------------------------*/
BLT_Result
BLT_Decoder_Drain(BLT_Decoder* decoder)
{
    BLT_MediaNode* output_node;
    BLT_Result     result;
    
    /* drain the output node */
    result = BLT_Stream_GetOutputNode(decoder->stream, &output_node);
    if (BLT_SUCCEEDED(result) && output_node) {
        BLT_OutputNode* output_node_interface = ATX_CAST(output_node, BLT_OutputNode);
        if (output_node_interface) {
            result = BLT_OutputNode_Drain(output_node_interface);
        } else {
            result = BLT_SUCCESS;
        }
        ATX_RELEASE_OBJECT(output_node);
    } else {
        result = BLT_ERROR_INVALID_STATE;
    }
    
    return result;
}
/*----------------------------------------------------------------------
|    BLT_Decoder_SetVolume
+---------------------------------------------------------------------*/
BLT_Result
BLT_Decoder_SetVolume(BLT_Decoder* decoder, float volume)
{
    BLT_MediaNode* output_node;
    BLT_Result     result;
    
    /* get the volume from the output node */
    result = BLT_Stream_GetOutputNode(decoder->stream, &output_node);
    if (BLT_SUCCEEDED(result) && output_node) {
        BLT_VolumeControl* volume_control = ATX_CAST(output_node, BLT_VolumeControl);
        if (volume_control) {
            result = BLT_VolumeControl_SetVolume(volume_control, volume);
        } else {
            result = BLT_ERROR_NOT_SUPPORTED;
        }
        ATX_RELEASE_OBJECT(output_node);
    } else {
        result = BLT_ERROR_INVALID_STATE;
    }
    
    return result;
}
/*----------------------------------------------------------------------
|   Mp4ParserInput_SetStream
+---------------------------------------------------------------------*/
BLT_METHOD
Mp4ParserInput_SetStream(BLT_InputStreamUser* _self,
                         ATX_InputStream*     stream,
                         const BLT_MediaType* stream_media_type)
{
    Mp4Parser* self = ATX_SELF_M(input, Mp4Parser, BLT_InputStreamUser);
    BLT_Result result = BLT_ERROR_INVALID_MEDIA_FORMAT;
    
    /* check media type */
    if (stream_media_type == NULL || 
        (stream_media_type->id != self->input.audio_media_type.id &&
         stream_media_type->id != self->input.video_media_type.id)) {
        return BLT_ERROR_INVALID_MEDIA_TYPE;
    }

    /* if we had a file before, release it now */
    delete self->input.mp4_file;
    self->input.mp4_file = NULL;
    self->input.slow_seek = false;
    
    /* create an adapter for the stream */
    AP4_ByteStream* stream_adapter = new ATX_InputStream_To_AP4_ByteStream_Adapter(stream);

    /* check if the source can seek quickly or not */
    {
        ATX_Properties* stream_properties = ATX_CAST(stream, ATX_Properties);
        if (stream_properties) {
            ATX_PropertyValue property_value;
            result = ATX_Properties_GetProperty(stream_properties, 
                                                ATX_INPUT_STREAM_PROPERTY_SEEK_SPEED,
                                                &property_value);
            if (ATX_SUCCEEDED(result) && 
                property_value.type == ATX_PROPERTY_VALUE_TYPE_INTEGER &&
                property_value.data.integer <= ATX_INPUT_STREAM_SEEK_SPEED_SLOW) {
                AP4_ByteStream* buffered = new AP4_BufferedInputStream(*stream_adapter);
                ATX_LOG_FINE("using no-seek mode, source is slow");
                stream_adapter->Release();
                stream_adapter = buffered;
                self->input.slow_seek = true;
            }
        }
    }

    /* parse the MP4 file */
    ATX_LOG_FINE("parsing MP4 file");
    self->input.mp4_file = new AP4_File(*stream_adapter, 
                                        AP4_DefaultAtomFactory::Instance,
                                        true); /* parse until moov only */
    stream_adapter->Release();

    // get the global file info
    AP4_Movie* movie = self->input.mp4_file->GetMovie();
    if (movie == NULL) {
        ATX_LOG_FINE("no movie in file");
        goto fail;
    }
    
    // update the stream info
    BLT_StreamInfo stream_info;
    stream_info.type     = BLT_STREAM_TYPE_MULTIPLEXED;
    stream_info.id       = 0;
    stream_info.duration = movie->GetDurationMs();
    stream_info.mask = BLT_STREAM_INFO_MASK_TYPE |
                       BLT_STREAM_INFO_MASK_ID   |
                       BLT_STREAM_INFO_MASK_DURATION;    
    BLT_Stream_SetInfo(ATX_BASE(self, BLT_BaseMediaNode).context, &stream_info);
    
    // create a linear reader if the source is slow-seeking
    if (self->input.slow_seek) {
        self->input.reader = new AP4_LinearReader(*movie);
    }
    
    // setup the tracks
    result = Mp4Parser_SetupAudioOutput(self, movie);
    if (BLT_FAILED(result)) goto fail;
    result = Mp4Parser_SetupVideoOutput(self, movie);
    if (BLT_FAILED(result)) goto fail;
    
    // check that we have at least one media track
    if (self->audio_output.track == NULL && 
        self->video_output.track == NULL) {
        ATX_LOG_FINE("no media track found");
        goto fail;
    }
    
    return BLT_SUCCESS;

fail:
    delete self->input.mp4_file;
    self->input.mp4_file = NULL;
    self->audio_output.track = NULL;
    self->video_output.track = NULL;
    
    return result;
}