/* global bp, BP_Uploader, _, Backbone */

window.bp = window.bp || {};

( function() {

	// Bail if not set.
	if ( typeof BP_Uploader === 'undefined' ) {
		return;
	}

	bp.Models      = bp.Models || {};
	bp.Collections = bp.Collections || {};
	bp.Views       = bp.Views || {};

	bp.WebCam = {
		start: function() {
			this.params = {
				video:          null,
				videoStream:    null,
				capture_enable: false,
				capture:        null,
				canvas:         null,
				warning:        null,
				flipped:        false
			};

			bp.Avatar.nav.on( 'bp-avatar-view:changed', _.bind( this.setView, this ) );
		},

		setView: function( view ) {
			if ( 'camera' !== view ) {
				// Stop the camera if needed.
				if ( ! _.isNull( this.params.video ) ) {
					this.stop();

					// Remove all warnings as we're changing the view.
					this.removeWarning();
				}

				// Stop as this is not Camera area.
				return;
			}

			// Create the WebCam view.
			var cameraView = new bp.Views.WebCamAvatar( { model: new Backbone.Model( { user_media: false } ) } );

			// Make sure the flipped param is reset.
			this.params.flipped = false;

			// Add it to views.
			bp.Avatar.views.add( { id: 'camera', view: cameraView } );

			// Display it.
	        cameraView.inject( '.bp-avatar' );
		},

		removeView: function() {
			var camera;

			if ( ! _.isUndefined( bp.Avatar.views.get( 'camera' ) ) ) {
				camera = bp.Avatar.views.get( 'camera' );
				camera.get( 'view' ).remove();
				bp.Avatar.views.remove( { id: 'camera', view: camera } );
			}
		},

		gotStream: function( stream ) {
			var video = bp.WebCam.params.video;
			bp.WebCam.params.videoStream = stream;

			// User Feedback.
			bp.WebCam.displayWarning( 'loaded' );

			video.onerror = function () {
				// User Feedback.
				bp.WebCam.displayWarning( 'videoerror' );

				if ( video ) {
					bp.WebCam.stop();
				}
			};

			stream.onended = bp.WebCam.noStream();

			// Older browsers may not have srcObject.
			if ( 'srcObject' in video ) {
				video.srcObject = stream;
			} else {
				// Avoid using this in new browsers, as it is going away.
				video.src = window.URL.createObjectURL( stream );
			}

			video.onloadedmetadata = function() {
				video.play();
			};

			bp.WebCam.params.capture_enable = true;
		},

		stop: function() {
			bp.WebCam.params.capture_enable = false;
			if ( bp.WebCam.params.videoStream ) {
				if ( bp.WebCam.params.videoStream.stop ) {
					bp.WebCam.params.videoStream.stop();
				} else if ( bp.WebCam.params.videoStream.msStop ) {
					bp.WebCam.params.videoStream.msStop();
				}
				bp.WebCam.params.videoStream.onended = null;
				bp.WebCam.params.videoStream = null;
			}
			if ( bp.WebCam.params.video ) {
				bp.WebCam.params.video.onerror = null;
				bp.WebCam.params.video.pause();
				if ( bp.WebCam.params.video.mozSrcObject ) {
					bp.WebCam.params.video.mozSrcObject = null;
				}
				bp.WebCam.params.video.src = '';
			}
		},

		noStream: function() {
			if ( _.isNull( bp.WebCam.params.videoStream ) ) {
				// User Feedback.
				bp.WebCam.displayWarning( 'noaccess' );

				bp.WebCam.removeView();
			}
		},

		setAvatar: function( avatar ) {
			if ( ! avatar.get( 'url' ) ) {
				bp.WebCam.displayWarning( 'nocapture' );
			}

			// Remove the view.
			bp.WebCam.removeView();

			bp.Avatar.setAvatar( avatar );
		},

		removeWarning: function() {
			if ( ! _.isNull( this.params.warning ) ) {
				this.params.warning.remove();
			}
		},

		displayWarning: function( code ) {
			this.removeWarning();

			this.params.warning = new bp.Views.uploaderWarning( {
				value: BP_Uploader.strings.camera_warnings[code]
			} );

			this.params.warning.inject( '.bp-avatar-status' );
		}
	};

	// BuddyPress WebCam view.
	bp.Views.WebCamAvatar = bp.View.extend( {
		tagName: 'div',
		id: 'bp-webcam-avatar',
		template: bp.template( 'bp-avatar-webcam' ),

		events: {
			'click .avatar-webcam-capture': 'captureStream',
			'click .avatar-webcam-save': 'saveCapture'
		},

		initialize: function() {
			var params;

			// Standardize getUserMedia browser call.
			navigator.getUserMedia = (
				navigator.getUserMedia ||
				navigator.webkitGetUserMedia ||
				navigator.mozGetUserMedia ||
				navigator.msGetUserMedia ||
				navigator.oGetUserMedia
			);

			if ( typeof navigator.getUserMedia !== 'undefined' ) {
				// We need to add some cropping stuff to use bp.Avatar.setAvatar().
				params = _.extend( _.pick( BP_Uploader.settings.defaults.multipart_params.bp_params,
					'object',
					'item_id',
					'nonces'
					), {
						user_media:  true,
						w: BP_Uploader.settings.crop.full_w,
						h: BP_Uploader.settings.crop.full_h,
						x: 0,
						y: 0,
						type: 'camera'
					}
				);

				this.model.set( params );
			}

			this.on( 'ready', this.useStream, this );
		},

		useStream:function() {
			// No support for user media... Stop!
			if ( ! this.model.get( 'user_media' ) ) {
				return;
			}

			this.options.video = new bp.Views.WebCamVideo();
			this.options.canvas = new bp.Views.WebCamCanvas();

			this.$el.find( '#avatar-to-crop' ).append( this.options.video.el );
			this.$el.find( '#avatar-crop-pane' ).append( this.options.canvas.el );

			bp.WebCam.params.video = this.options.video.el;
			bp.WebCam.params.canvas = this.options.canvas.el;

			// User Feedback.
			bp.WebCam.displayWarning( 'requesting' );

			// Use deprecated getUserMedia call for browsers that require it.
			if ( typeof navigator.mediaDevices.getUserMedia === 'undefined' ) {
				navigator.getUserMedia({
					audio: false,
					video: true
				}, bp.WebCam.gotStream, bp.WebCam.noStream);

			// Teh new hotness!
			} else {
				navigator.mediaDevices.getUserMedia({
					audio: false,
					video: true
				}).then(bp.WebCam.gotStream, bp.WebCam.noStream)
				// ES3 compatibility.
				['catch'](function() {
					// User Feedback.
					bp.WebCam.displayWarning( 'errormsg' );
				});
			}
		},

		captureStream: function( event ) {
			var sx, sc;
			event.preventDefault();

			if ( ! bp.WebCam.params.capture_enable ) {
				// User Feedback.
				bp.WebCam.displayWarning( 'loading' );
				return;
			}

			if ( this.model.get( 'h' ) > this.options.video.el.videoHeight || this.model.get( 'w' ) > this.options.video.el.videoWidth ) {
				bp.WebCam.displayWarning( 'videoerror' );
				return;
			}

			// Set the offset.
			sc = this.options.video.el.videoHeight;
			sx = ( this.options.video.el.videoWidth - sc ) / 2;

			// Flip only once.
			if ( ! bp.WebCam.params.flipped ) {
				this.options.canvas.el.getContext( '2d' ).translate( this.model.get( 'w' ), 0 );
				this.options.canvas.el.getContext( '2d' ).scale( -1, 1 );
				bp.WebCam.params.flipped = true;
			}

			this.options.canvas.el.getContext( '2d' ).drawImage( this.options.video.el, sx, 0, sc, sc, 0, 0, this.model.get( 'w' ), this.model.get( 'h' ) );
			bp.WebCam.params.capture = this.options.canvas.el.toDataURL( 'image/png' );
			this.model.set( 'url', bp.WebCam.params.capture );

			// User Feedback.
			bp.WebCam.displayWarning( 'ready' );
		},

		saveCapture: function( event ) {
			event.preventDefault();

			if ( ! bp.WebCam.params.capture ) {
				// User Feedback.
				bp.WebCam.displayWarning( 'nocapture' );
				return;
			}

			bp.WebCam.stop();
			bp.WebCam.setAvatar( this.model );
		}
	} );

	// BuddyPress Video stream view.
	bp.Views.WebCamVideo = bp.View.extend( {
		tagName: 'video',
		id: 'bp-webcam-video',
		attributes: {
			autoplay: 'autoplay'
		}
	} );

	// BuddyPress Canvas (capture) view.
	bp.Views.WebCamCanvas = bp.View.extend( {
		tagName: 'canvas',
		id: 'bp-webcam-canvas',
		attributes: {
			width:  150,
			height: 150
		},

		initialize: function() {
			// Make sure to take in account bp_core_avatar_full_height or bp_core_avatar_full_width php filters.
			if ( ! _.isUndefined( BP_Uploader.settings.crop.full_h ) && ! _.isUndefined( BP_Uploader.settings.crop.full_w ) ) {
				this.el.attributes.width.value  = BP_Uploader.settings.crop.full_w;
				this.el.attributes.height.value = BP_Uploader.settings.crop.full_h;
			}
		}
	} );

	bp.WebCam.start();

})( bp, jQuery );
