import * as fs from 'fs';
import Serializable from './Serializable.js';
import Serializables from './Serializables.js';

export default class SerializableHelper {

    /**
     * Read a Serializable object (or an array of them) from given file
     * 
     * @param {string} path 
     * @param {string} filename 
     * @param {Serializable} type
     * 
     * @returns {Serializable | Serializable[]} 
     */
    static load(path, filename, type) {
        if (!fs.existsSync(path + "/" + filename)) {
            throw new Error("File '" + path + "/" + filename + "' does not exists!");
        }

        const fileContent = fs.readFileSync(path + "/" + filename).toString();

        return SerializableHelper.deserialize(fileContent, type);
    }

    /**
     * Writes a Serializeable object to the given filen.
     * If the file allready exists, it will be overriden.
     * 
     * @param {string} path path to the file (use '.' for same directory)
     * @param {string} filename filename 
     * @param {Serializable | Serializable[]} obj objekt or array of objects to save
     * 
     * @throws {Error} throws exception if object is not implementing Serializable and is not an array of Serializable
     */
    static save(path, filename, obj) {
        fs.writeFileSync(path + "/" + filename, SerializableHelper.serialize(obj));
    }

    /**
     * @param {Serializable | Serializable[]} obj
     * @returns {string}
     */
    static serialize(obj){
        if (obj !== null
            && (!(!Array.isArray(obj) || (obj.length == 0 || typeof obj[0].toJson === 'function'))
            || (!Array.isArray(obj) && typeof obj.toJson !== 'function'))
        ) {
            throw new Error("Only serializeable objects can be saved! Given: " + typeof obj);
        }

        if(obj === null){
            return null;
        }

        if (!Array.isArray(obj)) {
            return obj.toJson();
        } else {
            const arr = [];
            obj.forEach(o => arr.push(o.toJson()));
            return JSON.stringify(arr);
        }
    }

    /**
     * @param {string} strg 
     * @param {Serializable} type 
     * @returns {Serializable | Serializable[]} 
     */
    static deserialize(strg, type){
        if(strg == null){
            return null;
        }

        if (strg.startsWith('[')) {
            const arr = JSON.parse(strg);
            const outArr = [];

            arr.forEach(so => {
                const tmpObj = JSON.parse(so);
                if (tmpObj != null && tmpObj.__className_srl__ != null && Serializables.classes.has(tmpObj.__className_srl__)) {
                    outArr.push(Serializables.classes.get(tmpObj.__className_srl__).fromJson(so));
                } else if(type != null) {
                    outArr.push(type.fromJson(so));
                } else {
                    throw new Error('Unable to deserialize object! Type or __className_srl__ parameter has to be set.');
                }
            });

            return outArr;
        } else {
            const tmpObj = JSON.parse(strg);

            if (tmpObj != null && tmpObj.__className_srl__ != null && Serializables.classes.has(tmpObj.__className_srl__)) {
                return Serializables.classes.get(tmpObj.__className_srl__).fromJson(strg);
            } else if(type != null){
                return type.fromJson(strg);
            }else {
                throw new Error('Unable to deserialize object! Type or __className_srl__ parameter has to be set.');
            }
        }
    }
}
