/*
 *
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 *
*/

/*global require, module*/

var MediaFile = require('cordova-plugin-media-capture.MediaFile');
var MediaFileData = require('cordova-plugin-media-capture.MediaFileData');
var CaptureError = require('cordova-plugin-media-capture.CaptureError');

/**
 * Helper function that converts data URI to Blob
 * @param  {String} dataURI Data URI to convert
 * @return {Blob}           Blob, covnerted from DataURI String
 */
function dataURItoBlob(dataURI) {
    // convert base64 to raw binary data held in a string
    // doesn't handle URLEncoded DataURIs
    var byteString = atob(dataURI.split(',')[1]);

    // separate out the mime component
    var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];

    // write the bytes of the string to an ArrayBuffer
    var ab = new ArrayBuffer(byteString.length);
    var ia = new Uint8Array(ab);
    for (var i = 0; i < byteString.length; i++) {
        ia[i] = byteString.charCodeAt(i);
    }

    // write the ArrayBuffer to a blob, and you're done
    return new Blob([ab], { type: mimeString });
}

/**
 * Creates basic camera UI with preview 'video' element and 'Cancel' button
 * Capture starts, when you clicking on preview.
 */
function CameraUI() {

    // Root element for preview 
    var container = document.createElement('div');
    container.style.cssText = "left: 0px; top: 0px; width: 100%; height: 100%; position: fixed; z-index:9999;" +
                                   "padding: 40px; background-color: rgba(0,0,0,0.75);" + 
                                   "text-align:center; visibility: hidden";

    // Set up root element contetnts
    container.innerHTML =
        '<div id="captureHint" style="height:100%; position:relative; display:inline-flex; align-content:flex-start;">' +
        '<h2 style="position: absolute; width: 100%; background-color: rgba(255,255,255,0.25); margin: 0">' +
            'Click on preview to capture image. Click outside of preview to cancel.</h1>' + 
        '<video id="capturePreview" style="height: 100%"></video>' +
        '</div>';

    // Add container element to DOM but do not display it since visibility == hidden
    document.body.appendChild(container);

    // Create fullscreen preview
    var preview = document.getElementById('capturePreview');
    preview.autoplay = true;
    // We'll show preview only when video element content
    // is fully loaded to avoid glitches
    preview.onplay = function () {
        container.style.visibility = 'visible';
    };

    this.container = container;
    this.preview = preview;
}

/**
 * Displays capture preview
 * @param  {Number} count       Number of images to take
 * @param  {Function} successCB Success callback, that accepts data URL of captured image
 * @param  {Function} errorCB   Error callback
 */
CameraUI.prototype.startPreview = function(count, successCB, errorCB) {

    this.preview.onclick = function (e) {
        // proceed with capture here
        // We don't need to propagate click event to parent elements.
        // Otherwise click on vieo element will trigger click event handler
        // for preview root element and cause preview cancellation
        e.stopPropagation();
        // Create canvas element, put video frame on it
        // and save its contant as Data URL
        var canvas = document.createElement('canvas');
        canvas.width = this.videoWidth;
        canvas.height = this.videoHeight;
        canvas.getContext('2d').drawImage(that.preview, 0, 0);
        successCB(canvas.toDataURL('image/jpeg'));
    };

    this.container.onclick = function () {
        // Cancel capture here
        errorCB(new CaptureError(CaptureError.CAPTURE_NO_MEDIA_FILES));
    };

    var that = this;
    navigator.getUserMedia({video: true}, function (previewStream) {
        // Save video stream to be able to stop it later 
        that._previewStream = previewStream;
        that.preview.src = URL.createObjectURL(previewStream);
        // We don't need to set visibility = true for preview element
        // since this will be done automatically in onplay event handler
    }, function (/*err*/) {
        errorCB(new CaptureError(CaptureError.CAPTURE_INTERNAL_ERR));
    });
};

/**
 * Destroys camera preview, removes all elements created
 */
CameraUI.prototype.destroyPreview = function () {
    this.preview.pause();
    this.preview.src = null;
    this._previewStream.stop();
    this._previewStream = null;
    this.container && document.body.removeChild(this.container);
};


module.exports = {

    captureAudio:function(successCallback, errorCallback) {
        errorCallback && errorCallback(new CaptureError(CaptureError.CAPTURE_NOT_SUPPORTED));
    },

    captureVideo:function (successCallback, errorCallback) {
        errorCallback && errorCallback(new CaptureError(CaptureError.CAPTURE_NOT_SUPPORTED));
    },

    captureImage:function (successCallback, errorCallback, args) {

        var fail = function (code) {
            errorCallback && errorCallback(new CaptureError(code || CaptureError.CAPTURE_INTERNAL_ERR));
        };

        var options = args[0];

        var limit = options.limit || 1;
        if (typeof limit !== 'number' || limit < 1) {
            fail(CaptureError.CAPTURE_INVALID_ARGUMENT);
            return;
        }

        // Counter for already taken images
        var imagesTaken = 0;

        navigator.getUserMedia = navigator.getUserMedia ||
                         navigator.webkitGetUserMedia ||
                         navigator.mozGetUserMedia ||
                         navigator.msGetUserMedia;

        if (!navigator.getUserMedia) {
            fail(CaptureError.CAPTURE_NOT_SUPPORTED);
            return;
        }

        var ui = new CameraUI();
        ui.startPreview(limit, function (data) {
            // Check if we're done with capture. If so, then destroy UI
            if (++imagesTaken >= limit) {
                ui.destroyPreview();
            }

            // Array of resultant MediaFiles
            var mediaFiles = [];

            // save data to file here
            window.requestFileSystem(window.TEMPORARY, data.length * limit, function (fileSystem) {
                // If we need to capture multiple files, then append counter to filename
                var fileName = limit <= 1 ? 'image.jpg' : 'image' + imagesTaken + '.jpg';
                fileSystem.root.getFile(fileName, {create: true}, function (file) {
                    file.createWriter(function (writer) {
                        writer.onwriteend = function () {
                            file.getMetadata(function (meta) {
                                mediaFiles.push(new MediaFile(file.name, file.toURL(), 'image/jpeg', meta.modificationTime, meta.size));
                                // Check if we're done with capture. If so, then call a successCallback
                                if (imagesTaken >= limit) {
                                    successCallback(mediaFiles);
                                }
                            }, fail);
                        };
                        writer.onerror = fail;
                        // Since success callback for start preview returns
                        // a base64 encoded string, we need to convert it to blob first
                        writer.write(dataURItoBlob(data));
                    });
                }, fail);
            }, fail);
        }, function (err) {
            ui.destroyPreview();
            fail(err.code);
        });
    },

    getFormatData: function (successCallback, errorCallback, args) {

        var img = document.createElement('img');
        img.src = args[0];
        img.onload = function () {
            successCallback && successCallback(new MediaFileData(null, 0, img.height, img.width, 0));
        };
    }
};

require("cordova/exec/proxy").add("Capture",module.exports);
