#ifndef _GPD_XS_TRANSFORM_INCLUDED
#define _GPD_XS_TRANSFORM_INCLUDED

#include "EXTERN.h"
#include "perl.h"
#include "ppport.h"
#include "perl_unpollute.h"

#include "thx_member.h"
#include "mapper_context.h"

#include <vector>
#include <list>

namespace gpd {
namespace transform {

struct DecoderFieldtable {
    struct Entry {
        unsigned int field;
        SV *value;

        Entry(unsigned int _field, SV *_value) {
            field = _field;
            value = _value;
        }
    };

    int size;
    Entry *entries;
};

struct EncoderFieldtable {
    struct Entry {
        const char *name;
        unsigned int field;
        SV *value;
    };

    int size;
    Entry *entries;
};

struct UnknownFieldContext {
    int size;
    const gpd::MapperContext::ExternalItem *const *mapper_context;
};

typedef void (*CDecoderTransform)(pTHX_ SV *target);
typedef void (*CDecoderTransformFieldtable)(pTHX_ SV *target, DecoderFieldtable *fieldtable);
typedef void (*CEncoderTransform)(pTHX_ SV *target, SV *value);
typedef void (*CEncoderTransformFieldtable)(pTHX_ EncoderFieldtable **fieldtable, SV *value);
typedef void (*CUnknownFieldTransform)(pTHX_ UnknownFieldContext *field_context, SV *value);

class DecoderTransform {
public:
    DecoderTransform(CDecoderTransform c_transfrom);
    DecoderTransform(CDecoderTransformFieldtable c_transform_fieldtable);
    DecoderTransform(SV *perl_transform);

    // clear and delete this object, it is needed just to avoid
    // having a THX member in this object
    void destroy(pTHX);

    void transform(pTHX_ SV *target) const;
    void transform_fieldtable(pTHX_ SV *target, DecoderFieldtable *fieldtable) const;

private:
    // private to make sure deletion goes through destroy()
    ~DecoderTransform() {}

    CDecoderTransform c_transform;
    CDecoderTransformFieldtable c_transform_fieldtable;
    SV *perl_transform;
};

class EncoderTransform {
public:
    EncoderTransform(CEncoderTransform c_transform);
    EncoderTransform(CEncoderTransformFieldtable c_transform_fieldtable);
    EncoderTransform(SV * perl_transform);

    // clear and delete this object, it is needed just to avoid
    // having a THX member in this object
    void destroy(pTHX);

    void transform(pTHX_ SV *target, SV *value) const;
    void transform_fieldtable(pTHX_ EncoderFieldtable **target, SV *value) const;

private:
    // private to make sure deletion goes through destroy()
    ~EncoderTransform() {}

    CEncoderTransform c_transform;
    CEncoderTransformFieldtable c_transform_fieldtable;
    SV *perl_transform;
};

class UnknownFieldTransform {
public:
    UnknownFieldTransform(CUnknownFieldTransform c_transform);

    // clear and delete this object, it is needed just to avoid
    // having a THX member in this object
    void destroy(pTHX);

    void transform(pTHX_ UnknownFieldContext *field_context, SV *value) const {
        c_transform(aTHX_ field_context, value);
    }

private:
    // private to make sure deletion goes through destroy()
    ~UnknownFieldTransform() {}

    CUnknownFieldTransform c_transform;
};

class DecoderTransformQueue {
    struct PendingTransform {
        const DecoderTransform *transform;
        SV *target;
        int fieldtable_offset;
        int fieldtable_size;

        PendingTransform(SV *_target, const DecoderTransform *_transform) {
            target = _target;
            transform = _transform;
            fieldtable_offset = -1;
        }
    };

public:
    DecoderTransformQueue(pTHX);
    ~DecoderTransformQueue();

    void clear();
    size_t add_transform(SV *target, const DecoderTransform *message_transform, const DecoderTransform *field_transform);
    void finish_add_transform(size_t index, int size, DecoderFieldtable::Entry *entries);
    void apply_transforms();

    static void static_clear(DecoderTransformQueue *queue);

private:
    DECL_THX_MEMBER;
    std::vector<PendingTransform> pending_transforms;
    std::vector<DecoderFieldtable::Entry> fieldtable;
};

void fieldtable_debug_decoder_transform(pTHX_ SV *target, DecoderFieldtable *fieldtable);
void fieldtable_profile_decoder_transform(pTHX_ SV *target, DecoderFieldtable *fieldtable);
void fieldtable_debug_encoder_transform(pTHX_ EncoderFieldtable **fieldtable, SV *value);
void fieldtable_debug_encoder_unknown_fields(pTHX_ UnknownFieldContext *field_context, SV *value);
AV *fieldtable_debug_encoder_unknown_fields_get();

}
}

#endif