/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 package org.mangui.osmf.plugins {
    import flash.media.Video;
    import flash.net.NetStream;

    import org.mangui.hls.HLS;
    import org.mangui.hls.event.HLSEvent;
    import org.mangui.osmf.plugins.loader.HLSNetLoader;
    import org.mangui.osmf.plugins.traits.*;
    import org.mangui.osmf.plugins.utils.ErrorManager;

    import org.osmf.media.LoadableElementBase;
    import org.osmf.media.MediaElement;
    import org.osmf.media.MediaResourceBase;
    import org.osmf.media.videoClasses.VideoSurface;
    import org.osmf.net.NetStreamAudioTrait;
    import org.osmf.net.StreamType;
    import org.osmf.net.StreamingURLResource;
    import org.osmf.traits.AudioTrait;
    import org.osmf.traits.BufferTrait;
    import org.osmf.traits.DVRTrait;
    import org.osmf.traits.LoadTrait;
    import org.osmf.traits.LoaderBase;
    import org.osmf.traits.MediaTraitType;
    import org.osmf.traits.PlayTrait;
    import org.osmf.traits.SeekTrait;
    import org.osmf.traits.TimeTrait;
    import org.osmf.utils.OSMFSettings;
    import org.osmf.events.MediaError;
    import org.osmf.events.MediaErrorEvent;

    CONFIG::LOGGING {
    import org.mangui.hls.utils.Log;
    }

    public class HLSMediaElement extends LoadableElementBase {
        private var _hls : HLS;
        private var _stream : NetStream;
        private var _defaultduration : Number;
        private var videoSurface : VideoSurface;
        private var _smoothing : Boolean;

        public function HLSMediaElement(resource : MediaResourceBase, hls : HLS, duration : Number) {
            _hls = hls;
            _defaultduration = duration;
            super(resource, new HLSNetLoader(hls));
            _hls.addEventListener(HLSEvent.ERROR, _errorHandler);
        }

        protected function createVideo() : Video {
            return new Video();
        }

        override protected function createLoadTrait(resource : MediaResourceBase, loader : LoaderBase) : LoadTrait {
            return new HLSNetStreamLoadTrait(_hls, _defaultduration, loader, resource);
        }

        override protected function processLoadingState() : void {
            CONFIG::LOGGING {
            Log.debug("HLSMediaElement:processLoadingState");
            }
        }

        override protected function processReadyState() : void {
            CONFIG::LOGGING {
            Log.debug("HLSMediaElement:processReadyState");
            }
            initTraits();
        }

        override protected function processUnloadingState() : void {
            CONFIG::LOGGING {
            Log.debug("HLSMediaElement:processUnloadingState");
            }
            removeTrait(MediaTraitType.AUDIO);
            removeTrait(MediaTraitType.BUFFER);
            removeTrait(MediaTraitType.TIME);
            removeTrait(MediaTraitType.DISPLAY_OBJECT);
            removeTrait(MediaTraitType.PLAY);
            removeTrait(MediaTraitType.SEEK);
            removeTrait(MediaTraitType.DYNAMIC_STREAM);
            removeTrait(MediaTraitType.DVR);
            removeTrait(MediaTraitType.ALTERNATIVE_AUDIO);
            if (videoSurface != null) {
                videoSurface.attachNetStream(null);
                videoSurface = null;
            }
        }

        /**
         * Specifies whether the video should be smoothed (interpolated) when it is scaled.
         * For smoothing to work, the runtime must be in high-quality mode (the default).
         * The default value is false (no smoothing).  Set this property to true to take
         * advantage of mipmapping image optimization.
         *
         * @see flash.media.Video
         *
         *  @langversion 3.0
         *  @playerversion Flash 10
         *  @playerversion AIR 1.5
         *  @productversion OSMF 1.0
         **/
        public function get smoothing() : Boolean {
            return _smoothing;
        }

        public function set smoothing(value : Boolean) : void {
            _smoothing = value;
            if (videoSurface != null) {
                videoSurface.smoothing = value;
            }
        }

        private function initTraits() : void {
            _stream = _hls.stream;

            // Set the video's dimensions so that it doesn't appear at the wrong size.
            // We'll set the correct dimensions once the metadata is loaded.  (FM-206)
            videoSurface = new VideoSurface(OSMFSettings.enableStageVideo && OSMFSettings.supportsStageVideo, createVideo);
            videoSurface.smoothing = true;
            videoSurface.deblocking = 1;
            videoSurface.width = videoSurface.height = 0;
            videoSurface.attachNetStream(_stream);

            var audioTrait : AudioTrait = new NetStreamAudioTrait(_stream);
            addTrait(MediaTraitType.AUDIO, audioTrait);

            var ccTrait : HLSClosedCaptionsTrait = new HLSClosedCaptionsTrait(_hls);
            addTrait(HLSMediaTraitType.CLOSED_CAPTIONS, ccTrait);

            var bufferTrait : BufferTrait = new HLSBufferTrait(_hls);
            addTrait(MediaTraitType.BUFFER, bufferTrait);

            var timeTrait : TimeTrait = new HLSTimeTrait(_hls, _defaultduration);
            addTrait(MediaTraitType.TIME, timeTrait);

            var displayObjectTrait : HLSDisplayObjectTrait = new HLSDisplayObjectTrait(_hls,videoSurface, NaN, NaN);
            addTrait(MediaTraitType.DISPLAY_OBJECT, displayObjectTrait);

            // setup seek trait
            var seekTrait : SeekTrait = new HLSSeekTrait(_hls, timeTrait);
            addTrait(MediaTraitType.SEEK, seekTrait);

            if (_hls.levels.length > 1) {
                // setup dynamic stream trait
                var dsTrait : HLSDynamicStreamTrait = new HLSDynamicStreamTrait(_hls);
                addTrait(MediaTraitType.DYNAMIC_STREAM, dsTrait);
            }

            // retrieve stream type
            var streamType : String = (resource as StreamingURLResource).streamType;
            if (streamType == StreamType.DVR) {
                // add DvrTrait
                var dvrTrait : DVRTrait = new DVRTrait(true);
                addTrait(MediaTraitType.DVR, dvrTrait);
            }

            // setup drm trait
            // addTrait(MediaTraitType.DRM, drmTrait);

            // setup alternative audio trait
            var alternateAudioTrait : HLSAlternativeAudioTrait = new HLSAlternativeAudioTrait(_hls, this as MediaElement);
            addTrait(MediaTraitType.ALTERNATIVE_AUDIO, alternateAudioTrait);

            var playTrait : PlayTrait = new HLSPlayTrait(_hls);
            addTrait(MediaTraitType.PLAY, playTrait);
        }

        private function _errorHandler(event : HLSEvent) : void {
            var errorCode : int = ErrorManager.getMediaErrorCode(event);
            var errorMsg : String = ErrorManager.getMediaErrorMessage(event);
            CONFIG::LOGGING {
            Log.warn("HLS Error event received, dispatching MediaError " + errorCode + "," + errorMsg);
            }
            dispatchEvent(new MediaErrorEvent(MediaErrorEvent.MEDIA_ERROR, false, false, new MediaError(errorCode, errorMsg)));
        }
    }
}
