///</// <reference path='../file_storage.ts' />

namespace ti_common.file.storage {
    'use strict';
    let TI_WORKSPACE_SERVER: string = '';
    class TICloudConfig implements IPlatformConfig, angular.IServiceProvider {
        setWorkSpaceServer(workspaceURL: string) {
            TI_WORKSPACE_SERVER = workspaceURL;
        }

        // no serivce, just config
        $get() {
            return {};
        }
    }
    class TICloudFile extends ti_common.file.File {

        private static NOT_READY_MESSAGE = 'Uploading';
        static TYPE = 'TICloudFile';

        public notReadyMessage: string = TICloudFile.NOT_READY_MESSAGE;
        private uploadPromise: angular.IPromise<string>;
        constructor(
            name: string,
            private $q: angular.IQService,
            private cloudStorage: TICloudFileStorage,
            size: number = null,
            private mtime: string = null
        ) {
            super(name, TICloudFile.TYPE, size);
        }

        getContents(contentType?: ti_common.file.ContentType): angular.IPromise<ti_common.file.Content> {
            let waitForUploadifNeeded = this.uploadPromise ? this.uploadPromise : this.$q.resolve('');
            if (!contentType) {
                contentType = ti_common.file.ContentType.Text;
            }
            return waitForUploadifNeeded.then(
                () => {
                    return this.cloudStorage.openFile(this.name, contentType);
                }
            );
        }

        delete(): angular.IPromise<boolean> {
            return this.cloudStorage.deleteFile(this.name);
        }

        // check to see if the name clashes with an existing file
        // if yes generate a new name
        createNewNameIfNeeded(): angular.IPromise<string> {

            let fileName = this.name;
            let fileNameOnly = fileName.substring(0, fileName.indexOf('.'));
            let fileExtensionOnly = fileName.substring(fileName.indexOf('.') + 1);
            let fileFilter = new RegExp(fileNameOnly + '.*' + fileExtensionOnly);
            let found = false;
            return this.cloudStorage.getFileList(fileFilter).then((files) => {
                let biggestNum = 0;
                _.each(files, (fd) => {
                    let file = fd.name;
                    if (file === fileName) {
                        found = true;
                    }

                    let regex = new RegExp(fileNameOnly + ' \\(\\d+\\)\\.' + fileExtensionOnly);
                    if (file.match(regex)) {
                        let idx1 = file.indexOf('(');
                        let idx2 = file.indexOf(')');
                        let numStr = file.substring(idx1 + 1, idx2);
                        let num = parseInt(numStr);
                        biggestNum = (biggestNum > num) ? biggestNum : num;
                    }
                });

                if (!found) {
                    return fileName;
                } else if (found && biggestNum === 0) {
                    return fileNameOnly + ' (1).' + fileExtensionOnly;
                } else {
                    biggestNum++;
                    return fileNameOnly + ' (' + biggestNum + ').' + fileExtensionOnly;
                }
            })
                .then((newFileName) => {
                    this.name = newFileName;
                    this.path = newFileName;
                    return newFileName;
                });
        }

        setContents(contents: Content): angular.IPromise<string> {
            this.isReady = false;
            return this.cloudStorage.saveFile(this.name, contents).then(
                (message: string) => {
                    this.isReady = true;
                    return message;
                }
            );
        }

        setDomFileContents(domFile: DOMFile): angular.IPromise<string> {
            this.isReady = false;
            // restrict file to under 50MB
            if (domFile.size > 52428800) {
                this.uploadPromise = this.$q.reject('Exceeds file size limit');
            } else {
                let deferredUpload = this.$q.defer<string>();
                this.uploadPromise = deferredUpload.promise;
                this.uploadPromise = this.readDomFile(domFile)
                    .then((contents: Uint8Array) => {
                        return this.setContents(new Blob([contents]));
                    });
            }

            return this.uploadPromise;
        }

        getDisplayTime(): string {
            if (this.mtime) {
                return this.mtime.substring(0, this.mtime.length - 5).replace('T', ' ');
            } else {
                return 'new';
            }
        }

        private readDomFile(domFile: DOMFile): angular.IPromise<Uint8Array> {
            let deferred = this.$q.defer<Uint8Array>();
            let r = new FileReader();
            r.onload = (e: any) => {
                let contents = e.target.result;
                let bytesArray = new Uint8Array(contents);
                deferred.resolve(bytesArray);
            };

            r.readAsArrayBuffer(domFile);
            return deferred.promise;
        }
    }

    interface CloudStorageFileDescriptor {
        name: string;
        mtime: string;
        size: number;
    }

    function createUrl(name) {
        return TI_WORKSPACE_SERVER + '/' + encodeURIComponent(name);
    }

    // This service is only meant to be used internally to build other services 
    // such as implementing File ...
    class TICloudFileStorage implements ICloudStorage {

        static $inject = ['$http', '$q'];
        constructor(private $http: angular.IHttpService, private $q: angular.IQService) {
        }

