/*
        Copyright (c) 2025, MOEX. All rights reserved.
        
        Plaza-2 Client Gate API Usage Sample.
        
        All the software and documentation included in this and any
        other MOEX CGate Release is copyrighted by MOEX.
        
        Redistribution and use in source and binary forms, with or without
        modification, are permitted only by the terms of a valid
        software license agreement with MOEX.
        
        THIS SOFTWARE IS PROVIDED "AS IS" AND MOEX DISCLAIMS ALL WARRANTIES
        EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION, ANY IMPLIED WARRANTIES OF
        NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR
        PURPOSE.  MOEX DOES NOT WARRANT THAT USE OF THE SOFTWARE WILL BE
        UNINTERRUPTED OR ERROR-FREE.  MOEX SHALL NOT, UNDER ANY CIRCUMSTANCES, BE
        LIABLE TO LICENSEE FOR LOST PROFITS, CONSEQUENTIAL, INCIDENTAL, SPECIAL OR
        INDIRECT DAMAGES ARISING OUT OF OR RELATED TO THIS AGREEMENT OR THE
        TRANSACTIONS CONTEMPLATED HEREUNDER, EVEN IF MOEX HAS BEEN APPRISED OF
        THE LIKELIHOOD OF SUCH DAMAGES.
*/

#pragma once

#define CG_SHORT_NAMES
#include <cgate.h>
#include <unordered_map>
#include <string>

class Scheme {
public:
    explicit Scheme(cg_scheme_desc_t* scheme):
        cg_scheme(scheme) {
        //заполнение таблицы полей сообщений
        size_t idx = 0;
        for (cg_message_desc_t* msg = scheme->messages; msg; msg = msg->next) {
            auto& message = messages[idx];
            for (cg_field_desc_t* fld = msg->fields; fld; fld = fld->next) {
                auto& field = message[fld->name];
                field.offset = fld->offset;
                field.type = fld->type;
            }
            names[idx++] = msg->name;
        }

    }
    ~Scheme() = default;

    struct Field {
        size_t offset;
        std::string type;
    };
    using Fields = std::unordered_map<std::string, Field>;

    const Fields& message(size_t idx) const {
        return messages.at(idx);
    }

    const std::string& name(size_t idx) const {
        return names.at(idx);
    }

    cg_scheme_desc_t* cgscheme() const {
        return cg_scheme;
    }

private:
    std::unordered_map<size_t, Fields> messages;
    std::unordered_map<size_t, std::string> names;
    cg_scheme_desc_t* cg_scheme{};
};

int32_t data_i32(const void* data, const Scheme::Field& field) {
    assert(field.type == "i4");
    return *(reinterpret_cast<const int32_t*>(static_cast<const char*>(data) + field.offset));
}

int64_t data_i64(const void* data, const Scheme::Field& field) {
    assert(field.type == "i8");
    return *(reinterpret_cast<const int64_t*>(static_cast<const char*>(data) + field.offset));
}

std::string data_str(const void* data, const Scheme::Field& field) {
    assert(field.type[0] == 'c');
    return std::string(static_cast<const char*>(data) + field.offset);
}

struct FutSessContentsRecord {
    FutSessContentsRecord() = default;
    FutSessContentsRecord(const void* data, const Scheme::Fields& message):
        repl_id(data_i64(data, message.at("replID"))),
        repl_rev(data_i64(data, message.at("replRev"))),
        repl_act(data_i64(data, message.at("replAct"))),
        sess_id(data_i32(data, message.at("sess_id"))),
        isin_id(data_i32(data, message.at("isin_id"))),
        base_contract_code(data_str(data, message.at("base_contract_code"))),
        trade_mode_id(data_i32(data, message.at("trade_mode_id"))),
        group_mask(data_i64(data, message.at("group_mask")))
        {}
    int64_t repl_id;
    int64_t repl_rev;
    int64_t repl_act;
    int32_t sess_id;
    int32_t isin_id;
    std::string base_contract_code;
    int32_t trade_mode_id;
    int64_t group_mask;
};

