text:
//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// This code is licensed under the MIT License (MIT).
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

(function () {
    "use strict";

    // Constant to decide whether to use the capture and encoding recommendations from the Video Stabilization effect
    const usVsRecommendation = 1;

    var Capture = Windows.Media.Capture;
    var DeviceInformation = Windows.Devices.Enumeration.DeviceInformation;
    var DeviceClass = Windows.Devices.Enumeration.DeviceClass;
    var DisplayOrientations = Windows.Graphics.Display.DisplayOrientations;
    var FileProperties = Windows.Storage.FileProperties;
    var Media = Windows.Media;
    var SimpleOrientation = Windows.Devices.Sensors.SimpleOrientation;
    var SimpleOrientationSensor = Windows.Devices.Sensors.SimpleOrientationSensor;
    var StorageLibrary = Windows.Storage.StorageLibrary;
    var KnownLibraryId = Windows.Storage.KnownLibraryId;
    var ApplicationData = Windows.Storage.ApplicationData;

    // Receive notifications about rotation of the device and UI and apply any necessary rotation to the preview stream and UI controls
    var oOrientationSensor = SimpleOrientationSensor.getDefault(),
        oDisplayInformation = Windows.Graphics.Display.DisplayInformation.getForCurrentView(),
        oDeviceOrientation = SimpleOrientation.notRotated,
        oDisplayOrientation = DisplayOrientations.portrait;

    // Prevent the screen from sleeping while the camera is running
    var oDisplayRequest = new Windows.System.Display.DisplayRequest();

    // For listening to media property changes
    var oSystemMediaControls = Media.SystemMediaTransportControls.getForCurrentView();

    // MediaCapture and its state variables
    var oMediaCapture = null,
        oEncodingProfile = null,
        isInitialized = false,
        isPreviewing = false,
        isRecording = false;

    // Information about the camera device
    var externalCamera = false,
        mirroringPreview = false;

    // Stabilization effect and properties
    var oVideoStabilizationEffect = null,
        oInputPropertiesBackup = null,
        oOutputPropertiesBackup = null;

    // Rotation metadata to apply to the preview stream and recorded videos (MF_MT_VIDEO_ROTATION)
    // Reference: http://msdn.microsoft.com/en-us/library/windows/apps/xaml/hh868174.aspx
    var RotationKey = "C380465D-2271-428C-9B83-ECEA3B4A85C1";

    // Folder in which the captures will be stored (initialized in SetupUiAsync)
    var oCaptureFolder;

    // Initialization
    var app = WinJS.Application;
    var activation = Windows.ApplicationModel.Activation;
    app.onactivated = function (args) {
        if (args.detail.kind === activation.ActivationKind.launch) {
            if (args.detail.previousExecutionState !== activation.ApplicationExecutionState.terminated) {
                document.getElementById("vsToggleButton").addEventListener("click", vsToggleButton_tapped);
                document.getElementById("videoButton").addEventListener("click", videoButton_tapped);
            }

            setupUiAsync();
            initializeCameraAsync();
            args.setPromise(WinJS.UI.processAll());
        }
    };

    // About to be suspended
    app.oncheckpoint = function (args) {
        cleanupCameraAsync()
        .then(function () {
            args.setPromise(cleanupUiAsync());
        }).done();
    };

    // Resuming from a user suspension
    Windows.UI.WebUI.WebUIApplication.addEventListener("resuming", function () {
        setupUiAsync();
        initializeCameraAsync();
    }, false);

    // Closing
    app.onunload = function (args) {
        document.getElementById("vsToggleButton").removeEventListener("click", vsToggleButton_tapped);
        document.getElementById("videoButton").removeEventListener("click", videoButton_tapped);

        cleanupCameraAsync()
        .then(function () {
            args.setPromise(cleanupUiAsync());
        }).done();
    };

    /// <summary>
    /// Initializes the MediaCapture, registers events, gets camera device information for mirroring and rotating, starts preview and unlocks the UI
    /// </summary>
    /// <returns></returns>
    function initializeCameraAsync() {
        console.log("InitializeCameraAsync");

        // Get available devices for capturing pictures
        return findCameraDeviceByPanelAsync(Windows.Devices.Enumeration.Panel.back)
        .then(function (camera) {
            if (camera === null) {
                console.log("No camera device found!");
                return;
            }
            // Figure out where the camera is located
            if (!camera.enclosureLocation || camera.enclosureLocation.panel === Windows.Devices.Enumeration.Panel.unknown) {
                // No information on the location of the camera, assume it's an external camera, not integrated on the device
                externalCamera = true;
            }
            else {
                // Camera is fixed on the device
                externalCamera = false;

                // Only mirror the preview if the camera is on the front panel
                mirroringPreview = (camera.enclosureLocation.panel === Windows.Devices.Enumeration.Panel.front);
            }

            oMediaCapture = new Capture.MediaCapture();

            // Register for a notification when video recording has reached the maximum time and when something goes wrong
            oMediaCapture.addEventListener("recordlimitationexceeded", mediaCapture_recordLimitationExceeded);
            oMediaCapture.addEventListener("failed", mediaCapture_failed);

            var settings = new Capture.MediaCaptureInitializationSettings();
            settings.videoDeviceId = camera.id;
            settings.streamingCaptureMode = Capture.StreamingCaptureMode.audioAndVideo;

            // Initialize media capture and start the preview
            return oMediaCapture.initializeAsync(settings)
            .then(function () {
                // Set up the encoding profile for video recording
                oEncodingProfile = Windows.Media.MediaProperties.MediaEncodingProfile.createMp4(Windows.Media.MediaProperties.VideoEncodingQuality.auto);
                isInitialized = true;

                startPreview();
            });
        }, function (error) {
            console.log(error.message);
        }).done();
    }

    /// <summary>
    /// Cleans up the camera resources (after stopping any video recording and/or preview if necessary) and unregisters from MediaCapture events
    /// </summary>
    /// <returns></returns>
    function cleanupCameraAsync() {
        console.log("cleanupCameraAsync");

        var promiseList = {};

        if (isInitialized) {
            // If a recording is in progress during cleanup, stop it to save the recording
            if (isRecording) {
                var stopRecordPromise = stopRecordingAsync();
                promiseList[promiseList.length] = stopRecordPromise;
            }

            if (oVideoStabilizationEffect != null) {
                var cleanStabilizationTask = cleanUpVideoStabilizationEffectAsync();
                promiseList[promiseList.length] = cleanStabilizationTask;
            }

            if (isPreviewing) {
                // The call to stop the preview is included here for completeness, but can be
                // safely removed if a call to MediaCapture.close() is being made later,
                // as the preview will be automatically stopped at that point
                stopPreview();
            }

            isInitialized = false;
        }

        // When all our tasks complete, clean up MediaCapture
        return WinJS.Promise.join(promiseList)
        .then(function () {
            if (oMediaCapture != null) {
                oMediaCapture.removeEventListener("recordlimitationexceeded", mediaCapture_recordLimitationExceeded);
                oMediaCapture.removeEventListener("failed", mediaCapture_failed);
                oMediaCapture.close();
                oMediaCapture = null;
            }
        });
    }

    /// <summary>
    /// Starts the preview and adjusts it for for rotation and mirroring after making a request to keep the screen on
    /// </summary>
    function startPreview() {
        // Prevent the device from sleeping while the preview is running
        oDisplayRequest.requestActive();

        // Set the preview source in the UI and mirror it if necessary
        var previewVidTag = document.getElementById("cameraPreview");
        if (mirroringPreview) {
            cameraPreview.style.transform = "scale(-1, 1)";
        }

        var previewUrl = URL.createObjectURL(oMediaCapture);
        previewVidTag.src = previewUrl;
        previewVidTag.play();

        previewVidTag.addEventListener("playing", function () {
            isPreviewing = true;
            updateCaptureControls();
            setPreviewRotationAsync();
        });
    }

    /// <summary>
    /// Gets the current orientation of the UI in relation to the device (when AutoRotationPreferences cannot be honored) and applies a corrective rotation to the preview
    /// </summary>
    /// <returns></returns>
    function setPreviewRotationAsync()
    {
        // Calculate which way and how far to rotate the preview
        var rotationDegrees = convertDisplayOrientationToDegrees(oDisplayOrientation);

        // The rotation direction needs to be inverted if the preview is being mirrored
        if (mirroringPreview)
        {
            rotationDegrees = (360 - rotationDegrees) % 360;
        }

        // Add rotation metadata to the preview stream to make sure the aspect ratio / dimensions match when rendering and getting preview frames
        var props = oMediaCapture.videoDeviceController.getMediaStreamProperties(Capture.MediaStreamType.videoPreview);
        props.properties.insert(RotationKey, rotationDegrees);
        return oMediaCapture.setEncodingPropertiesAsync(Capture.MediaStreamType.videoPreview, props, null);
    }

    /// <summary>
    /// Stops the preview and deactivates a display request, to allow the screen to go into power saving modes
    /// </summary>
    /// <returns></returns>
    function stopPreview() {
        isPreviewing = false;

        // Cleanup the UI
        var previewVidTag = document.getElementById("cameraPreview");
        previewVidTag.pause();
        previewVidTag.src = null;

        // Allow the device screen to sleep now that the preview is stopped
        oDisplayRequest.requestRelease();
    }

    /// <summary>
    /// Adds video stabilization to the video record stream, registers for its event, enables it, and gets the effect instance
    /// </summary>
    /// <returns></returns>
    function createVideoStabilizationEffectAsync() {
        // No work to be done if there already is an effect
        if (oVideoStabilizationEffect != null) {
            return WinJS.Promise.as();
        }

        // Create the definition, which will contain some initialization settings
        var definition = new Windows.Media.Core.VideoStabilizationEffectDefinition();

        // Add the effect to the video record stream
        return oMediaCapture.addVideoEffectAsync(definition, Capture.MediaStreamType.videoRecord)
        .then(function (mediaExtension) {
            console.log("VS effect added to camera pipeline");

            oVideoStabilizationEffect = mediaExtension;

            // Subscribe to notifications for changes in the enabled state for the effect
            oVideoStabilizationEffect.addEventListener("enabledchanged", videoStabilizationEffect_EnabledChanged);

            // Enable the effect
            oVideoStabilizationEffect.enabled = true;

            if (usVsRecommendation) {
                // Configure the camera pipeline to use the optimal settings for VS
                return setUpVideoStabilizationRecommendationAsync();

                // At this point, one of two things has happened:
                //
                // a) If a more suitable capture resolution was available:
                //    1. Such resolution will have been set up for video capture ("input")
                //    2. The MediaEncodingProfile ("output") will have been changed to specify dimensions reflecting the amount of cropping
                //       done on said capture resolution (possibly even none if the new resolution offers enough padding)
                // b) If no better suited capture resolution was available:
                //    1. The video capture resolution will not have changed
                //    2. The MediaEncodingProfile will have been changed to specify smaller dimensions than the capture resolution
                //       so that the video isn't scaled back up to the capture resolution after cropping, which could cause a loss in quality
            }
            else {
                console.log("Not setting up VS recommendation");

                // Not setting up VS recommendation means that the video will be captured at the desired resolution,
                // then cropped by the VS effect as part of the stabilization process, and then scaled back up to the
                // original capture resolution
                return WinJS.Promise.as();
            }
        });
    }

    /// <summary>
    /// Configures the camera pipeline to use the optimal resolutions for VS based on the settings currently in use
    /// </summary>
    /// <returns></returns>
    function setUpVideoStabilizationRecommendationAsync() {
        console.log("Setting up VS recommendation...");

        // Get the recommendation from the effect based on our current input and output configuration
        var recommendation = oVideoStabilizationEffect.getRecommendedStreamConfiguration(oMediaCapture.videoDeviceController, oEncodingProfile.video);

        // Handle the recommendations for the output from the effect
        if (recommendation.outputProperties != null) {
            // Back up the current output properties from before VS was activated
            oOutputPropertiesBackup = oEncodingProfile.video;

            // Apply the recommended encoding profile for the output, which will result in a video with the same dimensions as configured
            // before VideoStabilization was added if an appropriate padded capture resolution was available. Otherwise, it will be slightly
            // smaller (due to cropping). This prevents upscaling back to the original size, which can result in a loss of quality
            oEncodingProfile.video = recommendation.outputProperties;
            console.log("VS recommendation for the MediaEncodingProfile (output) has been applied");
        }

        // Handle the recommendation for the input into the effect, which can contain a larger resolution than currently configured, so cropping is minimized
        if (recommendation.inputProperties != null)
        {
            // Back up the current input properties from before VS was activated
            oInputPropertiesBackup = oMediaCapture.videoDeviceController.getMediaStreamProperties(Capture.MediaStreamType.videoRecord);

            // Set the recommendation from the effect (a resolution higher than the current one to allow for cropping) on the input
            return oMediaCapture.videoDeviceController.setMediaStreamPropertiesAsync(Capture.MediaStreamType.videoRecord, recommendation.inputProperties)
            .then(function ()
            {
                console.log("VS recommendation for the MediaStreamProperties (input) has been applied");
            });
        }
        else
        {
            return WinJS.Promise.as();
        }
    }

    /// <summary>
    ///  Disables and removes the video stabilization effect, and unregisters the event handler for the EnabledChanged event of the effect
    /// </summary>
    /// <returns></returns>
    function cleanUpVideoStabilizationEffectAsync() {
        // No work to be done if there is no effect
        if (!oVideoStabilizationEffect) {
            return WinJS.Promise.as();
        }

        // Disable the effect
        oVideoStabilizationEffect.enabled = false;

        oVideoStabilizationEffect.removeEventListener("enablechanged", videoStabilizationEffect_EnabledChanged);

        // Remove the effect from the record stream
        return oMediaCapture.clearEffectsAsync(Capture.MediaStreamType.videoRecord)
        .then(function () {
            console.log("VS effect removed from camera pipeline");

            // If backed up settings (stream properties and encoding profile) exist, restore them and clear the backups
            if (!oOutputPropertiesBackup) {
                oEncodingProfile.video = oOutputPropertiesBackup;
                oOutputPropertiesBackup = null;
            }

            var promiseToExecute = null;
            if (oInputPropertiesBackup != null) {
                promiseToExecute = oMediaCapture.videoDeviceController.setMediaStreamPropertiesAsync(Capture.MediaStreamType.videoRecord, oInputPropertiesBackup);
                oInputPropertiesBackup = null;
            }
            else {
                promiseToExecute = WinJS.Promise.as();
            }

            return promiseToExecute;
        }).then(function () {
            oVideoStabilizationEffect = null;
            return WinJS.Promise.as();
        });
    }

    /// <summary>
    /// Records an MP4 video to a StorageFile and adds rotation metadata to it
    /// </summary>
    /// <returns></returns>
    function startRecordingAsync() {
        return oCaptureFolder.createFileAsync("SimpleVideo.mp4", Windows.Storage.CreationCollisionOption.generateUniqueName)
        .then(function (file) {
            // Calculate rotation angle, taking mirroring into account if necessary
            var rotationAngle = 360 - convertDeviceOrientationToDegrees(getCameraOrientation());
            var encodingProfile = Windows.Media.MediaProperties.MediaEncodingProfile.createMp4(Windows.Media.MediaProperties.VideoEncodingQuality.auto);
            encodingProfile.video.properties.insert(RotationKey, rotationAngle);

            console.log("Starting recording to " + file.path);
            return oMediaCapture.startRecordToStorageFileAsync(encodingProfile, file)
            .then(function () {
                isRecording = true;
                console.log("Started recording!");
            });
        });
    }

    /// <summary>
    /// Stops recording a video
    /// </summary>
    /// <returns></returns>
    function stopRecordingAsync() {
        console.log("Stopping recording...");
        return oMediaCapture.stopRecordAsync()
        .then(function () {
            isRecording = false;
            console.log("Stopped recording!");

            if (oVideoStabilizationEffect != null) {
                if (!oVideoStabilizationEffect.enabled) {
                    return cleanUpVideoStabilizationEffectAsync();
                }
            }
            return WinJS.Promise.as();
        });
    }

    /// <summary>
    /// Attempts to find and return a device mounted on the panel specified, and on failure to find one it will return the first device listed
    /// </summary>
    /// <param name="panel">The desired panel on which the returned device should be mounted, if available</param>
    /// <returns></returns>
    function findCameraDeviceByPanelAsync(panel) {
        var deviceInfo = null;
        // Get available devices for capturing pictures
        return DeviceInformation.findAllAsync(DeviceClass.videoCapture)
        .then(function (devices) {
            devices.forEach(function (cameraDeviceInfo) {
                if (cameraDeviceInfo.enclosureLocation != null && cameraDeviceInfo.enclosureLocation.panel === panel) {
                    deviceInfo = cameraDeviceInfo;
                    return;
                }
            });

            // Nothing matched, just return the first
            if (!deviceInfo && devices.length > 0) {
                deviceInfo = devices.getAt(0);
            }

            return deviceInfo;
        });
    }

    /// <summary>
    /// Applies the given orientation to a photo stream and saves it as a StorageFile
    /// </summary>
    /// <param name="stream">The photo stream</param>
    /// <param name="photoOrientation">The orientation metadata to apply to the photo</param>
    /// <returns></returns>
    function reencodeAndSavePhotoAsync(inputStream, file, orientation) {
        var Imaging = Windows.Graphics.Imaging;
        var bitmapDecoder = null,
            bitmapEncoder = null,
            outputStream = null;

        return Imaging.BitmapDecoder.createAsync(inputStream)
        .then(function (decoder) {
            bitmapDecoder = decoder;
            return file.openAsync(Windows.Storage.FileAccessMode.readWrite);
        }).then(function (outStream) {
            outputStream = outStream;
            return Imaging.BitmapEncoder.createForTranscodingAsync(outputStream, bitmapDecoder);
        }).then(function (encoder) {
            bitmapEncoder = encoder;
            var properties = new Imaging.BitmapPropertySet();
            properties.insert("System.Photo.Orientation", new Imaging.BitmapTypedValue(orientation, Windows.Foundation.PropertyType.uint16));
            return bitmapEncoder.bitmapProperties.setPropertiesAsync(properties)
        }).then(function () {
            return bitmapEncoder.flushAsync();
        }).then(function () {
            inputStream.close();
            outputStream.close();
        });
    }

    /// <summary>
    /// This method will update the icons, enable/disable and show/hide the photo/video buttons depending on the current state of the app and the capabilities of the device
    /// </summary>
    function updateCaptureControls() {
        // The button should only be enabled if the preview started sucessfully
        videoButton.disabled = !isPreviewing;

        // Allow toggling VS only while not recording or when the effect exists but it has been disabled
        if (isPreviewing && !isRecording) {
            vsToggleButton.disabled = false;
        }
        else {
            if (oVideoStabilizationEffect != null) {
                if (oVideoStabilizationEffect.enabled === false) {
                    vsToggleButton.disabled = false;
                } else {
                    vsToggleButton.disabled = true;
                }
            }
            else {
                vsToggleButton.disabled = true;
            }
        }

        // Update recording button to show "Stop" icon instead of red "Record" icon
        var vidButton = document.getElementById("videoButton").winControl;
        if (isRecording) {
            vidButton.icon = "stop";
        }
        else {
            vidButton.icon = "video";
        }

        // Show "VS On" when effect exists and is enabled, otherwise show "VS Off"
        var vsButton = document.getElementById("vsToggleButton").winControl;
        if (oVideoStabilizationEffect != null)
        {
            if (oVideoStabilizationEffect.enabled === false) {
                vsButton.icon = "remote";
                vsButton.label = "VS Off";
            }
            else {
                vsButton.icon = "link";
                vsButton.label = "VS On";
            }
        }
        else
        {
            vsButton.icon = "remote";
            vsButton.label = "VS Off";
        }
    }

    /// <summary>
    /// Attempts to lock the page orientation, hide the StatusBar (on Phone) and registers event handlers for orientation sensors
    /// </summary>
    function setupUiAsync() {
        var Display = Windows.Graphics.Display;

        // Attempt to lock page to landscape orientation to prevent the CaptureElement from rotating, as this gives a better experience
        Display.DisplayInformation.autoRotationPreferences = Display.DisplayOrientations.landscape;

        registerEventHandlers();

        // Populate orientation variables with the current state
        oDisplayOrientation = oDisplayInformation.currentOrientation;
        if (oOrientationSensor != null) {
            oDeviceOrientation = oOrientationSensor.getCurrentOrientation();
        }

        return StorageLibrary.getLibraryAsync(KnownLibraryId.pictures)
        .then(function (picturesLibrary) {
            // Fall back to the local app storage if the Pictures Library is not available
            oCaptureFolder = picturesLibrary.saveFolder || ApplicationData.current.localFolder;

            // Hide the status bar
            if (Windows.Foundation.Metadata.ApiInformation.isTypePresent("Windows.UI.ViewManagement.StatusBar")) {
                return Windows.UI.ViewManagement.StatusBar.getForCurrentView().hideAsync();
            }
            else {
                return WinJS.Promise.as();
            }
        })
    }

    /// <summary>
    /// Unregisters event handlers for orientation sensors, allows the StatusBar (on Phone) to show, and removes the page orientation lock
    /// </summary>
    /// <returns></returns>
    function cleanupUiAsync() {
        unregisterEventHandlers();

        // Revert orientation preferences
        oDisplayInformation.AutoRotationPreferences = DisplayOrientations.none;

        // Show the status bar
        if (Windows.Foundation.Metadata.ApiInformation.isTypePresent("Windows.UI.ViewManagement.StatusBar")) {
            return Windows.UI.ViewManagement.StatusBar.getForCurrentView().showAsync();
        }
        else {
            return WinJS.Promise.as();
        }
    }

    /// <summary>
    /// Registers event handlers for orientation sensors, and performs an initial update of the UI rotation
    /// </summary>
    function registerEventHandlers()
    {
        // If there is an orientation sensor present on the device, register for notifications
        if (oOrientationSensor != null) {
            oOrientationSensor.addEventListener("orientationchanged", orientationSensor_orientationChanged);

            // Update orientation of buttons with the current orientation
            updateButtonOrientation();
        }

        oDisplayInformation.addEventListener("orientationchanged", displayInformation_orientationChanged);
        oSystemMediaControls.addEventListener("propertychanged", systemMediaControls_PropertyChanged);
    }

    /// <summary>
    /// Unregisters event handlers for orientation sensors
    /// </summary>
    function unregisterEventHandlers()
    {
        if (oOrientationSensor != null) {
            oOrientationSensor.removeEventListener("orientationchanged", orientationSensor_orientationChanged);
        }

        oDisplayInformation.removeEventListener("orientationchanged", displayInformation_orientationChanged);
        oSystemMediaControls.removeEventListener("propertychanged", systemMediaControls_PropertyChanged);
    }

    /// <summary>
    /// Calculates the current camera orientation from the device orientation by taking into account whether the camera is external or facing the user
    /// </summary>
    /// <returns>The camera orientation in space, with an inverted rotation in the case the camera is mounted on the device and is facing the user</returns>
    function getCameraOrientation() {
        if (externalCamera) {
            // Cameras that are not attached to the device do not rotate along with it, so apply no rotation
            return SimpleOrientation.notRotated;
        }

        var result = oDeviceOrientation;

        // Account for the fact that, on portrait-first devices, the camera sensor is mounted at a 90 degree offset to the native orientation
        if (oDisplayInformation.nativeOrientation === DisplayOrientations.portrait) {
            switch (result) {
                case SimpleOrientation.rotated90DegreesCounterclockwise:
                    result = SimpleOrientation.notRotated;
                    break;
                case SimpleOrientation.rotated180DegreesCounterclockwise:
                    result = SimpleOrientation.rotated90DegreesCounterclockwise;
                    break;
                case SimpleOrientation.rotated270DegreesCounterclockwise:
                    result = SimpleOrientation.rotated180DegreesCounterclockwise;
                    break;
                case SimpleOrientation.notRotated:
                default:
                    result = SimpleOrientation.rotated270DegreesCounterclockwise;
                    break;
            }
        }

        // If the preview is being mirrored for a front-facing camera, then the rotation should be inverted
        if (mirroringPreview) {
            // This only affects the 90 and 270 degree cases, because rotating 0 and 180 degrees is the same clockwise and counter-clockwise
            switch (result) {
                case SimpleOrientation.rotated90DegreesCounterclockwise:
                    return SimpleOrientation.rotated270DegreesCounterclockwise;
                case SimpleOrientation.rotated270DegreesCounterclockwise:
                    return SimpleOrientation.rotated90DegreesCounterclockwise;
            }
        }

        return result;
    }

    /// <summary>
    /// Converts the given orientation of the device in space to the metadata that can be added to captured photos
    /// </summary>
    /// <param name="orientation">The orientation of the device in space</param>
    /// <returns></returns>
    function convertOrientationToPhotoOrientation(orientation) {
        switch (orientation) {
            case SimpleOrientation.rotated90DegreesCounterclockwise:
                return FileProperties.PhotoOrientation.rotate90;
            case SimpleOrientation.rotated180DegreesCounterclockwise:
                return FileProperties.PhotoOrientation.rotate180;
            case SimpleOrientation.rotated270DegreesCounterclockwise:
                return FileProperties.PhotoOrientation.rotate270;
            case SimpleOrientation.notRotated:
            default:
                return FileProperties.PhotoOrientation.normal;
        }
    }

    /// <summary>
    /// Converts the given orientation of the device in space to the corresponding rotation in degrees
    /// </summary>
    /// <param name="orientation">The orientation of the device in space</param>
    /// <returns>An orientation in degrees</returns>
    function convertDeviceOrientationToDegrees(orientation) {
        switch (orientation) {
            case SimpleOrientation.rotated90DegreesCounterclockwise:
                return 90;
            case SimpleOrientation.rotated180DegreesCounterclockwise:
                return 180;
            case SimpleOrientation.rotated270DegreesCounterclockwise:
                return 270;
            case SimpleOrientation.notRotated:
            default:
                return 0;
        }
    }

    /// <summary>
    /// Converts the given orientation of the app on the screen to the corresponding rotation in degrees
    /// </summary>
    /// <param name="orientation">The orientation of the app on the screen</param>
    /// <returns>An orientation in degrees</returns>
    function convertDisplayOrientationToDegrees(orientation) {
        switch (orientation) {
            case DisplayOrientations.portrait:
                return 90;
            case DisplayOrientations.LandscapeFlipped:
                return 180;
            case DisplayOrientations.PortraitFlipped:
                return 270;
            case DisplayOrientations.Landscape:
            default:
                return 0;
        }
    }

    /// <summary>
    /// Uses the current device orientation in space and page orientation on the screen to calculate the rotation
    /// transformation to apply to the controls
    /// </summary>
    function updateButtonOrientation() {
        var currDeviceOrientation = convertDeviceOrientationToDegrees(oDeviceOrientation);
        var currDisplayOrientation = convertDisplayOrientationToDegrees(oDisplayOrientation);

        if (oDisplayInformation.nativeOrientation === DisplayOrientations.portrait) {
            currDeviceOrientation -= 90;
        }

        // Combine both rotations and make sure that 0 <= result < 360
        var angle = (360 + currDisplayOrientation + currDeviceOrientation) % 360;

        // Rotate the buttons in the UI to match the rotation of the device
        videoButton.style.transform = "rotate(" + angle + "deg)";
        vsToggleButton.style.transform = "rotate(" + angle + "deg)";
    }

    /// <summary>
    /// This event will fire when the page is rotated, when the DisplayInformation.AutoRotationPreferences value set in the setupUiAsync() method cannot be not honored.
    /// </summary>
    /// <param name="sender">The event source.</param>
    function displayInformation_orientationChanged(sender) {
        oDisplayOrientation = sender.currentOrientation;

        if (isPreviewing) {
            setPreviewRotationAsync();
        }

        updateButtonOrientation();
    }

    function vsToggleButton_tapped() {
        // Note that for the most part, this button is disabled during recording, except when VS is turned off automatically
        var promiseToExecute = null;

        if (!isRecording)
        {
            // While *not* recording, either the effect doesn't exist (and should be created) or it exists and is enabled.
            // This sample doesn't allow the user to disable the effect while recording. For the case that the effect disables
            // itself while recording, StopRecordingAsync() will clean up the effect after the recording session ends.

            if (oVideoStabilizationEffect == null) {
                // VS didn't exist, so create and enable the effect
                promiseToExecute = createVideoStabilizationEffectAsync();
            }
            else if (oVideoStabilizationEffect.enabled) {
                // VS was enabled, so clean it up and restore any capture settings (MediaStreamType and EncodingProfile)
                promiseToExecute = cleanUpVideoStabilizationEffectAsync();
            }
            else {
                promiseToExecute = WinJS.Promise.as();
            }
        }
        else if (!oVideoStabilizationEffect.enabled) {
            // While recording this button is diabled. The situation the user is allowed to tap it while recording, is when
            // VS turns itself off (see VideoStabilizationEffect_EnabledChanged), so proceed to re-enable the effect
            oVideoStabilizationEffect.enabled = true;
            promiseToExecute = WinJS.Promise.as();
        }
        else { // Button enabled during recording
            console.log("Warning: VS toggle button enabled during recording");
            promiseToExecute = WinJS.Promise.as();
        }

        promiseToExecute.then(function() {
            updateCaptureControls();
        }).done();
    }

    function videoButton_tapped() {
        var promiseToExecute = null;
        if (!isRecording) {
            promiseToExecute = startRecordingAsync();
        }
        else {
            promiseToExecute = stopRecordingAsync();
        }

        promiseToExecute
        .then(function () {
            updateCaptureControls();
        }, function (error) {
            console.log(error.message);
        }).done();
    }

    /// <summary>
    /// In the event of the app being minimized this method handles media property change events. If the app receives a mute
    /// notification, it is no longer in the foregroud.
    /// </summary>
    /// <param name="args"></param>
    function systemMediaControls_PropertyChanged(args) {
        // Check to see if the app is being muted. If so, it is being minimized.
        // Otherwise if it is not initialized, it is being brought into focus.
        if (args.target.soundLevel === Media.SoundLevel.muted) {
            cleanupCameraAsync();
        }
        else if (!isInitialized) {
            initializeCameraAsync();
        }
    }

    /// <summary>
    /// Triggers when the Enabled property of the VideoStabilizationEffect changes.
    /// </summary>
    /// <param name="sender">The instance of the effect firing the event.</param>
    /// <param name="args">Information about the event.</param>
    function videoStabilizationEffect_EnabledChanged(sender, args) {
        console.log("VS Enabled: " + sender.target.enabled + ". Reason: " + sender.reason);
        updateCaptureControls();
    }

    /// <summary>
    /// Occurs each time the simple orientation sensor reports a new sensor reading.
    /// </summary>
    /// <param name="args">The event data.</param>
    function orientationSensor_orientationChanged(args) {
        // If the device is parallel to the ground, keep the last orientation used. This allows users to take pictures of documents (FaceUp)
        // or the ceiling (FaceDown) in any orientation, by first holding the device in the desired orientation, and then pointing the camera
        // at the desired subject.
        if (args.orientation != SimpleOrientation.faceup && args.orientation != SimpleOrientation.facedown) {
            oDeviceOrientation = args.orientation;
            updateButtonOrientation();
        }
    }

    /// <summary>
    /// This is a notification that recording has to stop, and the app is expected to finalize the recording
    /// </summary>
    function mediaCapture_recordLimitationExceeded() {
        stopRecordingAsync()
        .done(function () {
            updateCaptureControls();
        });
    }

    function mediaCapture_failed(errorEventArgs)
    {
        console.log("MediaCapture_Failed: 0x" + errorEventArgs.code + ": " + errorEventArgs.message);

        cleanupCameraAsync()
        .done(function() {
            updateCaptureControls();
        });
    }

    app.start();
})();


