"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const decorator_1 = require("../../decorator");
const helper_1 = require("../../helper");
const mapper_1 = require("../mapper");
const util_1 = require("../util");
function collectionFromDb(attributeValue, propertyMetadata) {
    const explicitType = propertyMetadata && propertyMetadata.typeInfo ? propertyMetadata.typeInfo.type : null;
    if ('SS' in attributeValue) {
        const arr = attributeValue.SS;
        return explicitType && explicitType === Array ? arr : new Set(arr);
    }
    if ('NS' in attributeValue) {
        const arr = attributeValue.NS.map(parseFloat);
        return explicitType && explicitType === Array ? arr : new Set(arr);
    }
    if ('BS' in attributeValue) {
        const arr = attributeValue.BS;
        return explicitType && explicitType === Array ? arr : new Set(arr);
    }
    if ('L' in attributeValue) {
        let arr;
        if (decorator_1.hasGenericType(propertyMetadata)) {
            arr = attributeValue.L.map(item => mapper_1.fromDb(item.M, propertyMetadata.typeInfo.genericType));
        }
        else {
            // tslint:disable-next-line:no-unnecessary-callback-wrapper
            arr = attributeValue.L.map(v => mapper_1.fromDbOne(v));
        }
        return explicitType && explicitType === Set ? new Set(arr) : arr;
    }
    throw new Error('No Collection Data (SS | NS | BS | L) was found in attribute data');
}
function collectionToDb(propertyValue, propertyMetadata) {
    if (Array.isArray(propertyValue) || util_1.isSet(propertyValue)) {
        let collectionType;
        // detect collection type
        if (propertyMetadata) {
            // based on metadata
            collectionType = detectCollectionTypeFromMetadata(propertyMetadata, propertyValue);
        }
        else {
            // based on value
            collectionType = util_1.detectCollectionTypeFromValue(propertyValue);
        }
        // convert to array if we deal with a set for same behaviour
        propertyValue = util_1.isSet(propertyValue) ? Array.from(propertyValue) : propertyValue;
        // empty values are not allowed for S(et) types only for L(ist)
        if ((collectionType === 'SS' || collectionType === 'NS' || collectionType === 'BS') && propertyValue.length === 0) {
            return null;
        }
        // do the mapping depending on type
        switch (collectionType) {
            case 'SS':
                return { SS: propertyValue };
            case 'NS':
                return { NS: propertyValue.map(num => num.toString()) };
            case 'BS':
                return { BS: propertyValue };
            case 'L':
                if (decorator_1.hasGenericType(propertyMetadata)) {
                    return {
                        L: propertyValue.map(value => ({
                            M: mapper_1.toDb(value, propertyMetadata.typeInfo.genericType),
                        })),
                    };
                }
                else {
                    return {
                        L: propertyValue
                            // tslint:disable-next-line:no-unnecessary-callback-wrapper
                            .map(v => mapper_1.toDbOne(v))
                            .filter(helper_1.notNull),
                    };
                }
            default:
                throw new Error(`Collection type must be one of SS | NS | BS | L found type ${collectionType}`);
        }
    }
    else {
        throw new Error(`Given value must be either Array or Set ${propertyValue}`);
    }
}
exports.CollectionMapper = {
    fromDb: collectionFromDb,
    toDb: collectionToDb,
};
function detectCollectionTypeFromMetadata(propertyMetadata, propertyValue) {
    let collectionType;
    const explicitType = propertyMetadata && propertyMetadata.typeInfo ? propertyMetadata.typeInfo.type : null;
    switch (explicitType) {
        case Array:
            collectionType = 'L';
            break;
        case Set:
            if (propertyMetadata.isSortedCollection) {
                // only the L(ist) type preserves order
                collectionType = 'L';
            }
            else {
                if (decorator_1.hasGenericType(propertyMetadata)) {
                    // generic type of Set is defined, so decide based on the generic type which db set type should be used
                    if (util_1.isBufferType(propertyMetadata.typeInfo.genericType)) {
                        collectionType = 'BS';
                    }
                    else {
                        switch (propertyMetadata.typeInfo.genericType) {
                            case String:
                                collectionType = 'SS';
                                break;
                            case Number:
                                collectionType = 'NS';
                                break;
                            default:
                                // fallback to list if the type is not one of String or Number
                                collectionType = 'L';
                        }
                    }
                }
                else {
                    // auto detect based on set item values
                    collectionType = util_1.detectCollectionTypeFromValue(propertyValue);
                }
            }
            break;
        default:
            throw new Error(`only 'Array' and 'Set' are valid values for explicit type, found ${explicitType}`);
    }
    return collectionType;
}
//# sourceMappingURL=collection.mapper.js.map