/* Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#define CFP_LUCY
#define C_LUCY_INVERTER
#define C_LUCY_INVERTERENTRY
#include "XSBind.h"
#include "Lucy/Index/Inverter.h"
#include "Lucy/Document/Doc.h"
#include "Lucy/Index/Segment.h"
#include "Clownfish/Blob.h"
#include "Lucy/Plan/FieldType.h"
#include "Lucy/Plan/BlobType.h"
#include "Lucy/Plan/NumericType.h"
#include "Lucy/Plan/Schema.h"
#include "Lucy/Plan/TextType.h"

static lucy_InverterEntry*
S_fetch_entry(pTHX_ lucy_Inverter *self, HE *hash_entry) {
    lucy_InverterIVARS *const ivars = lucy_Inverter_IVARS(self);
    lucy_Schema *const schema = ivars->schema;
    STRLEN key_size;
    const char *key = XSBind_hash_key_to_utf8(aTHX_ hash_entry, &key_size);

    cfish_String *field = CFISH_SSTR_WRAP_UTF8(key, key_size);
    int32_t field_num = LUCY_Seg_Field_Num(ivars->segment, field);
    if (!field_num) {
        // This field seems not to be in the segment yet.  Try to find it in
        // the Schema.
        if (LUCY_Schema_Fetch_Type(schema, field)) {
            // The field is in the Schema.  Get a field num from the Segment.
            field_num = LUCY_Seg_Add_Field(ivars->segment, field);
        }
        else {
            // We've truly failed to find the field.  The user must
            // not have spec'd it.
            THROW(CFISH_ERR, "Unknown field name: '%s'", key);
        }
    }

    lucy_InverterEntry *entry
        = (lucy_InverterEntry*)CFISH_Vec_Fetch(ivars->entry_pool, field_num);
    if (!entry) {
        entry = lucy_InvEntry_new(schema, field, field_num);
        CFISH_Vec_Store(ivars->entry_pool, field_num, (cfish_Obj*)entry);
    }
    return entry;
}

void
LUCY_Inverter_Invert_Doc_IMP(lucy_Inverter *self, lucy_Doc *doc) {
    dTHX;
    HV  *const fields = (HV*)LUCY_Doc_Get_Fields(doc);
    I32  num_keys     = hv_iterinit(fields);

    // Prepare for the new doc.
    LUCY_Inverter_Set_Doc(self, doc);

    // Extract and invert the doc's fields.
    while (num_keys--) {
        HE *hash_entry = hv_iternext(fields);
        lucy_InverterEntry *inv_entry = S_fetch_entry(aTHX_ self, hash_entry);
        SV *value_sv = HeVAL(hash_entry);
        lucy_InverterEntryIVARS *const entry_ivars
            = lucy_InvEntry_IVARS(inv_entry);
        lucy_FieldType *type = entry_ivars->type;
        cfish_Obj *obj = NULL;

        // Get the field value, forcing text fields to UTF-8.
        switch (LUCY_FType_Primitive_ID(type) & lucy_FType_PRIMITIVE_ID_MASK) {
            case lucy_FType_TEXT: {
                    STRLEN val_len;
                    char *val_ptr = SvPVutf8(value_sv, val_len);
                    obj = (cfish_Obj*)cfish_Str_new_wrap_trusted_utf8(val_ptr,
                                                                      val_len);
                    break;
                }
            case lucy_FType_BLOB: {
                    STRLEN val_len;
                    char *val_ptr = SvPV(value_sv, val_len);
                    obj = (cfish_Obj*)cfish_Blob_new_wrap(val_ptr, val_len);
                    break;
                }
            case lucy_FType_INT32: {
                    obj = (cfish_Obj*)cfish_Int_new(SvIV(value_sv));
                    break;
                }
            case lucy_FType_INT64: {
                    // nwellnhof: Using SvNOK could avoid a int/float/int
                    // round-trip with 32-bit IVs.
                    int64_t val = sizeof(IV) == 8
                                  ? SvIV(value_sv)
                                  : (int64_t)SvNV(value_sv); // lossy
                    obj = (cfish_Obj*)cfish_Int_new(val);
                    break;
                }
            case lucy_FType_FLOAT32:
            case lucy_FType_FLOAT64: {
                    obj = (cfish_Obj*)cfish_Float_new(SvNV(value_sv));
                    break;
                }
            default:
                THROW(CFISH_ERR, "Unrecognized type: %o", type);
        }

        CFISH_DECREF(entry_ivars->value);
        entry_ivars->value = obj;

        LUCY_Inverter_Add_Field(self, inv_entry);
    }
}