text:
package copr

import (
	"bytes"
	"context"
	"fmt"
	"io"
	"math"
	"sort"
	"strconv"
	"sync"
	"sync/atomic"
	"time"

	"github.com/pingcap/errors"
	"github.com/pingcap/failpoint"
	"github.com/pingcap/kvproto/pkg/coprocessor"
	"github.com/pingcap/kvproto/pkg/kvrpcpb"
	"github.com/pingcap/kvproto/pkg/metapb"
	"github.com/pingcap/kvproto/pkg/mpp"
	"github.com/pingcap/log"
	"github.com/pingcap/tidb/kv"
	"github.com/pingcap/tidb/store/driver/backoff"
	derr "github.com/pingcap/tidb/store/driver/error"
	"github.com/pingcap/tidb/util/logutil"
	"github.com/tikv/client-go/v2/metrics"
	"github.com/tikv/client-go/v2/tikv"
	"github.com/tikv/client-go/v2/tikvrpc"
	"go.uber.org/zap"
)

// batchCopTask comprises of multiple copTask that will send to same store.
type batchCopTask struct {
	storeAddr string
	cmdType   tikvrpc.CmdType
	ctx       *tikv.RPCContext

	regionInfos []RegionInfo
}

type batchCopResponse struct {
	pbResp *coprocessor.BatchResponse
	detail *CopRuntimeStats

	// batch Cop Response is yet to return startKey. So batchCop cannot retry partially.
	startKey kv.Key
	err      error
	respSize int64
	respTime time.Duration
}