struct OptSessContentsRecord {
    OptSessContentsRecord() = default;
    OptSessContentsRecord(const void* data, const Scheme::Fields& message):
        repl_id(data_i64(data, message.at("replID"))),
        repl_rev(data_i64(data, message.at("replRev"))),
        repl_act(data_i64(data, message.at("replAct"))),
        sess_id(data_i32(data, message.at("sess_id"))),
        isin_id(data_i32(data, message.at("isin_id"))),
        base_contract_code(data_str(data, message.at("base_contract_code"))),
        trade_mode_id(data_i32(data, message.at("trade_mode_id"))),
        group_mask(data_i64(data, message.at("group_mask")))
        {}
    int64_t repl_id;
    int64_t repl_rev;
    int64_t repl_act;
    int32_t sess_id;
    int32_t isin_id;
    std::string base_contract_code;
    int32_t trade_mode_id;
    int64_t group_mask;
};

struct FutVcbRecord {
    FutVcbRecord() = default;
    FutVcbRecord(const void* data, const Scheme::Fields& message):
        repl_id(data_i64(data, message.at("replID"))),
        repl_rev(data_i64(data, message.at("replRev"))),
        repl_act(data_i64(data, message.at("replAct"))),
        base_contract_code(data_str(data, message.at("base_contract_code"))),
        base_contract_id(data_i32(data, message.at("base_contract_id"))),
        section_id(data_i32(data, message.at("section_id")))
        {}
    int64_t repl_id;
    int64_t repl_rev;
    int64_t repl_act;
    std::string base_contract_code;
    int32_t base_contract_id;
    int32_t section_id;
};

struct OptVcbRecord {
    OptVcbRecord() = default;
    OptVcbRecord(const void* data, const Scheme::Fields& message):
        repl_id(data_i64(data, message.at("replID"))),
        repl_rev(data_i64(data, message.at("replRev"))),
        repl_act(data_i64(data, message.at("replAct"))),
        base_contract_code(data_str(data, message.at("base_contract_code"))),
        base_contract_id(data_i32(data, message.at("base_contract_id"))),
        section_id(data_i32(data, message.at("section_id")))
        {}
    int64_t repl_id;
    int64_t repl_rev;
    int64_t repl_act;
    std::string base_contract_code;
    int32_t base_contract_id;
    int32_t section_id;
};

struct SysEventsRecord {
    SysEventsRecord() = default;
    SysEventsRecord(const void* data, const Scheme::Fields& message):
        repl_id(data_i64(data, message.at("replID"))),
        repl_rev(data_i64(data, message.at("replRev"))),
        repl_act(data_i64(data, message.at("replAct"))),
        event_type(data_i32(data, message.at("event_type"))),
        event_id(data_i64(data, message.at("event_id"))),
        sess_id(data_i32(data, message.at("sess_id"))),
        message(data_str(data, message.at("message")))
        {}
    int64_t repl_id;
    int64_t repl_rev;
    int64_t repl_act;
    int32_t event_type;
    int64_t event_id;
    int32_t sess_id;
    std::string message;
};

struct SecurityGroupStateRecord {
    SecurityGroupStateRecord() = default;
    SecurityGroupStateRecord(const void* data, const Scheme::Fields& message):
        repl_id(data_i64(data, message.at("replID"))),
        repl_rev(data_i64(data, message.at("replRev"))),
        repl_act(data_i64(data, message.at("replAct"))),
        security_group_id(data_i64(data, message.at("security_group_id"))),
        type(data_i32(data, message.at("type"))),
        trade_mode_mask(data_i32(data, message.at("trade_mode_mask"))),
        group_mask(data_i64(data, message.at("group_mask"))),
        section_id(data_i32(data, message.at("section_id"))),
        base_contract_id(data_i32(data, message.at("base_contract_id"))),
        state(data_i32(data, message.at("state")))
        {}
    int64_t repl_id;
    int64_t repl_rev;
    int64_t repl_act;
    int64_t security_group_id;
    int32_t type;
    int32_t trade_mode_mask;
    int64_t group_mask;
    int32_t section_id;
    int32_t base_contract_id;
    int32_t state;
};
