﻿import {Component, Input, Output, OnInit, EventEmitter, Injectable} from '@angular/core';
import { NgClass, NgStyle} from '@angular/common';
import {FormsModule } from '@angular/forms';
import { Http, Headers, RequestOptions, Response } from '@angular/http';

import { ErrorInCommand, BaseEntity} from './../../models/index';
import { IFileDataStructure, IEmptyConstruct, DataChanged, IDataStructure, IEntityDataService, CustomizeComponentDescriptor, IEntityContainer } from './../../interfaces/index';
import { FileUploader, FileUploaderOptions } from 'ng2-file-upload/ng2-file-upload';

@Component({
	moduleId: module.id,
    selector: 'file-upload',
    templateUrl: './file-upload-wrapper.control.html'
})
@Injectable()
export class FileUploadWrapperControl implements OnInit {

    @Input() public url: string;
    @Input() public queueLimit: number; // Limit file uploads
    @Input() public maxFileSize: number; //Input MB
    @Input() public autoUpload: boolean;
    @Input() public allowedMimeType: Array<string>;
    @Input() public allowedFileType: Array<string>;
    @Input() set entityType(EntityType: IEmptyConstruct) {
        this.dataEntity = EntityType;
    }

    //Public Events
    @Output() public successEvent: EventEmitter<string[]> = new EventEmitter<string[]>();
    @Output() public errorEvent: EventEmitter<ErrorInCommand> = new EventEmitter<ErrorInCommand>();
    @Output() public FileUploadedList: string[] = [];

    private fileEntity: IFileDataStructure;
    private dataEntity: IEmptyConstruct = BaseEntity;
    private uploader: FileUploader;
    private uploaderOptions: FileUploaderOptions = {};
    private totallyUploaded: number = 0;
    private multiple: boolean = true;
    private isShowFileList: boolean = false;
    private errorFileSize: boolean = false;
    constructor(private entityService: IEntityDataService, private http: Http) {

    }

    public ngOnInit(): any {
        this.uploaderOptions.queueLimit = this.queueLimit;
        this.uploaderOptions.maxFileSize = this.maxFileSize * 1024 * 1024;
        this.uploaderOptions.autoUpload = this.autoUpload;
        this.uploaderOptions.allowedMimeType = this.allowedMimeType;
        this.uploaderOptions.allowedFileType = this.allowedFileType;

        this.uploader = new FileUploader(this.uploaderOptions);
        this.multiple = (!this.queueLimit || this.queueLimit > 1);
    }

    public getTarget(obj: any): any {
        let targ: any;
        let e: any = obj;
        if (e.target) targ = e.target;
        else if (e.srcElement) targ = e.srcElement;
        if (targ.nodeType == 3) // defeat Safari bug
            targ = targ.parentNode;
        return targ;
    }

    public onChange($event: any): void {
        let target: any = this.getTarget($event);
        if (target.files.length > 0 && target.files[0].size > this.maxFileSize * 1024 * 1024) {
            this.errorFileSize = true;
        }
        else {
            this.errorFileSize = false;
            this.uploader.queue.pop();
            this.uploader.addToQueue(target.files);
            this.isShowFileList = this.uploader.queue.length > 0;
            if (this.autoUpload)
                this.uploadAll();
        }
    }

    public uploadAll() {
        var that = this;
        that.totallyUploaded = 0;
        for (var index = 0; index < that.uploader.queue.length; index++) {
            var fileItem = that.uploader.queue[index];
            var file = fileItem._file;
            this.readFileToBase64(file, index);
        }
    }

    private readFileToBase64(file: File, fileIndex: number) {
        var dataBinary: string;
        var reader = new FileReader();
        var _fileTemp = file;

        reader.onload = file => {
            var contents: any = file.target;
            dataBinary = btoa(contents.result);

            //Convert string to byte array
            var byteArray = this.convertStringToByteArray(dataBinary);
            //save file to database
            if (this.url === undefined || this.url === null)
                this.saveFileToDatabase(byteArray, _fileTemp, fileIndex);
            else
                this.saveFileToDatabaseByUrl(byteArray, _fileTemp, fileIndex);
        };

        reader.readAsBinaryString(file);
    }

    private saveFileToDatabase(dataBinary: any, file: File, fileIndex: number) {
        var that = this;
        var fileEntity: IFileDataStructure = new this.dataEntity();
        fileEntity.FileDataBinary = dataBinary;
        fileEntity.Name = file.name;
        fileEntity.Type = file.type;
        fileEntity.Size = file.size;
        var myFileEntityAction = "SaveFile_" + Math.random() * 10000013;
        that.entityService.dataObserver.subscribe((newEntity: DataChanged) => {
            if (newEntity.ID == myFileEntityAction) {
                //update progress bar
                that.totallyUploaded++;
                let ratio = 100 / that.uploader.queue.length;
                that.uploader.progress = that.totallyUploaded * ratio;

                //Update status file item
                that.uploader.queue[fileIndex].isSuccess = true;
                that.uploader.queue[fileIndex].isError = false;

                //Add item into FileUploaded
                that.FileUploadedList.push(newEntity.data[0].ID);
                that.uploader.queue[fileIndex].alias = newEntity.data[0].ID;

                //public event show file item upload successed
                that.successEvent.next(that.FileUploadedList);
            }
        });
        that.entityService.createEntity(this.dataEntity, fileEntity, myFileEntityAction);
        that.entityService.errorObserver.subscribe((newEntity: DataChanged) => {
            //Update status file item
            that.uploader.queue[fileIndex].isSuccess = false;
            that.uploader.queue[fileIndex].isError = true;
            this.errorEvent.emit({ ID: "", errorData: null, errorMessage: 'File not upload', inputData: fileEntity });
        });
    }

