/*
 * Copyright (c) 2016 The ZLToolKit project authors. All Rights Reserved.
 *
 * This file is part of ZLToolKit(https://github.com/ZLMediaKit/ZLToolKit).
 *
 * Use of this source code is governed by MIT license that can be found in the
 * LICENSE file in the root of the source tree. All contributing project authors
 * may be found in the AUTHORS file in the root of the source tree.
 */

#include <iostream>
#include "Util/CMD.h"
#include "Util/logger.h"
#include "Util/util.h"
#include "Network/TcpClient.h"
#include <csignal>

using namespace std;
using namespace toolkit;

class TestClient: public TcpClient {
public:
    using Ptr = std::shared_ptr<TestClient>;
    TestClient() : TcpClient(){}
    ~TestClient(){}
    void connect(const string &strUrl, uint16_t iPort,float fTimeoutSec){
        startConnect(strUrl,iPort,fTimeoutSec);
    }
    void disconnect(){
        shutdown();
    }
    size_t commit(const string &method,const string &path,const string &host) {
        string strGet = StrPrinter
                << method
                << " "
                << path
                << " HTTP/1.1\r\n"
                << "Host: " << host << "\r\n"
                << "Connection: keep-alive\r\n"
                << "User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_1) "
                        "AppleWebKit/537.36 (KHTML, like Gecko) "
                        "Chrome/58.0.3029.110 Safari/537.36\r\n"
                << "Accept-Encoding: gzip, deflate, sdch\r\n"
                << "Accept-Language: zh-CN,zh;q=0.8,en;q=0.6\r\n\r\n";
        DebugL << "\r\n" << strGet;
        return SockSender::send(strGet);
    }

protected:
    virtual void onConnect(const SockException &ex) override{
        //连接结果事件  [AUTO-TRANSLATED:46887902]
        // Connection established event
        InfoL << (ex ?  ex.what() : "success");
    }
    virtual void onRecv(const Buffer::Ptr &pBuf) override{
        //接收数据事件  [AUTO-TRANSLATED:397ef7e4]
        // Data received event
        DebugL << pBuf->data();
    }
    virtual void onFlush() override{
        //发送阻塞后，缓存清空事件  [AUTO-TRANSLATED:46e8bca0]
        // Buffer cleared after send blocking event
        DebugL;
    }
    virtual void onError(const SockException &ex) override{
        //断开连接事件，一般是EOF  [AUTO-TRANSLATED:7359fecf]
        // Disconnected event, usually EOF
        WarnL << ex;
    }
};

//命令(http)  [AUTO-TRANSLATED:d96c7331]
// Command (http)
class CMD_http: public CMD {
public:
    CMD_http(){
        _client.reset(new TestClient);
        _parser.reset(new OptionParser([this](const std::shared_ptr<ostream> &stream,mINI &args){
            //所有选项解析完毕后触发该回调，我们可以在这里做一些全局的操作  [AUTO-TRANSLATED:cce43d55]
            // All options parsed, trigger this callback, we can do some global operations here
            if(hasKey("connect")){
                //发起连接操作  [AUTO-TRANSLATED:405329cf]
                // Initiate connection operation
                connect(stream);
                return;
            }
            if(hasKey("commit")){
                commit(stream);
                return;
            }
        }));

        (*_parser) << Option('T', "type", Option::ArgRequired, nullptr, true, "应用程序模式，0：传统模式，1：shell模式", nullptr);

        (*_parser) << Option('s',/*该选项简称，如果是\x00则说明无简称*/
                             "server",/*该选项全称,每个选项必须有全称；不得为null或空字符串*/
                             Option::ArgRequired,/*该选项后面必须跟值*/
                             "www.baidu.com:80",/*该选项默认值*/
                             false,/*该选项是否必须赋值，如果没有默认值且为ArgRequired时用户必须提供该参数否则将抛异常*/
                             "tcp服务器地址，以冒号分隔端口号",/*该选项说明文字*/
                             [this](const std::shared_ptr<ostream> &stream, const string &arg){/*解析到该选项的回调*/
                                 if(arg.find(":") == string::npos){
                                     //中断后续选项的解析以及解析完毕回调等操作  [AUTO-TRANSLATED:15b7592f]
                                     // Interrupt subsequent option parsing and parsing completion callback operations
                                     throw std::runtime_error("\t地址必须指明端口号.");
                                 }
                                 //如果返回false则忽略后续选项的解析  [AUTO-TRANSLATED:01a3d6bc]
                                 // If return false, ignore subsequent option parsing
                                 return true;
                             });

        (*_parser) << Option('d', "disconnect", Option::ArgNone, nullptr ,false, "是否断开连接",
                             [this](const std::shared_ptr<ostream> &stream, const string &arg){
                                 //断开连接操作，所以后续的参数我们都不解析了  [AUTO-TRANSLATED:8aa1db56]
                                 // Disconnect operation, so we don't parse subsequent parameters
                                 disconnect(stream);
                                 return false;
                             });

        (*_parser) << Option('c', "connect", Option::ArgNone, nullptr, false, "发起tcp connect操作", nullptr);
        (*_parser) << Option('t', "time_out", Option::ArgRequired, "3",false, "连接超时间", nullptr);
        (*_parser) << Option('m', "method", Option::ArgRequired, "GET",false, "HTTP方法,譬如GET、POST", nullptr);
        (*_parser) << Option('p', "path", Option::ArgRequired, "/index.html",false, "HTTP url路径", nullptr);
        (*_parser) << Option('C', "commit", Option::ArgNone, nullptr, false, "提交HTTP请求", nullptr);



    }

    ~CMD_http() {}

    const char *description() const override {
        return "http测试客户端";
    }

private:
    void connect(const std::shared_ptr<ostream> &stream){
        (*stream) << "connect操作" << endl;
        _client->connect(splitedVal("server")[0],splitedVal("server")[1],(*this)["time_out"]);
    }
    void disconnect(const std::shared_ptr<ostream> &stream){
        (*stream) << "disconnect操作" << endl;
        _client->disconnect();
    }
    void commit(const std::shared_ptr<ostream> &stream){
        (*stream) << "commit操作" << endl;
        _client->commit((*this)["method"],(*this)["path"],(*this)["server"]);
    }

private:
    TestClient::Ptr _client;
};



int main(int argc,char *argv[]){
    REGIST_CMD(http);
    signal(SIGINT,[](int ){
        exit(0);
    });
    try{
        CMD_DO("http",argc,argv);
    }catch (std::exception &ex){
        cout << ex.what() << endl;
        return 0;
    }
    if(GET_CMD("http")["type"] == 0){
        cout << "传统模式，已退出程序，请尝试shell模式" << endl;
        return 0;
    }
    GET_CMD("http").delOption("type");
    //初始化环境  [AUTO-TRANSLATED:efbad911]
    // Initialize environment
    Logger::Instance().add(std::shared_ptr<ConsoleChannel>(new ConsoleChannel()));
    Logger::Instance().setWriter(std::shared_ptr<LogWriter>(new AsyncLogWriter()));

    cout << "> 欢迎进入命令模式，你可以输入\"help\"命令获取帮助" << endl;
    string cmd_line;
    while(cin.good()){
        try{
            cout << "> ";
            getline(cin,cmd_line);
            CMDRegister::Instance()(cmd_line);
        }catch (ExitException &){
            break;
        }catch (std::exception &ex){
            cout << ex.what() << endl;
        }
    }
    return 0;
}