// GetData implements the kv.ResultSubset GetData interface.
func (rs *batchCopResponse) GetData() []byte {
	return rs.pbResp.Data
}

// GetStartKey implements the kv.ResultSubset GetStartKey interface.
func (rs *batchCopResponse) GetStartKey() kv.Key {
	return rs.startKey
}

// GetExecDetails is unavailable currently, because TiFlash has not collected exec details for batch cop.
// TODO: Will fix in near future.
func (rs *batchCopResponse) GetCopRuntimeStats() *CopRuntimeStats {
	return rs.detail
}

// MemSize returns how many bytes of memory this response use
func (rs *batchCopResponse) MemSize() int64 {
	if rs.respSize != 0 {
		return rs.respSize
	}

	// ignore rs.err
	rs.respSize += int64(cap(rs.startKey))
	if rs.detail != nil {
		rs.respSize += int64(sizeofExecDetails)
	}
	if rs.pbResp != nil {
		// Using a approximate size since it's hard to get a accurate value.
		rs.respSize += int64(rs.pbResp.Size())
	}
	return rs.respSize
}

func (rs *batchCopResponse) RespTime() time.Duration {
	return rs.respTime
}

func deepCopyStoreTaskMap(storeTaskMap map[uint64]*batchCopTask) map[uint64]*batchCopTask {
	storeTasks := make(map[uint64]*batchCopTask)
	for storeID, task := range storeTaskMap {
		t := batchCopTask{
			storeAddr: task.storeAddr,
			cmdType:   task.cmdType,
			ctx:       task.ctx,
		}
		t.regionInfos = make([]RegionInfo, len(task.regionInfos))
		copy(t.regionInfos, task.regionInfos)
		storeTasks[storeID] = &t
	}
	return storeTasks
}

func regionTotalCount(storeTasks map[uint64]*batchCopTask, candidateRegionInfos []RegionInfo) int {
	count := len(candidateRegionInfos)
	for _, task := range storeTasks {
		count += len(task.regionInfos)
	}
	return count
}

const (
	maxBalanceScore       = 100
	balanceScoreThreshold = 85
)

// Select at most cnt RegionInfos from candidateRegionInfos that belong to storeID.
// If selected[i] is true, candidateRegionInfos[i] has been selected and should be skip.
// storeID2RegionIndex is a map that key is storeID and value is a region index slice.
// selectRegion use storeID2RegionIndex to find RegionInfos that belong to storeID efficiently.
func selectRegion(storeID uint64, candidateRegionInfos []RegionInfo, selected []bool, storeID2RegionIndex map[uint64][]int, cnt int64) []RegionInfo {
	regionIndexes, ok := storeID2RegionIndex[storeID]
	if !ok {
		logutil.BgLogger().Error("selectRegion: storeID2RegionIndex not found", zap.Uint64("storeID", storeID))
		return nil
	}
	var regionInfos []RegionInfo
	i := 0
	for ; i < len(regionIndexes) && len(regionInfos) < int(cnt); i++ {
		idx := regionIndexes[i]
		if selected[idx] {
			continue
		}
		selected[idx] = true
		regionInfos = append(regionInfos, candidateRegionInfos[idx])
	}
	// Remove regions that has beed selected.
	storeID2RegionIndex[storeID] = regionIndexes[i:]
	return regionInfos
}

// Higher scores mean more balance: (100 - unblance percentage)
func balanceScore(maxRegionCount, minRegionCount int, balanceContinuousRegionCount int64) int {
	if minRegionCount <= 0 {
		return math.MinInt32
	}
	unbalanceCount := maxRegionCount - minRegionCount
	if unbalanceCount <= int(balanceContinuousRegionCount) {
		return maxBalanceScore
	}
	return maxBalanceScore - unbalanceCount*100/minRegionCount
}

