import SparqlTask from '../Core/SparqlTask';
import Insert from './Insert';
import { Type } from '../Core/types';
import { parseValue } from '../SparqlObject/common'
import formatter from '../common/stringifier'

type insertInstance = {
    [key: string]: string | string[] | insertInstance | insertInstance[]
}

let allBodiesAsTriples: {[reference: string]: Array<string>[]};

export function initialize(this: Insert, reference: string, insertRequest: insertInstance[]) {
    allBodiesAsTriples = {}
    insertRequest.forEach((jsonRequest) => {

        parseInstance(jsonRequest, reference)
    });
    this.insertModel = allBodiesAsTriples;
}


function initializeTripleFromReference(reference: string) {
    let allTriples: string[] = [];
    let triplesFromArrayable: string[] = [];
    SparqlTask.sparqlObjects[reference].attributes.forEach((attribute) => {
        if (!attribute.arrayable) {
            allTriples.push(...attribute.triples)
        } else {
            triplesFromArrayable.push(...attribute.triples)
        }
    })
    let out = allTriples.filter((triple, index, array) => {
        return (array.indexOf(triple) === index) && // Unique value
            (triplesFromArrayable.indexOf(triple) === -1); // And not from an arrayable attribute 
    });
    return out
}

function parseInstance(instance: insertInstance, reference: string) {
    let model = SparqlTask.sparqlObjects[reference];
    let attributesDictionary = model.dictionify();
    let initialBody = initializeTripleFromReference(reference);
    let dictionary: {[attribute: string] : string} = {}
    Object.keys(instance).forEach((attributeName) => {
        let attribute = attributesDictionary[attributeName]
        if (attribute.arrayable) {
            if (attribute.type === Type.OBJECT && typeof instance[attributeName] === 'object') {
                let pKey = SparqlTask.sparqlObjects[attribute.reference].primaryKey;
                let subInstanceArray = (<insertInstance[]>instance[attributeName]);
                subInstanceArray.forEach((subInstance) => {
                    let value = parseValue((<string> subInstance[pKey]));
                    initialBody.push(...attribute.parseTriple(value))
                    parseInstance(subInstance, attribute.reference);
                })
            } else {
                let subValueArray = (<string[]>instance[attributeName]);
                subValueArray.map((subValue) => parseValue(subValue)).forEach((subValue) => {
                    initialBody.push(...attribute.parseTriple(subValue))
                })
            }
        } else {
            if (attribute.type === Type.OBJECT && typeof instance[attributeName] === 'object') {
                let pKey = SparqlTask.sparqlObjects[attribute.reference].primaryKey;
                let subInstance = (<insertInstance>instance[attributeName])
                let value = parseValue((<string>subInstance[pKey]))
                dictionary[attributeName] = value;
                parseInstance(subInstance, attribute.reference);
            } else {
                let value = parseValue((<string>instance[attributeName]));
                dictionary[attributeName] = value;
            }
        }
    })
    initialBody = initialBody.map((triple) => {           
        return formatter(triple, dictionary, {keyPrefix: '?', asRegExp: false});
    }) 
    allBodiesAsTriples[reference] = allBodiesAsTriples[reference] || [];
    allBodiesAsTriples[reference].push(initialBody);
}


export default initialize;