(function () {
    'use strict';

    const
        event = require('myevents'),
        fs = require('fs'),
        net = require('net'),
        crc = require('crc32'),
        crypto = require('crypto'),
        utils = require('utility'),
        { upload: upload_config, version } = require('sys-config'),
        taskStatus = require('taskstatus'),
        URL = require('url');

    const ChunkSize = upload_config.chuck_max_size;

    class Upload {
        constructor(option) {
            this.speed = {
                down_count: 0,
                sTime: 0,
                start_size: 0
            };
            this.options = {
                url: '',
                status: taskStatus.wait,
                start_len: 0,
                end_len: 0,
                speed: 0,
                size:0,
                show_time:''
            };
            Object.assign(this.options, option);
        }
        init() {
            let me = this;
            me.Socket = new net.Socket();
            return me._bindEvent();
        }
        startSB() {
            let me = this;
            me.options.status = taskStatus.doing;

            me.Socket || me.init();

            if (me.Socket.connecting) return me;

            let url = me.options.url.split(':');
            [options.start_len, options.end_len] = [options.end_len, options.end_len + 10];
            me.Socket.connect(url[1], url[0], () => {
                me.fileChunk();
            });
            return me;
        }
        resume() {
            let me = this;
            me.Socket.resume();
        }
        fileChunk() {
            let me = this,
                options = me.options;

            if ((options.end_len - options.start_len) > ChunkSize) {
                options.end_len = options.start_len + ChunkSize;
            }

            if (options.end_len > options.size) {
                options.end_len = options.size;
            }

            if (options.start_len > options.end_len) {
                options.start_len = 0;
                options.end_len = options.size === 0 ? 0 : 1;  //传递一个字节 ，获取服务端上传进度
            }
            fs.access(options.path, (err) => {
                if (err) {
                    options.status = taskStatus.readError;
                    me.Socket && me.Socket.destroy();
                    delete me.Socket;
                    return event.emit('upload_error');
                }
                this.readFile(options);
            });
            return me;
        }
        readFile(options) {
            let me = this;

            let buffer = [],
                buffer_length = 0;
            options.start_len = options.start_len || 0;
            options.end_len = options.end_len || 1;

            let reader = fs.createReadStream(options.path, {
                defaultEncoding: 'utf8',
                start: options.start_len,
                end: options.end_len
            });
            reader.on('data', (chunk) => {
                if (!chunk) return;
                buffer.push(chunk);
                buffer_length += chunk.length;
            });
            reader.on('end', () => {
                if (me.speed.down_count === 0) {
                    me.speed.sTime = Date.now();
                    me.speed.star_size = options.start_len;
                }
                me.sendData(Buffer.concat(buffer, buffer_length), options.start_len);
            });
        }
        sendData(chunk, startOffset) {
            let me = this,
                options = me.options;

            if (!me.Socket) {
                return;
            }
            var spec = {
                "md5": options.md5,
                "crc": crc.crc32Buffer(chunk),
                "cmd": 1,
                "fileName": (options.name),
                "offset": startOffset,
                "user": {},
                "fileLength": options.size,
                "client": {
                    "name": "政务云盘",
                    "version": version
                },
                "complete": 0
            }

            var specStr = JSON.stringify(spec);
            var specLength = Buffer.byteLength(specStr);

            var total = 4 + 4 + specLength + 4 + chunk.length;

            var content = Buffer.allocUnsafe(total);
            var offset = 0;

            content.writeInt32LE(total, offset);
            offset = offset + 4;

            content.writeInt32LE(specLength, offset);
            offset = offset + 4;

            content.write(specStr, offset);
            offset = offset + specLength;

            content.writeInt32LE(chunk.length, offset);
            offset = offset + 4;
            //在这个offset后面复制 chunk
            chunk.copy(content, offset);

            me.Socket.write(content, 'utf-8', function () {
                if (me.speed.down_count === 2) {
                    me.speed.down_count = 0;
                    options.speed = ((options.end_len - me.speed.start_size) / ((Date.now() - me.speed.sTime) / 1000)).toFixed(1);
                    //把秒拼接成 时：分：秒
                    let tempTime = parseInt((options.size - options.end_len)/options.speed);
                    let h = Math.floor(tempTime / 3600);
                    let m = Math.floor((tempTime / 60 % 60));
                    let s = Math.floor((tempTime % 60));
                    h > 9 ? h : h = '0'+ h;
                    m > 9 ? m : m = '0'+ m;
                    s > 9 ? s : s = '0'+ s;
                    options.show_time =  h + ':' + m + ':' + s;
                    me.speed.start_size = options.end_len;
                } else {
                    me.speed.down_count = me.speed.down_count + 1;
                }
            });
            return me;
        }
        _bindEvent() {
            let me = this,
                options = me.options;

            let error;

            me.Socket.on('error', function (err) {
                destroy(taskStatus.netError);
                event.emit('upload_pause', me, options);
            });

            me.Socket.on('close', function (had_error) {
                // console.log(file_info, ' me.Socket.on close');
                destroy(had_error ? taskStatus.netError : taskStatus.pause);
                event.emit('upload_pause', me, options);
            });

            me.Socket.on('data', function (d) {
                let data = JSON.parse(d.toString('utf8', 4));
                data.code = parseInt(data.code);
                if (data.code === 505) {
                    destroy(taskStatus.maxLimit);
                    event.emit('upload_error', me, options);
                    return;
                }
                if (data.code === 501 || data.code === 500 || data.code === 502) {
                    destroy(taskStatus.netError);
                    event.emit('upload_error', me, options);
                    return;
                }
                if (data.code === 503) {
                    destroy(taskStatus.pause);
                    event.emit('upload_error', me, options);
                    return;
                }
                if (data.code === 304) {
                    destroy(taskStatus.finsh);
                    event.emit('upload_complete', me, options);
                    return;
                }
                if (data.code === 200) {
                    options.start_len = data.accept;
                    options.end_len = data.accept + ChunkSize;

                    if (options.status !== taskStatus.doing) {
                        destroy(options.status);
                        event.emit('upload_error', me, options);
                        return;
                    }
                    event.emit('upload_range_finish', me, options);
                    me.fileChunk();
                    return;
                }
                destroy(taskStatus.notResponse);
                event.emit('upload_error', me, options);
            });
            //关闭任务  更新options.status的状态显示
            function destroy(taskStatus) {
                taskStatus && (options.status = taskStatus);
                me.Socket && me.Socket.destroy();
                delete me.Socket;
            }
            return me;
        }
    };
    module.exports = Upload;
}());