func isBalance(score int) bool {
	return score >= balanceScoreThreshold
}

func checkBatchCopTaskBalance(storeTasks map[uint64]*batchCopTask, balanceContinuousRegionCount int64) (int, []string) {
	if len(storeTasks) == 0 {
		return 0, []string{}
	}
	maxRegionCount := 0
	minRegionCount := math.MaxInt32
	balanceInfos := []string{}
	for storeID, task := range storeTasks {
		cnt := len(task.regionInfos)
		if cnt > maxRegionCount {
			maxRegionCount = cnt
		}
		if cnt < minRegionCount {
			minRegionCount = cnt
		}
		balanceInfos = append(balanceInfos, fmt.Sprintf("storeID %d storeAddr %s regionCount %d", storeID, task.storeAddr, cnt))
	}
	return balanceScore(maxRegionCount, minRegionCount, balanceContinuousRegionCount), balanceInfos
}

// balanceBatchCopTaskWithContinuity try to balance `continuous regions` between TiFlash Stores.
// In fact, not absolutely continuous is required, regions' range are closed to store in a TiFlash segment is enough for internal read optimization.
//
// First, sort candidateRegionInfos by their key ranges.
// Second, build a storeID2RegionIndex data structure to fastly locate regions of a store (avoid scanning candidateRegionInfos repeatly).
// Third, each store will take balanceContinuousRegionCount from the sorted candidateRegionInfos. These regions are stored very close to each other in TiFlash.
// Fourth, if the region count is not balance between TiFlash, it may fallback to the original balance logic.
func balanceBatchCopTaskWithContinuity(storeTaskMap map[uint64]*batchCopTask, candidateRegionInfos []RegionInfo, balanceContinuousRegionCount int64) ([]*batchCopTask, int) {
	if len(candidateRegionInfos) < 500 {
		return nil, 0
	}
	funcStart := time.Now()
	regionCount := regionTotalCount(storeTaskMap, candidateRegionInfos)
	storeTasks := deepCopyStoreTaskMap(storeTaskMap)

	// Sort regions by their key ranges.
	sort.Slice(candidateRegionInfos, func(i, j int) bool {
		// Special case: Sort empty ranges to the end.
		if candidateRegionInfos[i].Ranges.Len() < 1 || candidateRegionInfos[j].Ranges.Len() < 1 {
			return candidateRegionInfos[i].Ranges.Len() > candidateRegionInfos[j].Ranges.Len()
		}
		// StartKey0 < StartKey1
		return bytes.Compare(candidateRegionInfos[i].Ranges.At(0).StartKey, candidateRegionInfos[j].Ranges.At(0).StartKey) == -1
	})

	balanceStart := time.Now()
	// Build storeID -> region index slice index and we can fastly locate regions of a store.
	storeID2RegionIndex := make(map[uint64][]int)
	for i, ri := range candidateRegionInfos {
		for _, storeID := range ri.AllStores {
			if val, ok := storeID2RegionIndex[storeID]; ok {
				storeID2RegionIndex[storeID] = append(val, i)
			} else {
				storeID2RegionIndex[storeID] = []int{i}
			}
		}
	}

	// If selected[i] is true, candidateRegionInfos[i] is selected by a store and should skip it in selectRegion.
	selected := make([]bool, len(candidateRegionInfos))
	for {
		totalCount := 0
		selectCountThisRound := 0
		for storeID, task := range storeTasks {
			// Each store select balanceContinuousRegionCount regions from candidateRegionInfos.
			// Since candidateRegionInfos is sorted, it is very likely that these regions are close to each other in TiFlash.
			regionInfo := selectRegion(storeID, candidateRegionInfos, selected, storeID2RegionIndex, balanceContinuousRegionCount)
			task.regionInfos = append(task.regionInfos, regionInfo...)
			totalCount += len(task.regionInfos)
			selectCountThisRound += len(regionInfo)
		}
		if totalCount >= regionCount {
			break
		}
		if selectCountThisRound == 0 {
			logutil.BgLogger().Error("selectCandidateRegionInfos fail: some region cannot find relevant store.", zap.Int("regionCount", regionCount), zap.Int("candidateCount", len(candidateRegionInfos)))
			return nil, 0
		}
	}
	balanceEnd := time.Now()

	score, balanceInfos := checkBatchCopTaskBalance(storeTasks, balanceContinuousRegionCount)
	if !isBalance(score) {
		logutil.BgLogger().Warn("balanceBatchCopTaskWithContinuity is not balance", zap.Int("score", score), zap.Strings("balanceInfos", balanceInfos))
	}

	totalCount := 0
	var res []*batchCopTask
	for _, task := range storeTasks {
		totalCount += len(task.regionInfos)
		if len(task.regionInfos) > 0 {
			res = append(res, task)
		}
	}
	if totalCount != regionCount {
		logutil.BgLogger().Error("balanceBatchCopTaskWithContinuity error", zap.Int("totalCount", totalCount), zap.Int("regionCount", regionCount))
		return nil, 0
	}

	logutil.BgLogger().Debug("balanceBatchCopTaskWithContinuity time",
		zap.Int("candidateRegionCount", len(candidateRegionInfos)),
		zap.Int64("balanceContinuousRegionCount", balanceContinuousRegionCount),
		zap.Int("balanceScore", score),
		zap.Duration("balanceTime", balanceEnd.Sub(balanceStart)),
		zap.Duration("totalTime", time.Since(funcStart)))

	return res, score
}

// balanceBatchCopTask balance the regions between available stores, the basic rule is
// 1. the first region of each original batch cop task belongs to its original store because some
//    meta data(like the rpc context) in batchCopTask is related to it
// 2. for the remaining regions:
//    if there is only 1 available store, then put the region to the related store
//    otherwise, these region will be balance between TiFlash stores.
// Currently, there are two balance strategies.
// The first balance strategy: use a greedy algorithm to put it into the store with highest weight. This strategy only consider the region count between TiFlash stores.
//
// The second balance strategy: Not only consider the region count between TiFlash stores, but also try to make the regions' range continuous(stored in TiFlash closely).
// If balanceWithContinuity is true, the second balance strategy is enable.
func balanceBatchCopTask(ctx context.Context, kvStore *kvStore, originalTasks []*batchCopTask, mppStoreLastFailTime map[string]time.Time, ttl time.Duration, balanceWithContinuity bool, balanceContinuousRegionCount int64) []*batchCopTask {
	isMPP := mppStoreLastFailTime != nil
	// for mpp, we still need to detect the store availability
	if len(originalTasks) <= 1 && !isMPP {
		return originalTasks
	}
	cache := kvStore.GetRegionCache()
	storeTaskMap := make(map[uint64]*batchCopTask)
	// storeCandidateRegionMap stores all the possible store->region map. Its content is
	// store id -> region signature -> region info. We can see it as store id -> region lists.
	storeCandidateRegionMap := make(map[uint64]map[string]RegionInfo)
	totalRegionCandidateNum := 0
	totalRemainingRegionNum := 0

	if !isMPP {
		for _, task := range originalTasks {
			taskStoreID := task.regionInfos[0].AllStores[0]
			batchTask := &batchCopTask{
				storeAddr:   task.storeAddr,
				cmdType:     task.cmdType,
				ctx:         task.ctx,
				regionInfos: []RegionInfo{task.regionInfos[0]},
			}
			storeTaskMap[taskStoreID] = batchTask
		}
	} else {
		logutil.BgLogger().Info("detecting available mpp stores")
		// decide the available stores
		stores := cache.RegionCache.GetTiFlashStores()
		var wg sync.WaitGroup
		var mu sync.Mutex
		wg.Add(len(stores))
		cur := time.Now()
		for i := range stores {
			go func(idx int) {
				defer wg.Done()
				s := stores[idx]

				var last time.Time
				var ok bool
				mu.Lock()
				if last, ok = mppStoreLastFailTime[s.GetAddr()]; ok && cur.Sub(last) < 100*time.Millisecond {
					// The interval time is so short that may happen in a same query, so we needn't to check again.
					mu.Unlock()
					return
				}
				mu.Unlock()

				resp, err := kvStore.GetTiKVClient().SendRequest(ctx, s.GetAddr(), &tikvrpc.Request{
					Type:    tikvrpc.CmdMPPAlive,
					StoreTp: tikvrpc.TiFlash,
					Req:     &mpp.IsAliveRequest{},
					Context: kvrpcpb.Context{},
				}, 2*time.Second)

				if err != nil || !resp.Resp.(*mpp.IsAliveResponse).Available {
					errMsg := "store not ready to serve"
					if err != nil {
						errMsg = err.Error()
					}
					logutil.BgLogger().Warn("Store is not ready", zap.String("store address", s.GetAddr()), zap.String("err message", errMsg))
					mu.Lock()
					mppStoreLastFailTime[s.GetAddr()] = time.Now()
					mu.Unlock()
					return
				}

				if cur.Sub(last) < ttl {
					logutil.BgLogger().Warn("Cannot detect store's availability because the current time has not reached MPPStoreLastFailTime + MPPStoreFailTTL", zap.String("store address", s.GetAddr()), zap.Time("last fail time", last))
					return
				}

				mu.Lock()
				defer mu.Unlock()
				storeTaskMap[s.StoreID()] = &batchCopTask{
					storeAddr: s.GetAddr(),
					cmdType:   originalTasks[0].cmdType,
					ctx:       &tikv.RPCContext{Addr: s.GetAddr(), Store: s},
				}
			}(i)
		}
		wg.Wait()
	}

	var candidateRegionInfos []RegionInfo
	for _, task := range originalTasks {
		for index, ri := range task.regionInfos {
			// for each region, figure out the valid store num
			validStoreNum := 0
			if index == 0 && !isMPP {
				continue
			}
			var validStoreID uint64
			for _, storeID := range ri.AllStores {
				if _, ok := storeTaskMap[storeID]; ok {
					validStoreNum++
					// original store id might be invalid, so we have to set it again.
					validStoreID = storeID
				}
			}
			if validStoreNum == 0 {
				logutil.BgLogger().Warn("Meet regions that don't have an available store. Give up balancing")
				return originalTasks
			} else if validStoreNum == 1 {
				// if only one store is valid, just put it to storeTaskMap
				storeTaskMap[validStoreID].regionInfos = append(storeTaskMap[validStoreID].regionInfos, ri)
			} else {
				// if more than one store is valid, put the region
				// to store candidate map
				totalRegionCandidateNum += validStoreNum
				totalRemainingRegionNum += 1
				candidateRegionInfos = append(candidateRegionInfos, ri)
				taskKey := ri.Region.String()
				for _, storeID := range ri.AllStores {
					if _, validStore := storeTaskMap[storeID]; !validStore {
						continue
					}
					if _, ok := storeCandidateRegionMap[storeID]; !ok {
						candidateMap := make(map[string]RegionInfo)
						storeCandidateRegionMap[storeID] = candidateMap
					}
					if _, duplicateRegion := storeCandidateRegionMap[storeID][taskKey]; duplicateRegion {
						// duplicated region, should not happen, just give up balance
						logutil.BgLogger().Warn("Meet duplicated region info during when trying to balance batch cop task, give up balancing")
						return originalTasks
					}
					storeCandidateRegionMap[storeID][taskKey] = ri
				}
			}
		}
	}

	// If balanceBatchCopTaskWithContinuity failed (not balance or return nil), it will fallback to the original balance logic.
	// So storeTaskMap should not be modify.
	var contiguousTasks []*batchCopTask = nil
	contiguousBalanceScore := 0
	if balanceWithContinuity {
		contiguousTasks, contiguousBalanceScore = balanceBatchCopTaskWithContinuity(storeTaskMap, candidateRegionInfos, balanceContinuousRegionCount)
		if isBalance(contiguousBalanceScore) && contiguousTasks != nil {
			return contiguousTasks
		}
	}

	if totalRemainingRegionNum > 0 {
		avgStorePerRegion := float64(totalRegionCandidateNum) / float64(totalRemainingRegionNum)
		findNextStore := func(candidateStores []uint64) uint64 {
			store := uint64(math.MaxUint64)
			weightedRegionNum := math.MaxFloat64
			if candidateStores != nil {
				for _, storeID := range candidateStores {
					if _, validStore := storeCandidateRegionMap[storeID]; !validStore {
						continue
					}
					num := float64(len(storeCandidateRegionMap[storeID]))/avgStorePerRegion + float64(len(storeTaskMap[storeID].regionInfos))
					if num < weightedRegionNum {
						store = storeID
						weightedRegionNum = num
					}
				}
				if store != uint64(math.MaxUint64) {
					return store
				}
			}
			for storeID := range storeTaskMap {
				if _, validStore := storeCandidateRegionMap[storeID]; !validStore {
					continue
				}
				num := float64(len(storeCandidateRegionMap[storeID]))/avgStorePerRegion + float64(len(storeTaskMap[storeID].regionInfos))
				if num < weightedRegionNum {
					store = storeID
					weightedRegionNum = num
				}
			}
			return store
		}

		store := findNextStore(nil)
		for totalRemainingRegionNum > 0 {
			if store == uint64(math.MaxUint64) {
				break
			}
			var key string
			var ri RegionInfo
			for key, ri = range storeCandidateRegionMap[store] {
				// get the first region
				break
			}
			storeTaskMap[store].regionInfos = append(storeTaskMap[store].regionInfos, ri)
			totalRemainingRegionNum--
			for _, id := range ri.AllStores {
				if _, ok := storeCandidateRegionMap[id]; ok {
					delete(storeCandidateRegionMap[id], key)
					totalRegionCandidateNum--
					if len(storeCandidateRegionMap[id]) == 0 {
						delete(storeCandidateRegionMap, id)
					}
				}
			}
			if totalRemainingRegionNum > 0 {
				avgStorePerRegion = float64(totalRegionCandidateNum) / float64(totalRemainingRegionNum)
				// it is not optimal because we only check the stores that affected by this region, in fact in order
				// to find out the store with the lowest weightedRegionNum, all stores should be checked, but I think
				// check only the affected stores is more simple and will get a good enough result
				store = findNextStore(ri.AllStores)
			}
		}
		if totalRemainingRegionNum > 0 {
			logutil.BgLogger().Warn("Some regions are not used when trying to balance batch cop task, give up balancing")
			return originalTasks
		}
	}

	if contiguousTasks != nil {
		score, balanceInfos := checkBatchCopTaskBalance(storeTaskMap, balanceContinuousRegionCount)
		if !isBalance(score) {
			logutil.BgLogger().Warn("Region count is not balance and use contiguousTasks", zap.Int("contiguousBalanceScore", contiguousBalanceScore), zap.Int("score", score), zap.Strings("balanceInfos", balanceInfos))
			return contiguousTasks
		}
	}

	var ret []*batchCopTask
	for _, task := range storeTaskMap {
		if len(task.regionInfos) > 0 {
			ret = append(ret, task)
		}
	}
	return ret
}

