'use strict';
const net = require('net'),
    fs = require('fs'),
    Stream = require('stream'),
    http = require('http'),
    path = require('path'),
    URL = require('url'),
    zlib = require('zlib'),
    request = require('request'),
    event = require('myevents'),
    md5 = require('md5'),
    mkpath = require('mkpath'),
    { down: down_config } = require('sys-config'),
    taskStatus = require('taskstatus');

let tryNums = 0;

class DownFile {
    constructor(options) {
        this.speed = {
            down_count: 0,
            sTime: 0,
            start_Size: 0
        };
        this.options = {
            "md5": '',
            "name": '',
            "size": 0,
            "path": '',
            start_len: 0,
            end_len: down_config.chuck_max_size,
            url: '',
            status: taskStatus.doing,
            speed: 0,
            creator: Date.now(),
            urlError: 0,
            show_time:''
        };
        Object.assign(this.options, options);

        if (this.options.start_len > this.options.end_len) {
            this.options.end_len = this.options.start_len + down_config.chuck_max_size;
        }
        let path_info = path.parse(this.options.path);
        if (!fs.existsSync(path_info.dir)) {
            mkpath.sync(path_info.dir);
        }
    }

    start() {
        if (!this.options.url) {
            event.emit('down_overtime', this);
            return;
        }
        this.options.urlError = false;
        this.options.status = taskStatus.doing;
        this.down();
    }
    down() {
        let me = this;

        if (taskStatus.doing !== me.options.status) {
            event.emit('down_pause', me.options);
            return;
        }

        if (me.speed.down_count === 0) {
            me.speed.sTime = Date.now();
            me.speed.start_Size = me.options.start_len;
        }
        if (me.options.start_len > me.options.end_len) {
            me.options.start_len = 0;
            me.options.end_len = down_config.chuck_max_size;//在前状态记录错误 ，从0开始保证下载完整性
        }
        if (me.options.size <= me.options.end_len)
            me.options.end_len = me.options.size;

        let mode = me.options.start_len == 0 ? "w" : 'r+',
            stream = fs.createWriteStream(me.options.path, {
                flags: mode,
                autoClose: true,
                start: me.options.start_len
            });
        me.streamEvent(stream).request(stream);
    }
    streamEvent(stream) {
        let me = this;
        stream.on("error", function () {

            tryNums += 1;
            if (tryNums < 5) return me.request(stream, tryNums);

            me.options.status = taskStatus.readError;
            event.emit('down_error', me.options);
            return false;
        });

        stream.on('finish', () => {
            if (me.netError) {
                return;
            }
            if (me.options.status != taskStatus.doing) {
                event.emit('range_finish', me.options);
                return;
            }

            if (me.options.end_len === me.options.size) {
                me.options.status = taskStatus.finsh;
                event.emit('down_complete', me.options);
                return;
            }
            me.options.start_len = me.options.end_len;
            me.options.end_len += down_config.chuck_max_size;


            event.emit('range_finish', me.options);

            let timespace = Date.now() - me.speed.sTime;

            if (timespace >= 2000) {
                me.speed.down_count = 0;
                me.options.speed = ((me.options.end_len - me.speed.start_Size) / ((Date.now() - me.speed.sTime) / 1000)).toFixed(1);
                //把秒拼接成 时：分：秒
                let tempTime = parseInt((me.options.size - me.options.end_len)/me.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;
                me.options.show_time = h + ':'+ m + ':' + s;
            } else {
                me.speed.down_count++;
            }

            me.down();
        });
        return this;
    }
    request(stream, tryNums = 0) {

        let me = this;

        // request
        // .get(me.options.url)
        // .on('error', function(err) {
        //   console.log(err)
        // })
        // return me.httpRequest(stream);

        request({
            url: me.options.url,
            timeout: 1000 * 10,
            headers: {
                'Range': `bytes=${me.options.start_len}-${me.options.end_len}` //'500-999'
            }
        }, function (error, response, body) {
            if (error || !response) {
                tryNums += 1;
                if (tryNums < 5) return me.request(stream, tryNums)

                me.options.status = taskStatus.netError;
                event.emit('down_error', this);
                me.netError = true;
                return;
            }

            if (response.statusCode == 200 || response.statusCode == 206) {
                me.netError = false;
                return;
            }

            else if (response.statusCode == 401) {
                event.emit('down_overtime', this);
            }
            else {
                me.options.status = taskStatus.notExist;
                event.emit('down_error', this);
            }
            me.netError = true;

        }).on('error', function (err) {
            tryNums += 1;
            if (tryNums < 5) return me.request(stream, tryNums)
            me.options.status = taskStatus.netError;
            event.emit('down_error', this);
            me.netError = true;
        }).pipe(stream);
        return this;
    }
    httpRequest(stream) {
        let me = this;
        let urlParse = URL.parse(me.options.url);
        const options = {
            hostname: urlParse.host,
            port: urlParse.port || 80,
            path: urlParse.path,
            timeout: 1000 * 30,
            method: 'GET',
            headers: {
                'Range': `bytes=${me.options.start_len}-${me.options.end_len}` //'500-999'
            }
        };

        let buffers = [],
            bufferLength = 0;
        const req = http.request(options, function (response) {
            console.log(response)
            if (response.statusCode == 200 || response.statusCode == 206) {
                me.netError = false;
            }
            else {
                if (response.statusCode == 401)
                    event.emit('down_overtime', me);
                else {
                    event.emit('down_error', me);
                    me.options.status = taskStatus.notExist;
                }
                me.netError = true;
                return;
            }
            response.on("data", function (chunk) {
                if (Buffer.isBuffer(chunk)) {
                    buffers.push(chunk);
                    bufferLength += chunk.length
                }
            });
            response.on("end", function () {
                let body = Buffer.concat(buffers);
                stream.write(body);
                stream.end();
            });
        });
        req.on('error', (e) => {
            if (e.code === "HPE_INVALID_CONSTANT") {
                return;
            }
            me.options.status = taskStatus.netError;
            event.emit('down_error', me);
            me.netError = true;
        });
        req.end();
        return this;
    }


    _fileInfo() {

    }
}
module.exports = DownFile;