    private saveFileToDatabaseByUrl(dataBinary: any, file: File, fileIndex: number) {
        var that = this;
        var fileEntity: IFileDataStructure = new this.dataEntity();
        fileEntity.FileDataBinary = dataBinary;
        fileEntity.Name = file.name;
        fileEntity.Type = file.type;
        fileEntity.Size = file.size;
        let headers = new Headers({ 'Content-Type': 'application/json' });
        let options = new RequestOptions({ headers: headers });
        that.http.post(that.url, JSON.stringify(that.fileEntity), options)
            .subscribe(data => {
                //update progress bar
                let ratio = 100 / that.uploader.queue.length;
                that.uploader.progress = (fileIndex + 1) * ratio;

                //Update status file item
                that.uploader.queue[fileIndex].isSuccess = true;
                that.uploader.queue[fileIndex].isError = false;

                //Add item into FileUploaded
                that.FileUploadedList.push(data.json().ID);
                that.uploader.queue[fileIndex].alias = data.json().ID;

                //public event show file item upload successed
                that.successEvent.next(data.json().ID);
            },
            (error: any) => {
                //Update status file item
                that.uploader.queue[fileIndex].isSuccess = false;
                that.uploader.queue[fileIndex].isError = true;
                this.errorEvent.emit({ ID: "", errorData: error, errorMessage: 'File not upload', inputData: that.fileEntity });
            }
            );
    }
    //------------------ Remove File -----------------------
    removeAll() {
        for (let file of this.uploader.queue) {
            this.removeFile(file);
        }
    }
    removeFile(file: any) {
        var that = this;
        var myFileEntityAction: string = 'FileUploadDelete_' + (Math.random() * 1000013);

        if (file.alias !== "file") {
            this.entityService.fetchEntity(that.dataEntity, file.alias).then((res: any) => {
                var fileEntity: IFileDataStructure = res;
                that.entityService.deleteEntity(this.dataEntity, fileEntity);
                file.remove();

                //update progress bar
                let ratio = 100 / that.FileUploadedList.length;
                that.uploader.progress = that.uploader.queue.length * ratio;

                //remove FileUploaded
                that.removeFromFileUploaded(file.alias);
            });
        }
        else {
            file.remove();
        }
        that.isShowFileList = that.uploader.queue.length > 0;
    }
    public removeFromFileUploaded(value: any): void {
        let index = this.FileUploadedList.indexOf(value);
        let item = this.FileUploadedList[index];
        this.FileUploadedList.splice(index, 1);
        this.isShowFileList = !(this.FileUploadedList.length === 0 || this.uploader.queue.length === 0);
    }
    //------------------ Download File -----------------------
    public downloadFileById(fileId: string) {
        this.entityService.fetchEntity(this.dataEntity, fileId).then((res: any) => {
            var fileEntity: IFileDataStructure = res;
            var dataString = this.convertByteArrayToString(fileEntity.FileDataBinary);
            this.downloadFile(dataString, fileEntity.Name);
        });
    }

    private downloadFile(dataBinaryStr: string, fileName: string) {
        var fileUri: string;

        if (dataBinaryStr != null) {
            //console.log(arraybuffer);
            var data = atob(dataBinaryStr);
            // Use typed arrays to convert the binary data to a Blob
            var arraybuffer = new ArrayBuffer(data.length);
            var view = new Uint8Array(arraybuffer);
            for (var i = 0; i < data.length; i++) {
                view[i] = data.charCodeAt(i) & 0xff;
            }
            try {
                // This is the recommended method:
                var blob = new Blob([arraybuffer], { type: 'application/octet-stream' });
            } catch (e) {
                // The BlobBuilder API has been deprecated in favour of Blob, but older
                // browsers don't know about the Blob constructor
                // IE10 also supports BlobBuilder, but since the `Blob` constructor
                //  also works, there's no need to add `MSBlobBuilder`.
                var bb = new MSBlobBuilder();
                bb.append(arraybuffer);

                var blob = bb.getBlob('application/octet-stream'); // <-- Here's the Blob
            }

			// TODO: this is impediment from somewhere
            // saveAs(blob, fileName);
        }
    }

    //------------------ Utils -----------------------
    private convertByteArrayToString(dataByteArray: any) {
        var dataString: string = "";
        for (var i = 0; i < dataByteArray.length; i++) {
            dataString += String.fromCharCode(dataByteArray[i]);
        }
        return dataString;
    }

    private convertStringToByteArray(dataBinaryStr: any) {
        var byteNumbers: number[] = [];
        for (var i = 0; i < dataBinaryStr.length; i++) {
            byteNumbers.push(dataBinaryStr.charCodeAt(i));
        }
        return byteNumbers;
    }
}