func buildBatchCopTasks(bo *backoff.Backoffer, store *kvStore, ranges *KeyRanges, storeType kv.StoreType, mppStoreLastFailTime map[string]time.Time, ttl time.Duration, balanceWithContinuity bool, balanceContinuousRegionCount int64) ([]*batchCopTask, error) {
	cache := store.GetRegionCache()
	start := time.Now()
	const cmdType = tikvrpc.CmdBatchCop
	rangesLen := ranges.Len()
	for {

		locations, err := cache.SplitKeyRangesByLocations(bo, ranges)
		if err != nil {
			return nil, errors.Trace(err)
		}
		var tasks []*copTask
		for _, lo := range locations {
			tasks = append(tasks, &copTask{
				region:    lo.Location.Region,
				ranges:    lo.Ranges,
				cmdType:   cmdType,
				storeType: storeType,
			})
		}

		var batchTasks []*batchCopTask

		storeTaskMap := make(map[string]*batchCopTask)
		needRetry := false
		for _, task := range tasks {
			rpcCtx, err := cache.GetTiFlashRPCContext(bo.TiKVBackoffer(), task.region, false)
			if err != nil {
				return nil, errors.Trace(err)
			}
			// When rpcCtx is nil, it's not only attributed to the miss region, but also
			// some TiFlash stores crash and can't be recovered.
			// That is not an error that can be easily recovered, so we regard this error
			// same as rpc error.
			if rpcCtx == nil {
				needRetry = true
				logutil.BgLogger().Info("retry for TiFlash peer with region missing", zap.Uint64("region id", task.region.GetID()))
				// Probably all the regions are invalid. Make the loop continue and mark all the regions invalid.
				// Then `splitRegion` will reloads these regions.
				continue
			}
			allStores := cache.GetAllValidTiFlashStores(task.region, rpcCtx.Store)
			if batchCop, ok := storeTaskMap[rpcCtx.Addr]; ok {
				batchCop.regionInfos = append(batchCop.regionInfos, RegionInfo{Region: task.region, Meta: rpcCtx.Meta, Ranges: task.ranges, AllStores: allStores})
			} else {
				batchTask := &batchCopTask{
					storeAddr:   rpcCtx.Addr,
					cmdType:     cmdType,
					ctx:         rpcCtx,
					regionInfos: []RegionInfo{{Region: task.region, Meta: rpcCtx.Meta, Ranges: task.ranges, AllStores: allStores}},
				}
				storeTaskMap[rpcCtx.Addr] = batchTask
			}
		}
		if needRetry {
			// As mentioned above, nil rpcCtx is always attributed to failed stores.
			// It's equal to long poll the store but get no response. Here we'd better use
			// TiFlash error to trigger the TiKV fallback mechanism.
			err = bo.Backoff(tikv.BoTiFlashRPC(), errors.New("Cannot find region with TiFlash peer"))
			if err != nil {
				return nil, errors.Trace(err)
			}
			continue
		}

		for _, task := range storeTaskMap {
			batchTasks = append(batchTasks, task)
		}
		if log.GetLevel() <= zap.DebugLevel {
			msg := "Before region balance:"
			for _, task := range batchTasks {
				msg += " store " + task.storeAddr + ": " + strconv.Itoa(len(task.regionInfos)) + " regions,"
			}
			logutil.BgLogger().Debug(msg)
		}
		balanceStart := time.Now()
		batchTasks = balanceBatchCopTask(bo.GetCtx(), store, batchTasks, mppStoreLastFailTime, ttl, balanceWithContinuity, balanceContinuousRegionCount)
		balanceElapsed := time.Since(balanceStart)
		if log.GetLevel() <= zap.DebugLevel {
			msg := "After region balance:"
			for _, task := range batchTasks {
				msg += " store " + task.storeAddr + ": " + strconv.Itoa(len(task.regionInfos)) + " regions,"
			}
			logutil.BgLogger().Debug(msg)
		}

		if elapsed := time.Since(start); elapsed > time.Millisecond*500 {
			logutil.BgLogger().Warn("buildBatchCopTasks takes too much time",
				zap.Duration("elapsed", elapsed),
				zap.Duration("balanceElapsed", balanceElapsed),
				zap.Int("range len", rangesLen),
				zap.Int("task len", len(batchTasks)))
		}
		metrics.TxnRegionsNumHistogramWithBatchCoprocessor.Observe(float64(len(batchTasks)))
		return batchTasks, nil
	}
}

func (c *CopClient) sendBatch(ctx context.Context, req *kv.Request, vars *tikv.Variables) kv.Response {
	if req.KeepOrder || req.Desc {
		return copErrorResponse{errors.New("batch coprocessor cannot prove keep order or desc property")}
	}
	ctx = context.WithValue(ctx, tikv.TxnStartKey(), req.StartTs)
	bo := backoff.NewBackofferWithVars(ctx, copBuildTaskMaxBackoff, vars)
	ranges := NewKeyRanges(req.KeyRanges)
	tasks, err := buildBatchCopTasks(bo, c.store.kvStore, ranges, req.StoreType, nil, 0, false, 0)
	if err != nil {
		return copErrorResponse{err}
	}
	it := &batchCopIterator{
		store:     c.store.kvStore,
		req:       req,
		finishCh:  make(chan struct{}),
		vars:      vars,
		rpcCancel: tikv.NewRPCanceller(),
	}
	ctx = context.WithValue(ctx, tikv.RPCCancellerCtxKey{}, it.rpcCancel)
	it.tasks = tasks
	it.respChan = make(chan *batchCopResponse, 2048)
	go it.run(ctx)
	return it
}

type batchCopIterator struct {
	store    *kvStore
	req      *kv.Request
	finishCh chan struct{}

	tasks []*batchCopTask

	// Batch results are stored in respChan.
	respChan chan *batchCopResponse

	vars *tikv.Variables

	rpcCancel *tikv.RPCCanceller

	wg sync.WaitGroup
	// closed represents when the Close is called.
	// There are two cases we need to close the `finishCh` channel, one is when context is done, the other one is
	// when the Close is called. we use atomic.CompareAndSwap `closed` to to make sure the channel is not closed twice.
	closed uint32
}

func (b *batchCopIterator) run(ctx context.Context) {
	// We run workers for every batch cop.
	for _, task := range b.tasks {
		b.wg.Add(1)
		boMaxSleep := copNextMaxBackoff
		failpoint.Inject("ReduceCopNextMaxBackoff", func(value failpoint.Value) {
			if value.(bool) {
				boMaxSleep = 2
			}
		})
		bo := backoff.NewBackofferWithVars(ctx, boMaxSleep, b.vars)
		go b.handleTask(ctx, bo, task)
	}
	b.wg.Wait()
	close(b.respChan)
}

// Next returns next coprocessor result.
// NOTE: Use nil to indicate finish, so if the returned ResultSubset is not nil, reader should continue to call Next().
func (b *batchCopIterator) Next(ctx context.Context) (kv.ResultSubset, error) {
	var (
		resp   *batchCopResponse
		ok     bool
		closed bool
	)

	// Get next fetched resp from chan
	resp, ok, closed = b.recvFromRespCh(ctx)
	if !ok || closed {
		return nil, nil
	}

	if resp.err != nil {
		return nil, errors.Trace(resp.err)
	}

	err := b.store.CheckVisibility(b.req.StartTs)
	if err != nil {
		return nil, errors.Trace(err)
	}
	return resp, nil
}

func (b *batchCopIterator) recvFromRespCh(ctx context.Context) (resp *batchCopResponse, ok bool, exit bool) {
	ticker := time.NewTicker(3 * time.Second)
	defer ticker.Stop()
	for {
		select {
		case resp, ok = <-b.respChan:
			return
		case <-ticker.C:
			if atomic.LoadUint32(b.vars.Killed) == 1 {
				resp = &batchCopResponse{err: derr.ErrQueryInterrupted}
				ok = true
				return
			}
		case <-b.finishCh:
			exit = true
			return
		case <-ctx.Done():
			// We select the ctx.Done() in the thread of `Next` instead of in the worker to avoid the cost of `WithCancel`.
			if atomic.CompareAndSwapUint32(&b.closed, 0, 1) {
				close(b.finishCh)
			}
			exit = true
			return
		}
	}
}

// Close releases the resource.
func (b *batchCopIterator) Close() error {
	if atomic.CompareAndSwapUint32(&b.closed, 0, 1) {
		close(b.finishCh)
	}
	b.rpcCancel.CancelAll()
	b.wg.Wait()
	return nil
}

func (b *batchCopIterator) handleTask(ctx context.Context, bo *Backoffer, task *batchCopTask) {
	tasks := []*batchCopTask{task}
	for idx := 0; idx < len(tasks); idx++ {
		ret, err := b.handleTaskOnce(ctx, bo, tasks[idx])
		if err != nil {
			resp := &batchCopResponse{err: errors.Trace(err), detail: new(CopRuntimeStats)}
			b.sendToRespCh(resp)
			break
		}
		tasks = append(tasks, ret...)
	}
	b.wg.Done()
}

// Merge all ranges and request again.
func (b *batchCopIterator) retryBatchCopTask(ctx context.Context, bo *backoff.Backoffer, batchTask *batchCopTask) ([]*batchCopTask, error) {
	var ranges []kv.KeyRange
	for _, ri := range batchTask.regionInfos {
		ri.Ranges.Do(func(ran *kv.KeyRange) {
			ranges = append(ranges, *ran)
		})
	}
	return buildBatchCopTasks(bo, b.store, NewKeyRanges(ranges), b.req.StoreType, nil, 0, false, 0)
}

const readTimeoutUltraLong = 3600 * time.Second // For requests that may scan many regions for tiflash.