        getAppDataDirectory() {
            return this.$q((resolve, reject) => {
                //create a prefereces.inf file on the local workspace, filter this file out from other cloud file browsers.
                resolve('./');
            });
        }

        saveFile(filename: string, contents: Content): angular.IPromise<string> {
            let data: ti_common.file.Content;
            let url = createUrl(filename);
            let contentType;
            if (contents instanceof Blob) {
                contentType = ti_common.file.ContentType.Blob;
            } else {
                contentType = ti_common.file.ContentType.Text;
            }

            return this.$http.put(url, contents, {
                headers: {
                    'Content-Type': ContentTypeHeaderLookup[contentType]
                },
                transformRequest: []
            })
                .then(() => {
                    return 'Success';
                });
        }


        openFile(filename: string, contentType: ContentType): angular.IPromise<Content> {
            // structured this way to work around type script compiler warnings
            // doesn't deal well with exceptions
            let fail = (response: angular.IHttpPromiseCallbackArg<ti_common.file.Content>): any => {
                throw new Error("Could not open file " + filename + " : " + response.statusText);
            };
            let configObj: angular.IRequestShortcutConfig = {};
            configObj.responseType = ResponseTypeConfigLookup[contentType];

            return this.$http.get(createUrl(filename), configObj)
                .then((response: angular.IHttpPromiseCallbackArg<ti_common.file.Content>) => {
                    return response.data;
                }).catch(fail);
        }

        deleteFile(filename: string): angular.IPromise<boolean> {
            return this.$http.delete(createUrl(filename)).then(() => true);
        }

        fileExists(filename: string): angular.IPromise<boolean> {

            let url = createUrl(filename) + '?command=exists';
            let config: angular.IRequestShortcutConfig = {};

            config.params = {
                'filename': filename
            };

            return this.$http.get(url, config).then((response: angular.IHttpPromiseCallbackArg<string>) => {
                return JSON.parse(response.data);
            });
        }

        getFileList(extensions: string): angular.IPromise<ti_common.file.File[]>;
        getFileList(filterRegex: RegExp): angular.IPromise<ti_common.file.File[]>;
        getFileList(x: any): angular.IPromise<ti_common.file.File[]> {
            let url = TI_WORKSPACE_SERVER + '?command=list';
            if (x instanceof RegExp) {
                let filterRegex = x;
                if (filterRegex) {
                    let formattedFilter = encodeURIComponent(filterRegex.source);
                    url += '&filter=' + formattedFilter;
                }

                return this.$http.get(url).then((response: angular.IHttpPromiseCallbackArg<string>) => {
                    let filesList = JSON.parse(response.data);
                    filesList = _.map(filesList, (fd: CloudStorageFileDescriptor) => {
                        return new TICloudFile(fd.name, this.$q, this, fd.size, fd.mtime);
                    });
                    return filesList;
                });

            } else if (typeof x === 'string') {
                return this.$http.get(url).then((response: angular.IHttpPromiseCallbackArg<string>) => {
                    let filesList = JSON.parse(response.data);
                    let retFileList = [];
                    filesList = _.map(filesList, (fd: CloudStorageFileDescriptor) => {
                        if (this.isAcceptableExtension(fd.name, x)) {
                            retFileList.push(new TICloudFile(fd.name, this.$q, this, fd.size, fd.mtime));
                        }
                    });
                    return retFileList;
                });
            }
        }
        private isAcceptableExtension(filename: string, extensions: string): boolean {
            let extArray = extensions.split(',');
            let ret = false;
            _.each(extArray, (ext: string) => {
                let extRegex = new RegExp(ext, 'i');
                if (filename.match(extRegex)) {
                    ret = true;
                    return;
                }
            });
            return ret;
        }

        upload(domFile: DOMFile): File {

            let newFile = new TICloudFile(
                domFile.name,
                this.$q,
                this,
                domFile.size
            );

            newFile.createNewNameIfNeeded()
                .then(() => {
                    newFile.setDomFileContents(domFile);
                });

            return newFile;
        }
    }

    class TIFileFactory implements TIFileFactory {
        static $inject = ['$q', 'ticFileStorage'];

        constructor(
            private $q: angular.IQService,
            private ticCloudStorage: TICloudFileStorage
        ) { }

        create(path: string) {
            return new TICloudFile(path,
                this.$q,
                this.ticCloudStorage);
        }

        createFromJSON(obj: IFileSerializable) {

            if (obj.type !== TICloudFile.TYPE) {
                throw obj.type + ' is not of the type of ' + TICloudFile.TYPE;
            }
            return new TICloudFile(obj.name,
                this.$q,
                this.ticCloudStorage,
                obj.size,
                null);
        }

    }

    angular.module('ti_common').service('ticFileStorage', TICloudFileStorage);

    // File factory in the cloud case
    angular.module('ti_common').service('ticFileFactory', TIFileFactory);


    angular.module('ti_common').provider('ticPlatformConfig', TICloudConfig);

}
