#include "tinypb_dispatcher.hpp"
#include "logger.hpp"
#include "tinypb_closure.hpp"
#include "tinypb_controller.hpp"
#include "tinypb_data.hpp"
#include "error_code.hpp"
#include <memory>
#include <sstream>

namespace tinyrpc {
    TinypbDispatcher::TinypbDispatcher() {}
        
    TinypbDispatcher::~TinypbDispatcher() {

    }
    void TinypbDispatcher::dispatcher(ServerTcpConnection& conn, AbstractData& data, AbstractData& resp) {
        logger() << "dispatcher";
        TinypbData& request = dynamic_cast<TinypbData&>(data);
        TinypbData& respond = dynamic_cast<TinypbData&>(resp);

        std::string service_name;
        std::string method_name;

        respond.service_full_name = request.service_full_name;
        respond.msg_req = request.msg_req;

        logger() << "request service_full_name:" << "[" << request.service_full_name << "]";
        logger() << "request msg_req:" << "[" << request.msg_req << "]";

        bool ret = parseServiceFullName(request.service_full_name, service_name, method_name);

        if(ret == false) {
            respond.err_code = ERROR_PARSE_SERVICE_NAME;
            std::stringstream ss;
            ss << "not found service_name:[" << service_name << "]"; 
            respond.err_info = ss.str();
            return;
        }
        logger() << "request method_name:" << "[" << method_name << "]";

        auto it = m_service_map.find(service_name);

        if (it == m_service_map.end() || !(it->second)) {
            respond.err_code = ERROR_SERVICE_NOT_FOUND;
            std::stringstream ss;
            ss << "not found service_name:[" << service_name << "]"; 
            respond.err_info = ss.str();
            return;

        }

        std::shared_ptr<Service> service = it->second;

        const Method* method = service->GetDescriptor()->FindMethodByName(method_name);
        // const Method* method = nullptr;
        if(method == nullptr) {
            respond.err_code = ERROR_METHOD_NOT_FOUND;
            std::stringstream ss;
            ss << "not found service_name:[" << service_name << "]"; 
            respond.err_info = ss.str();
            return;
        }
        std::unique_ptr<Message> requestMsg (service->GetRequestPrototype(method).New());

        ret = requestMsg->ParseFromString(request.pb_data);

        if(ret == false) {
            respond.err_code = ERROR_FAILED_SERIALIZE;
            std::stringstream ss;
            ss << "faild to parse request data, request.name:[" << requestMsg->GetDescriptor()->full_name() << "]";
            respond.err_info = ss.str();
            return;
        }

        std::unique_ptr<Message> respondMsg (service->GetRequestPrototype(method).New());

        auto callback = [&respond, &respondMsg] {
            if(!respondMsg->SerializePartialToString(&respond.pb_data)) {
                respond.pb_data = "";
                logger() << respond.msg_req << "|reply error! encode reply package error";
                respond.err_code = ERROR_FAILED_SERIALIZE;
                respond.err_info = "failed to serilize relpy data";
            } else {
                logger() <<  respond.msg_req << "|Set server response data:" << respondMsg->ShortDebugString();
            }
        };
        
        std::unique_ptr<TinypbController> rpcController(new TinypbController);
        rpcController->SetMsgReq(respond.msg_req);
        rpcController->SetFullName(respond.service_full_name);
        rpcController->SetMethodName(method_name);
        TinypbClosure done(callback);

        service->CallMethod(method, rpcController.get(), requestMsg.get(), respondMsg.get(), &done /* callback */);

    }

    bool TinypbDispatcher::parseServiceFullName(const std::string& name, std::string& serviceName, std::string& methodName)  {
        if(name.empty()) return false;

        auto pos = name.find(".");
        if(pos == std::string::npos) return false;
        serviceName = name.substr(0, pos);
        methodName = name.substr(pos + 1);

        // logger() << "serviceName=" << serviceName;
        // logger() << "methodName=" << methodName;
        return true;
    }

    void TinypbDispatcher::registerService(std::shared_ptr<Service>& service) {
        std::string service_name = service->GetDescriptor()->full_name();
        m_service_map[service_name] = service;
        logger() << "success register service:" << "[" << service_name << "]";
    }

}