func (b *batchCopIterator) handleTaskOnce(ctx context.Context, bo *backoff.Backoffer, task *batchCopTask) ([]*batchCopTask, error) {
	sender := NewRegionBatchRequestSender(b.store.GetRegionCache(), b.store.GetTiKVClient())
	var regionInfos = make([]*coprocessor.RegionInfo, 0, len(task.regionInfos))
	for _, ri := range task.regionInfos {
		regionInfos = append(regionInfos, &coprocessor.RegionInfo{
			RegionId: ri.Region.GetID(),
			RegionEpoch: &metapb.RegionEpoch{
				ConfVer: ri.Region.GetConfVer(),
				Version: ri.Region.GetVer(),
			},
			Ranges: ri.Ranges.ToPBRanges(),
		})
	}

	copReq := coprocessor.BatchRequest{
		Tp:        b.req.Tp,
		StartTs:   b.req.StartTs,
		Data:      b.req.Data,
		SchemaVer: b.req.SchemaVar,
		Regions:   regionInfos,
	}

	req := tikvrpc.NewRequest(task.cmdType, &copReq, kvrpcpb.Context{
		IsolationLevel:   isolationLevelToPB(b.req.IsolationLevel),
		Priority:         priorityToPB(b.req.Priority),
		NotFillCache:     b.req.NotFillCache,
		RecordTimeStat:   true,
		RecordScanStat:   true,
		TaskId:           b.req.TaskID,
		ResourceGroupTag: b.req.ResourceGroupTag,
	})
	req.StoreTp = tikvrpc.TiFlash

	logutil.BgLogger().Debug("send batch request to ", zap.String("req info", req.String()), zap.Int("cop task len", len(task.regionInfos)))
	resp, retry, cancel, err := sender.SendReqToAddr(bo, task.ctx, task.regionInfos, req, readTimeoutUltraLong)
	// If there are store errors, we should retry for all regions.
	if retry {
		return b.retryBatchCopTask(ctx, bo, task)
	}
	if err != nil {
		err = derr.ToTiDBErr(err)
		return nil, errors.Trace(err)
	}
	defer cancel()
	return nil, b.handleStreamedBatchCopResponse(ctx, bo, resp.Resp.(*tikvrpc.BatchCopStreamResponse), task)
}

func (b *batchCopIterator) handleStreamedBatchCopResponse(ctx context.Context, bo *Backoffer, response *tikvrpc.BatchCopStreamResponse, task *batchCopTask) (err error) {
	defer response.Close()
	resp := response.BatchResponse
	if resp == nil {
		// streaming request returns io.EOF, so the first Response is nil.
		return
	}
	for {
		err = b.handleBatchCopResponse(bo, resp, task)
		if err != nil {
			return errors.Trace(err)
		}
		resp, err = response.Recv()
		if err != nil {
			if errors.Cause(err) == io.EOF {
				return nil
			}

			if err1 := bo.Backoff(tikv.BoTiKVRPC(), errors.Errorf("recv stream response error: %v, task store addr: %s", err, task.storeAddr)); err1 != nil {
				return errors.Trace(err)
			}

			// No coprocessor.Response for network error, rebuild task based on the last success one.
			if errors.Cause(err) == context.Canceled {
				logutil.BgLogger().Info("stream recv timeout", zap.Error(err))
			} else {
				logutil.BgLogger().Info("stream unknown error", zap.Error(err))
			}
			return derr.ErrTiFlashServerTimeout
		}
	}
}

func (b *batchCopIterator) handleBatchCopResponse(bo *Backoffer, response *coprocessor.BatchResponse, task *batchCopTask) (err error) {
	if otherErr := response.GetOtherError(); otherErr != "" {
		err = errors.Errorf("other error: %s", otherErr)
		logutil.BgLogger().Warn("other error",
			zap.Uint64("txnStartTS", b.req.StartTs),
			zap.String("storeAddr", task.storeAddr),
			zap.Error(err))
		return errors.Trace(err)
	}

	if len(response.RetryRegions) > 0 {
		logutil.BgLogger().Info("multiple regions are stale and need to be refreshed", zap.Int("region size", len(response.RetryRegions)))
		for idx, retry := range response.RetryRegions {
			id := tikv.NewRegionVerID(retry.Id, retry.RegionEpoch.ConfVer, retry.RegionEpoch.Version)
			logutil.BgLogger().Info("invalid region because tiflash detected stale region", zap.String("region id", id.String()))
			b.store.GetRegionCache().InvalidateCachedRegionWithReason(id, tikv.EpochNotMatch)
			if idx >= 10 {
				logutil.BgLogger().Info("stale regions are too many, so we omit the rest ones")
				break
			}
		}
		return
	}

	resp := batchCopResponse{
		pbResp: response,
		detail: new(CopRuntimeStats),
	}

	backoffTimes := bo.GetBackoffTimes()
	resp.detail.BackoffTime = time.Duration(bo.GetTotalSleep()) * time.Millisecond
	resp.detail.BackoffSleep = make(map[string]time.Duration, len(backoffTimes))
	resp.detail.BackoffTimes = make(map[string]int, len(backoffTimes))
	for backoff := range backoffTimes {
		resp.detail.BackoffTimes[backoff] = backoffTimes[backoff]
		resp.detail.BackoffSleep[backoff] = time.Duration(bo.GetBackoffSleepMS()[backoff]) * time.Millisecond
	}
	resp.detail.CalleeAddress = task.storeAddr

	b.sendToRespCh(&resp)

	return
}

func (b *batchCopIterator) sendToRespCh(resp *batchCopResponse) (exit bool) {
	select {
	case b.respChan <- resp:
	case <-b.finishCh:
		exit = true
	}
	return
}

redpajama_set_name: RedPajamaGithub


text:
using Synthesis.FieldTypes.Interfaces;

namespace Synthesis.Testing
{
	public class TestImageField : TestFileField, IImageField
	{
		public TestImageField(string url, int? width = null, int? height = null, string alternateText = null)
			: base(url)
		{
			Width = width;
			Height = height;
			AlternateText = alternateText;
		}

		/// <summary>
		///     Gets the width of the image, if one was entered
		/// </summary>
		public int? Width { get; set; }

		/// <summary>
		///     Gets the height of the image, if one was entered
		/// </summary>
		public int? Height { get; set; }

		/// <summary>
		///     Gets the alt text of the image, if any was entered
		/// </summary>
		public string AlternateText { get; set; }

		/// <summary>
		///     Renders the field using a Sitecore FieldRenderer and returns the result
		/// </summary>
		public string RenderedValue
		{
			get
			{
				string tag = $"<img src=\"{Url ?? string.Empty}\" alt=\"{AlternateText ?? string.Empty}\"";
				if (Width.HasValue)
					tag += " width=\"" + Width.Value + "\"";

				if (Height.HasValue)
					tag += " height=\"" + Height.Value + "\"";

				return tag + " />";
			}
		}
	}
}


text:
cask "gimp" do
  version "2.10.22"
  sha256 "844dc06731cbd8ccaa6ffd4e0c74ad49ed1ecb6ae65db71988102acb6c219d56"

  url "https://download.gimp.org/pub/gimp/v#{version.major_minor}/osx/gimp-#{version}-x86_64-3.dmg"
  name "GIMP"
  name "GNU Image Manipulation Program"
  desc "Free and open-source image editor"
  homepage "https://www.gimp.org/"

  livecheck do
    url "https://www.gimp.org/downloads/"
    strategy :page_match
    regex(%r{href=.*?/gimp-(\d+(?:\.\d+)*)-x86_64-3\.dmg}i)
  end

  auto_updates true

  app "GIMP-#{version.major_minor}.app"
  binary "#{appdir}/GIMP-#{version.major_minor}.app/Contents/MacOS/gimp"

  postflight do
    set_permissions "#{appdir}/GIMP-#{version.major_minor}.app/Contents/MacOS/gimp", "a+rx"
  end

  zap trash: [
    "~/Library/Preferences/org.gimp.gimp-#{version.major_minor}:.plist",
    "~/Library/Application Support/Gimp",
    "~/Library/Saved Application State/org.gimp.gimp-#{version.major_minor}:.savedState",
  ]
end


text:
var async = require('async'),
	wxConfig = require('../config/wx'),
	wx = require('../lib/')(wxConfig);

async.waterfall([
	function(callback){
		wx.login(callback);
	},
	function(callback){
		wx.getGroupList(callback);
	},
	function(groupList, callback){
		wx.getAllUserList(function(err, userList){
			callback(err, groupList, userList);
		});
	}
], function(err, groupList, userList){
	if (err) throw err;
	console.log(groupList);
	console.log(userList);
	console.log('Users Count:', userList.length);
});


text:
#ifndef __DECOBOSS_H__
#define __DECOBOSS_H__

class BossBodyPart;
class BossWeakpoint;
class BossCompositeBodyPart;

namespace decobossai {
class DecoBossAIState;
class Stage1AI;
class Stage2AI;
class Stage3AI;
};

#include "Boss.h"
#include "GameLevel.h"
#include "TeslaBlock.h"

class DecoBoss : public Boss {

    friend Boss* Boss::BuildStyleBoss(GameModel* gameModel, const GameWorld::WorldStyle& style);
    friend class decobossai::DecoBossAIState;
    friend class decobossai::Stage1AI;
    friend class decobossai::Stage2AI;
    friend class decobossai::Stage3AI;

public:
    static const float CORE_HEIGHT;
    static const float CORE_WIDTH;
    static const float HALF_CORE_HEIGHT;
    static const float CORE_HALF_DEPTH;

    static const float SIDE_BODY_PART_WIDTH;
    static const float SIDE_BODY_PART_HEIGHT;

    static const float FULL_BODY_TOTAL_HEIGHT;
    static const float FULL_BODY_TOTAL_WIDTH;

    static const float CORE_AND_ARMS_WIDTH;

    static const float LIGHTNING_RELAY_GLOW_SIZE;

    static const float ARM_WIDTH;
    static const float ARM_NOT_EXTENDED_HEIGHT;

    static const float ARM_POS_X_OFFSET;
    static const float ARM_Y_OFFSET;
    static const float ARM_Z_OFFSET;

    static const float ARM_SCOPING_SEG1_Y_RETRACT_TRANSLATION;
    static const float ARM_SCOPING_SEG2_Y_RETRACT_TRANSLATION;
    static const float ARM_SCOPING_SEG3_Y_RETRACT_TRANSLATION;
    static const float ARM_SCOPING_SEG4_Y_RETRACT_TRANSLATION;

    static const float ARM_ORIGIN_TO_HAND_END;
    static const float HAND_WIDTH;

    static const float SIDE_POS_X_OFFSET;
    static const float SIDE_Y_OFFSET;
    static const float SIDE_Z_OFFSET;

    static const float LIGHTNING_FIRE_POS_X;
    static const float LIGHTNING_FIRE_POS_Y;
    static const float LIGHTNING_FIRE_POS_Z;

    static const float ITEM_LOAD_OFFSET_Y;

    static const int NUM_EYES = 6;
    static const float EYE1_OFFSET_X;
    static const float EYE2_OFFSET_X;
    static const float EYE1_2_OFFSET_Y;
    static const float EYE3_OFFSET_Y;
    static const float EYE4_OFFSET_Y;
    static const float EYE5_OFFSET_Y;
    static const float EYE6_OFFSET_Y;

    ~DecoBoss();

    // Middle body parts
    const BossCompositeBodyPart* GetMiddleBody() const { return static_cast<const BossCompositeBodyPart*>(this->bodyParts[this->middleBodyIdx]); }
    const BossBodyPart* GetCore() const { return static_cast<const BossBodyPart*>(this->bodyParts[this->coreIdx]); }
    const BossBodyPart* GetLightningRelay() const { return static_cast<const BossBodyPart*>(this->bodyParts[this->lightningRelayIdx]); }

    // Arms (part of the middle body)
    const BossCompositeBodyPart* GetLeftArm() const { return static_cast<const BossCompositeBodyPart*>(this->bodyParts[this->leftArmIdx]); }
    const BossBodyPart* GetLeftArmGear() const { return static_cast<const BossBodyPart*>(this->bodyParts[this->leftArmGearIdx]); }
    const BossBodyPart* GetLeftArmScopingSeg1() const { return static_cast<const BossBodyPart*>(this->bodyParts[this->leftArmScopingSeg1Idx]); }
    const BossBodyPart* GetLeftArmScopingSeg2() const { return static_cast<const BossBodyPart*>(this->bodyParts[this->leftArmScopingSeg2Idx]); }
    const BossBodyPart* GetLeftArmScopingSeg3() const { return static_cast<const BossBodyPart*>(this->bodyParts[this->leftArmScopingSeg3Idx]); }
    const BossBodyPart* GetLeftArmScopingSeg4() const { return static_cast<const BossBodyPart*>(this->bodyParts[this->leftArmScopingSeg4Idx]); }
    const BossBodyPart* GetLeftArmHand() const { return static_cast<const BossBodyPart*>(this->bodyParts[this->leftArmHandIdx]); }

