#include "tinypb_coder.hpp"
#include "abstract_coder.hpp"
#include "tinypb_data.hpp"
#include <cstdint>
#include <cstring>
#include <memory>
#include <netinet/in.h>
#include <new>
#include <vector>


namespace tinyrpc {

    static const char PB_START = 0x02;     // start char
    static const char PB_END = 0x03;      // end char
    // static const int MSG_REQ_LEN = 20;    // default length of msg_req

    TinypbCoder::TinypbCoder() {
        // TODO
    }
    TinypbCoder::~TinypbCoder() {
        // TODO
    }
    bool  TinypbCoder::encoder(TcpBuffer& buffer, AbstractData& data)  {
        TinypbData& pbdata = dynamic_cast<TinypbData&>(data);
        if(pbdata.msg_req.empty()) return false;
        if(pbdata.service_full_name.empty()) return false;
        if(pbdata.pb_data.empty()) return false;
        // TODO msg_req 处理

        int32_t pk_len = 2 * sizeof(char) + 6 * sizeof(int32_t)
                    + pbdata.pb_data.length() + pbdata.service_full_name.length()
                    + pbdata.msg_req.length() + pbdata.err_info.length();

        std::unique_ptr<char[]> buf(new char[pk_len + 1]{});
        
        int cur_index = 0;
        buf[cur_index++] = PB_START;
        int32_t pk_len_net = htonl(pk_len);

        memcpy(&buf[cur_index], &pk_len_net, sizeof(pk_len_net));
        cur_index += sizeof(pk_len_net);

        int32_t msg_req_len = pbdata.msg_req.length();
        int32_t msg_req_len_net = htonl(msg_req_len);
        memcpy(&buf[cur_index], &msg_req_len_net, sizeof(msg_req_len_net));
        cur_index += sizeof(msg_req_len_net);
        memcpy(&buf[cur_index], pbdata.msg_req.c_str(), msg_req_len);
        cur_index += msg_req_len;

        int32_t service_name_len = pbdata.service_full_name.length();
        int32_t service_name_len_net = htonl(service_name_len);
        memcpy(&buf[cur_index], &service_name_len_net, sizeof(service_name_len_net));
        cur_index += sizeof(service_name_len_net);
        memcpy(&buf[cur_index], pbdata.msg_req.c_str(), service_name_len);
        cur_index += service_name_len;

        int32_t err_code = pbdata.err_code;
        int32_t err_code_net = htonl(err_code);
        memcpy(&buf[cur_index], &err_code_net, sizeof(err_code_net));
        cur_index += sizeof(err_code_net);

        int32_t err_info_len = pbdata.err_info.length();
        int32_t err_info_len_net = htonl(err_info_len);
        memcpy(&buf[cur_index], &err_info_len_net, sizeof(err_info_len_net));
        cur_index += sizeof(err_info_len_net);
        memcpy(&buf[cur_index], pbdata.err_info.c_str(), err_info_len);
        cur_index += err_info_len;
        
        int32_t pb_data_len = pbdata.err_info.length();
        memcpy(&buf[cur_index], pbdata.pb_data.c_str(), pb_data_len);
        cur_index += pb_data_len;

        int32_t check_num = 1;
        int32_t check_num_net = htonl(check_num);
        memcpy(&buf[cur_index], &check_num_net, sizeof(check_num_net));
        cur_index += sizeof(check_num_net);

        buf[cur_index++] = PB_END;

        memcpy(buffer.getWriteAddress(), buf.get(), pk_len);
        buffer.writeOffset(pk_len);
        return true;

    }

    bool TinypbCoder::decoder(TcpBuffer& buffer, AbstractData& data) {
        
        char* buff = static_cast<char*>(buffer.getReadAddress());

        int start_index = -1;
        int end_index = -1;
        bool isFullPack = false;
        int pack_len = -1;

        for(int i = 0; i < static_cast<int>(buffer.getReadable()); i++) {
            if(buff[i] == PB_START) {

                if(i + 1 >= static_cast<int>(buffer.getReadable())) {
                    return false; // 包不完整
                }

                pack_len = getInt32FromNetByte(buff[i + 1]);
                end_index  = pack_len + i - 1;

                if(end_index >= static_cast<int>(buffer.getReadable())) {
                    continue;
                }

                if(buff[end_index] == PB_END) {
                    isFullPack = true;
                    start_index = i;
                    break;
                }

                
            }
        }

        if(isFullPack == false) {
            return false; // 包不完整
        }

        TinypbData& pbdata = dynamic_cast<TinypbData&>(data);
        pbdata.pk_len = pack_len;
        
        int cur_index = start_index + sizeof(char) + sizeof(int32_t);

        if (cur_index > end_index) {
            return false;
        }
        
        pbdata.msg_req_len =  getInt32FromNetByte(buff[cur_index]);
        cur_index = cur_index + sizeof(int32_t);

        if (cur_index > end_index || cur_index + pbdata.msg_req_len - 1 > end_index) {
            return false;
        }

        std::unique_ptr<char[]> msg_req(new char[pbdata.msg_req_len + 1]{});
        memcpy(&msg_req[0], &buff[cur_index], pbdata.msg_req_len);

        pbdata.msg_req = std::string(msg_req.get());

        cur_index = cur_index + pbdata.msg_req_len;

        if (cur_index > end_index || cur_index + static_cast<int>(sizeof(int32_t)) - 1 > end_index) {
            return false;
        }

        pbdata.service_name_len = getInt32FromNetByte(buff[cur_index]);

        if (pbdata.service_name_len > pack_len) {
            return false;
        }

        cur_index = cur_index + sizeof(int32_t);

        if (cur_index > end_index || cur_index +  pbdata.service_name_len - 1 >end_index) {
            return false;
        }

        std::unique_ptr<char[]> service_name(new char[pbdata.service_name_len + 1]{});
        memcpy(&service_name[0], &buff[cur_index], pbdata.service_name_len);

        pbdata.service_full_name = std::string(service_name.get());

        cur_index = cur_index + pbdata.service_name_len; 
    
        if(cur_index > end_index || cur_index + static_cast<int>(sizeof(int32_t)) - 1 > end_index) {
            return false;
        }

        pbdata.err_code = getInt32FromNetByte(buff[cur_index]);
    
        cur_index = cur_index + sizeof(int32_t);

        if(cur_index > end_index || cur_index + static_cast<int>(sizeof(int32_t)) - 1 > end_index) {
            return false;
        }

        pbdata.err_info_len = getInt32FromNetByte(buff[cur_index]);

        cur_index = cur_index + sizeof(int32_t);

        if(cur_index > end_index || cur_index + pbdata.err_info_len - 1 > end_index) {
            return false;
        }

        std::unique_ptr<char[]> err_info(new char[pbdata.err_info_len + 1]{});

        memcpy(&err_info[0], &buff[cur_index], pbdata.err_info_len);

        pbdata.err_info = std::string(err_info.get());

        int pb_data_len = pbdata.pk_len 
                      - pbdata.service_name_len - pbdata.msg_req_len - pbdata.err_info_len
                      - 2 * sizeof(char) - 6 * sizeof(int32_t);

        cur_index = cur_index + pbdata.err_info_len;
        
        if(cur_index > end_index || cur_index + pb_data_len - 1 > end_index) {
            return false;
        }


        pbdata.pb_data = std::string(&buff[cur_index], pb_data_len);
        cur_index += pb_data_len;

        buffer.readOffset(cur_index - start_index);


        return true;
    }

}