    const BossCompositeBodyPart* GetRightArm() const { return static_cast<const BossCompositeBodyPart*>(this->bodyParts[this->rightArmIdx]); }
    const BossBodyPart* GetRightArmGear() const { return static_cast<const BossBodyPart*>(this->bodyParts[this->rightArmGearIdx]); }
    const BossBodyPart* GetRightArmScopingSeg1() const { return static_cast<const BossBodyPart*>(this->bodyParts[this->rightArmScopingSeg1Idx]); }
    const BossBodyPart* GetRightArmScopingSeg2() const { return static_cast<const BossBodyPart*>(this->bodyParts[this->rightArmScopingSeg2Idx]); }
    const BossBodyPart* GetRightArmScopingSeg3() const { return static_cast<const BossBodyPart*>(this->bodyParts[this->rightArmScopingSeg3Idx]); }
    const BossBodyPart* GetRightArmScopingSeg4() const { return static_cast<const BossBodyPart*>(this->bodyParts[this->rightArmScopingSeg4Idx]); }
    const BossBodyPart* GetRightArmHand() const { return static_cast<const BossBodyPart*>(this->bodyParts[this->rightArmHandIdx]); }

    // Left and right body parts
    const BossBodyPart* GetLeftBody() const { return static_cast<const BossBodyPart*>(this->bodyParts[this->leftBodyIdx]); }
    const BossBodyPart* GetRightBody() const { return static_cast<const BossBodyPart*>(this->bodyParts[this->rightBodyIdx]); }

    float GetCurrentHeight() const;
    float GetCurrentWidth() const;

    Point2D GetItemDropPosition() const;
    static float GetItemDropAnimDisplacement();

    Point3D GetLeftLightningRelayOffset() const;
    Point3D GetRightLightningRelayOffset() const;

    static Vector3D GetLightningFireVec3D() { return Vector3D(LIGHTNING_FIRE_POS_X, LIGHTNING_FIRE_POS_Y, LIGHTNING_FIRE_POS_Z); }
    static Vector2D GetLightningFireVec2D() { return Vector2D(LIGHTNING_FIRE_POS_X, LIGHTNING_FIRE_POS_Y); }

    bool IsRightBodyStillAlive() const;
    bool IsLeftBodyStillAlive() const;

    Point3D GetEyeClusterCenterPosition() const;
    const Vector2D& GetEyeOffset(int idx) const;

    // Inherited from Boss
    bool ProjectilePassesThrough(const Projectile* projectile) const;
    //bool ProjectileIsDestroyedOnCollision(const Projectile* projectile, BossBodyPart* collisionPart) const;

private:
    static const float SCALE_AMT;

    static const int LEVEL_WIDTH_NUM_PIECES  = 25;
    static const int LEVEL_HEIGHT_NUM_PIECES = 32;
    static const int CONFINE_X_PADDING_NUM_PIECES = 5;
    static const int CONFINE_Y_TOP_PADDING_NUM_PIECES = 2;
    static const float MOVEMENT_PADDING_X;
    static const float MOVEMENT_PADDING_Y;
    static const float MOVEMENT_MIN_Y_BOUNDARY;
    static const float ROTATION_Y_POSITION;

    static const int LEFT_TESLA_BLOCK_X_IDX  = CONFINE_X_PADDING_NUM_PIECES - 1;
    static const int LEFT_TESLA_BLOCK_Y_IDX  = 17;
    static const int RIGHT_TESLA_BLOCK_X_IDX = LEVEL_WIDTH_NUM_PIECES - CONFINE_X_PADDING_NUM_PIECES;
    static const int RIGHT_TESLA_BLOCK_Y_IDX = LEFT_TESLA_BLOCK_Y_IDX;

    static const float LEFT_ARM_HORIZ_ORIENT_ROT_ANGLE_IN_DEGS;
    static const float RIGHT_ARM_HORIZ_ORIENT_ROT_ANGLE_IN_DEGS;

    static const float ATTACK_PADDLE_WITH_BOTH_ARMS_MIN_Y_POS;
    static const float ATTACK_PADDLE_WITH_BOTH_ARMS_MAX_Y_POS;
    static const float ATTACK_PADDLE_WITH_ONE_ARM_MIN_Y_POS;
    static const float ATTACK_PADDLE_WITH_ONE_ARM_MAX_Y_POS;

    size_t middleBodyIdx, leftBodyIdx, rightBodyIdx;
    size_t coreIdx, lightningRelayIdx;
    size_t leftArmIdx, leftArmGearIdx, leftArmScopingSeg1Idx, leftArmScopingSeg2Idx, leftArmScopingSeg3Idx, leftArmScopingSeg4Idx, leftArmHandIdx;
    size_t rightArmIdx, rightArmGearIdx, rightArmScopingSeg1Idx, rightArmScopingSeg2Idx, rightArmScopingSeg3Idx, rightArmScopingSeg4Idx, rightArmHandIdx;

    std::vector<Vector2D> eyeOffsets;

    DecoBoss();

    // Inherited from Boss
    void Init(float startingX, float startingY, const std::vector<std::vector<LevelPiece*> >& levelPieces);

    BossCompositeBodyPart* BuildArm(BossCompositeBodyPart* middleBody, size_t& armIdx, size_t& gearIdx,
        size_t& scoping1Idx, size_t& scoping2Idx, size_t& scoping3Idx, size_t& scoping4Idx, size_t& handIdx);

    void GenerateArmAndRemainingBoundingLines();

    BossBodyPart* GetCoreEditable() { return static_cast<BossBodyPart*>(this->bodyParts[this->coreIdx]); }
    BossBodyPart* GetLeftBodyEditable() { return static_cast<BossBodyPart*>(this->bodyParts[this->leftBodyIdx]); }
    BossBodyPart* GetRightBodyEditable() { return static_cast<BossBodyPart*>(this->bodyParts[this->rightBodyIdx]); }

    BossCompositeBodyPart* GetLeftArmEditable() { return static_cast<BossCompositeBodyPart*>(this->bodyParts[this->leftArmIdx]); }
    BossBodyPart* GetLeftArmScopingSeg1Editable() { return static_cast<BossBodyPart*>(this->bodyParts[this->leftArmScopingSeg1Idx]); }
    BossBodyPart* GetLeftArmScopingSeg2Editable() { return static_cast<BossBodyPart*>(this->bodyParts[this->leftArmScopingSeg2Idx]); }
    BossBodyPart* GetLeftArmScopingSeg3Editable() { return static_cast<BossBodyPart*>(this->bodyParts[this->leftArmScopingSeg3Idx]); }
    BossBodyPart* GetLeftArmScopingSeg4Editable() { return static_cast<BossBodyPart*>(this->bodyParts[this->leftArmScopingSeg4Idx]); }
    BossBodyPart* GetLeftArmHandEditable() { return static_cast<BossBodyPart*>(this->bodyParts[this->leftArmHandIdx]); }

    BossCompositeBodyPart* GetRightArmEditable() { return static_cast<BossCompositeBodyPart*>(this->bodyParts[this->rightArmIdx]); }
    BossBodyPart* GetRightArmScopingSeg1Editable() { return static_cast<BossBodyPart*>(this->bodyParts[this->rightArmScopingSeg1Idx]); }
    BossBodyPart* GetRightArmScopingSeg2Editable() { return static_cast<BossBodyPart*>(this->bodyParts[this->rightArmScopingSeg2Idx]); }
    BossBodyPart* GetRightArmScopingSeg3Editable() { return static_cast<BossBodyPart*>(this->bodyParts[this->rightArmScopingSeg3Idx]); }
    BossBodyPart* GetRightArmScopingSeg4Editable() { return static_cast<BossBodyPart*>(this->bodyParts[this->rightArmScopingSeg4Idx]); }
    BossBodyPart* GetRightArmHandEditable() { return static_cast<BossBodyPart*>(this->bodyParts[this->rightArmHandIdx]); }

    float GetSideToSideDropStateDistance() const;
    float GetYBallMaxForLevelRotate() const;
    Point2D GetFarLeftDropStatePosition() const;
    Point2D GetFarRightDropStatePosition() const;

    static float GetMinXOfConfines() { return DecoBoss::CONFINE_X_PADDING_NUM_PIECES * LevelPiece::PIECE_WIDTH; }
    static float GetMaxXOfConfines() { return (DecoBoss::LEVEL_WIDTH_NUM_PIECES - DecoBoss::CONFINE_X_PADDING_NUM_PIECES) * LevelPiece::PIECE_WIDTH; }
    static float GetMaxYOfConfines() { return (DecoBoss::LEVEL_HEIGHT_NUM_PIECES - DecoBoss::CONFINE_Y_TOP_PADDING_NUM_PIECES) * LevelPiece::PIECE_HEIGHT; }

    static float GetMovementMinXBound() { return DecoBoss::GetMinXOfConfines() + DecoBoss::MOVEMENT_PADDING_X; }
    static float GetMovementMaxXBound() { return DecoBoss::GetMaxXOfConfines() - DecoBoss::MOVEMENT_PADDING_X; }
    static float GetMovementMinYBound() { return DecoBoss::MOVEMENT_MIN_Y_BOUNDARY; }
    static float GetMovementMaxYBound() { return DecoBoss::GetMaxYOfConfines() - DecoBoss::MOVEMENT_PADDING_Y; }

    float GetRandomAttackPaddleYPos() const;
    static Point2D GetBossPositionForLevelRotation();

    static TeslaBlock* GetLeftTeslaBlock(const GameLevel& level);
    static TeslaBlock* GetRightTeslaBlock(const GameLevel& level);

    DISALLOW_COPY_AND_ASSIGN(DecoBoss);
};

inline float DecoBoss::GetCurrentHeight() const {
    return ((this->IsRightBodyStillAlive() || this->IsLeftBodyStillAlive()) ? FULL_BODY_TOTAL_HEIGHT : CORE_HEIGHT);
}

inline float DecoBoss::GetCurrentWidth() const {
    return ((this->IsRightBodyStillAlive() || this->IsLeftBodyStillAlive()) ? FULL_BODY_TOTAL_WIDTH : CORE_AND_ARMS_WIDTH);
}

inline Point2D DecoBoss::GetItemDropPosition() const {
    return this->GetCore()->GetTranslationPt2D() + Vector2D(0.0f, DecoBoss::ITEM_LOAD_OFFSET_Y - DecoBoss::GetItemDropAnimDisplacement());
}

inline float DecoBoss::GetItemDropAnimDisplacement() {
    return GameItem::ITEM_HEIGHT;
}

inline Point3D DecoBoss::GetLeftLightningRelayOffset() const {
    return Point3D(-1.049f, -6.427f, 0.975f);
}

inline Point3D DecoBoss::GetRightLightningRelayOffset() const {
    return Point3D(1.049f, -6.427f, 0.975f);
}

inline bool DecoBoss::IsRightBodyStillAlive() const {
    return !this->GetRightBody()->GetIsDestroyed();
}
inline bool DecoBoss::IsLeftBodyStillAlive() const {
    return !this->GetLeftBody()->GetIsDestroyed();
}

inline Point3D DecoBoss::GetEyeClusterCenterPosition() const {
    return this->GetCore()->GetTranslationPt3D() + Vector3D(0.0f, 2.844f, 1.2f);
}

inline const Vector2D& DecoBoss::GetEyeOffset(int idx) const {
    return this->eyeOffsets[idx];
}

inline float DecoBoss::GetSideToSideDropStateDistance() const {
    return DecoBoss::GetMaxXOfConfines() - DecoBoss::GetMinXOfConfines() - this->GetCurrentWidth();
}

inline float DecoBoss::GetYBallMaxForLevelRotate() const {
    return (DecoBoss::ROTATION_Y_POSITION - 1.0f);
}

inline Point2D DecoBoss::GetFarLeftDropStatePosition() const {
    return Point2D(DecoBoss::GetMovementMinXBound() + this->GetCurrentWidth() / 2.0f, DecoBoss::GetMovementMinYBound() + 0.75f * this->GetCurrentHeight());
}
inline Point2D DecoBoss::GetFarRightDropStatePosition() const {
    return Point2D(DecoBoss::GetMovementMaxXBound() - this->GetCurrentWidth() / 2.0f, DecoBoss::GetMovementMinYBound() + 0.75f * this->GetCurrentHeight());
}

inline Point2D DecoBoss::GetBossPositionForLevelRotation() {
    return Point2D(
        DecoBoss::GetMinXOfConfines() + ((DecoBoss::GetMaxXOfConfines() - DecoBoss::GetMinXOfConfines()) / 2.0f), DecoBoss::ROTATION_Y_POSITION);
}

inline TeslaBlock* DecoBoss::GetLeftTeslaBlock(const GameLevel& level) {
    LevelPiece* block = level.GetLevelPieceFromCurrentLayout(LEFT_TESLA_BLOCK_Y_IDX, LEFT_TESLA_BLOCK_X_IDX);
    assert(block->GetType() == LevelPiece::Tesla);
    return static_cast<TeslaBlock*>(block);
}

inline TeslaBlock* DecoBoss::GetRightTeslaBlock(const GameLevel& level) {
    LevelPiece* block = level.GetLevelPieceFromCurrentLayout(RIGHT_TESLA_BLOCK_Y_IDX, RIGHT_TESLA_BLOCK_X_IDX);
    assert(block->GetType() == LevelPiece::Tesla);
    return static_cast<TeslaBlock*>(block);
}

#endif // __DECOBOSS_H__


text:
namespace Google.Cloud.Compute.V1.Snippets
{
    // [START compute_v1_generated_FirewallPolicies_Get_async_flattened]
    using Google.Cloud.Compute.V1;
    using System.Threading.Tasks;

    public sealed partial class GeneratedFirewallPoliciesClientSnippets
    {
        /// <summary>Snippet for GetAsync</summary>
        /// <remarks>
        /// This snippet has been automatically generated and should be regarded as a code template only.
        /// It will require modifications to work:
        /// - It may require correct/in-range values for request initialization.
        /// - It may require specifying regional endpoints when creating the service client as shown in
        ///   https://cloud.google.com/dotnet/docs/reference/help/client-configuration#endpoint.
        /// </remarks>
        public async Task GetAsync()
        {
            // Create client
            FirewallPoliciesClient firewallPoliciesClient = await FirewallPoliciesClient.CreateAsync();
            // Initialize request argument(s)
            string firewallPolicy = "";
            // Make the request
            FirewallPolicy response = await firewallPoliciesClient.GetAsync(firewallPolicy);
        }
    }
    // [END compute_v1_generated_FirewallPolicies_Get_async_flattened]
}


text:
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="?attr/selectableItemBackground"
    tools:parentTag="android.widget.LinearLayout">

    <TextView
        android:id="@+id/title"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingLeft="@dimen/padding_horizontal"
        android:paddingRight="@dimen/padding_horizontal"
        android:paddingTop="24dp"
        android:textColor="@color/text_color"
        android:textSize="18sp"
        tools:text="Title" />

    <TextView
        android:id="@+id/subtitle"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingLeft="@dimen/padding_horizontal"
        android:paddingRight="@dimen/padding_horizontal"
        android:paddingTop="8dp"
        android:textColor="@color/text_color"
        android:textSize="14sp"
        android:visibility="gone"
        tools:text="Subtitle" />

    <Space
        android:layout_width="0dp"
        android:layout_height="24dp" />

    <View
        android:layout_width="match_parent"
        android:layout_height="@dimen/divider"
        android:layout_marginLeft="@dimen/padding_horizontal"
        android:layout_marginRight="@dimen/padding_horizontal"
        android:background="@color/divider" />

</merge>


text:
<?php

namespace Application\Migrations;

use Doctrine\DBAL\Migrations\AbstractMigration;
use Doctrine\DBAL\Schema\Schema;

/**
 * Auto-generated Migration: Please modify to your needs!
 */
class Version20141227104409 extends AbstractMigration
{
    /**
     * @param Schema $schema
     *
     * @throws \Doctrine\DBAL\Migrations\AbortMigrationException
     */
    public function up(Schema $schema)
    {
        // this up() migration is auto-generated, please modify it to your needs
        $this->abortIf($this->connection->getDatabasePlatform()->getName() != 'mysql', 'Migration can only be executed safely on \'mysql\'.');

        $this->addSql('CREATE TABLE `place` (`id` INT AUTO_INCREMENT NOT NULL, `create_dt` DATETIME NOT NULL, `update_dt` DATETIME NOT NULL, `name` VARCHAR(75) NOT NULL, `latitude` NUMERIC(10, 8) NOT NULL, `longitude` NUMERIC(10, 8) NOT NULL, `deleted_at` DATETIME DEFAULT NULL, `is_online` TINYINT(1) DEFAULT \'0\' NOT NULL, PRIMARY KEY(`id`))');
    }

    /**
     * @param Schema $schema
     *
     * @throws \Doctrine\DBAL\Migrations\AbortMigrationException
     */
    public function down(Schema $schema)
    {
        // this down() migration is auto-generated, please modify it to your needs
        $this->abortIf($this->connection->getDatabasePlatform()->getName() != 'mysql', 'Migration can only be executed safely on \'mysql\'.');

        $this->addSql('DROP TABLE `place`');
    }
}


text:
// -----------------------------------------------------------------------
// <copyright file="UsageSummaryBase.java" company="Microsoft">
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
// -----------------------------------------------------------------------

package com.microsoft.store.partnercenter.models.usage;

import java.util.Locale;

import org.joda.time.DateTime;

import com.microsoft.store.partnercenter.models.ResourceBaseWithLinks;
import com.microsoft.store.partnercenter.models.StandardResourceLinks;

/**
 * This class defines the common properties for all usage summary
 */
public abstract class UsageSummaryBase
    extends ResourceBaseWithLinks<StandardResourceLinks>
{
    /**
     * Gets or sets the resource unique identifier. In the context of CustomerMonthlyUsageRecord this id is the customer
     * id.
     */
    private String __ResourceId;

    public String getResourceId()
    {
        return __ResourceId;
    }

    public void setResourceId( String value )
    {
        __ResourceId = value;
    }

    /**
     * Gets or sets the name of the resource. In the context of CustomerMonthlyUsageRecord this name is the customer
     * name.
     */
    private String __ResourceName;

    public String getResourceName()
    {
        return __ResourceName;
    }

    public void setResourceName( String value )
    {
        __ResourceName = value;
    }

    /**
     * Gets or sets the start date of the current billing period.
     */
    private DateTime __BillingStartDate;

    public DateTime getBillingStartDate()
    {
        return __BillingStartDate;
    }

    public void setBillingStartDate( DateTime value )
    {
        __BillingStartDate = value;
    }

    /**
     * Gets or sets the end date of the current billing period.
     */
    private DateTime __BillingEndDate;

    public DateTime getBillingEndDate()
    {
        return __BillingEndDate;
    }

    public void setBillingEndDate( DateTime value )
    {
        __BillingEndDate = value;
    }

    /**
     * Gets or sets the total estimated cost based on current usage from the start of the billing period.
     */
    private double __TotalCost;

    public double getTotalCost()
    {
        return __TotalCost;
    }

    public void setTotalCost( double value )
    {
        __TotalCost = value;
    }

    /**
     * Gets or sets the currency locale.
     */
    private Locale __CurrencyLocale;

    public Locale getCurrencyLocale()
    {
        return __CurrencyLocale;
    }

    public void setCurrencyLocale( Locale value )
    {
        __CurrencyLocale = value;
    }

    /**
     * Gets or sets the Last Modified date.
     */
    private DateTime __LastModifiedDate;

    public DateTime getLastModifiedDate()
    {
        return __LastModifiedDate;
    }

    public void setLastModifiedDate( DateTime value )
    {
    	__LastModifiedDate = value;
    }

}


text:
CalibrateMPU6050
================

Calibrate MPU6050 procedure

Setup
-----

1. Copy the folders from libraries/ to your Arduino IDE workspace libraries folder
2. Compile and Run in Arduino IDE

How to use
----------

* For the first 180 seconds, the Calibration procedure is in WARM-UP mode.
* After 180 seconds, the procedure adjusts the offset value to satisfy the sensor reading to expected value.
* By observing the offset values, the mean of sensor readings and the standard deviation in Serial Monitor, pick the offset values when they become stable.
* Use the offset value in your program as below:

```arduino
#include "I2Cdev.h"
#include "MPU6050.h"
#include "Wire.h"

#define MPU6050_ACCEL_OFFSET_X -773
#define MPU6050_ACCEL_OFFSET_Y 3278
#define MPU6050_ACCEL_OFFSET_Z 1660
#define MPU6050_GYRO_OFFSET_X  -24
#define MPU6050_GYRO_OFFSET_Y  -62
#define MPU6050_GYRO_OFFSET_Z  -6

MPU6050 mpu;

void setup() {
    Wire.begin();
    mpu.initialize();
    mpu.setXAccelOffset(MPU6050_ACCEL_OFFSET_X);
    mpu.setYAccelOffset(MPU6050_ACCEL_OFFSET_Y);
    mpu.setZAccelOffset(MPU6050_ACCEL_OFFSET_Z);
    mpu.setXGyroOffset(MPU6050_GYRO_OFFSET_X);
    mpu.setYGyroOffset(MPU6050_GYRO_OFFSET_Y);
    mpu.setZGyroOffset(MPU6050_GYRO_OFFSET_Z);
}

void loop() {
}
```


text:
import * as childProcess from 'child_process';
import * as del from 'del';
import * as gulp from 'gulp';
import * as nodemon from 'gulp-nodemon';
import * as runSequence from 'run-sequence';
import tslint from 'gulp-tslint';
import * as tsc from 'gulp-typescript';

const webpack = require('webpack-stream');

gulp.task('default', (callback) => {
  runSequence('webpack', 'build_server', 'run_server', 'start-mongo', callback);
});

gulp.task('lint', () => {
  gulp.src('./server/**/*.ts')
  .pipe(tslint({
    formatter: 'codeFrame'
  }))
  .pipe(tslint.report({
    summarizeFailureOutput: true
  }));
});

gulp.task('lint:fix', () => {
  gulp.src('./server/**/*.ts')
  .pipe(tslint({
    fix: true,
    formatter: 'codeFrame'
  }))
  .pipe(tslint.report({
    summarizeFailureOutput: true
  }));
});

// FIXME(sapzape): Temporarily separated until the lint error in the client area
// is resolved. Please see https://github.com/lunchclass/absolute/issues/533
gulp.task('lint_client', () => {
  gulp.src('./client/**/*.ts')
  .pipe(tslint({
    formatter: 'codeFrame'
  }))
  .pipe(tslint.report({
    summarizeFailureOutput: true
  }));
});

// FIXME(sapzape): Temporarily separated until the lint error in the client area
// is resolved. Please see https://github.com/lunchclass/absolute/issues/533
gulp.task('lint_client:fix', () => {
  gulp.src('./client/**/*.ts')
  .pipe(tslint({
    fix: true,
    formatter: 'codeFrame'
  }))
  .pipe(tslint.report({
    summarizeFailureOutput: true
  }));
});

gulp.task('test', ['webpack'], runCommand('jest'));

gulp.task('run_server', () => {
  process.env.NODE_PATH = "out/";
  nodemon({
    script: 'out/server/server.js',
    ignore: ['out/']
  });
});

gulp.task('clean', () => {
    return del('out', {force: true});
});

gulp.task('webpack', () => {
  return gulp.src('./client/src/*.ts')
    .pipe(webpack(require('./webpack.config.ts')))
    .pipe(gulp.dest('out/client'))
});

const tsProject = tsc.createProject('tsconfig.json');
gulp.task('build_server', () => {
  return tsProject.src()
    .pipe(tsProject())
    .js
    .pipe(gulp.dest('out/server/'));
});

const mongodb_path = './third_party/mongodb';
gulp.task('start-mongo', runCommand(`${mongodb_path}/bin/mongod --dbpath ${mongodb_path}`));
gulp.task('stop-mongo', runCommand(`${mongodb_path}/bin/mongo --eval "use admin; db.shutdownServer();`));

function runCommand(command: string) {
  return (cb: any) => {
    childProcess.spawn(command, [], {
      shell: true,
      stdio : ['inherit', 'inherit', 'inherit']
    });
    cb();
  }
}