import {Units as AGCUnits, NoiseReductionMode} from '@int/geotoolkit/seismic/pipeline/processor/AGC';
import {MementoDeserializationContext} from '@int/geotoolkit/persistence/MementoDeserializationContext';
import {MementoSerializationContext} from '@int/geotoolkit/persistence/MementoSerializationContext';
import {UIRegistry} from '@int/geotoolkit/persistence/UIRegistry';
import {createAjv as create} from 'jsonforms-core';
import cloneDeep from 'lodash/cloneDeep';
import schemas from '@int/geotoolkit/resources/schema';
import set from 'lodash/set';
import {InterpolationType} from '@int/geotoolkit/seismic/pipeline/InterpolationType';
import {NormalizationType} from '@int/geotoolkit/seismic/pipeline/NormalizationType';
import {SeismicColors} from '@int/geotoolkit/seismic/util/SeismicColors';
import {MathUtil} from '@int/geotoolkit/util/MathUtil';

const registry = new UIRegistry();
const colorMaps = SeismicColors.getDefault().listNameColorMaps();
const InterpolationTypeKeys = Object.keys(InterpolationType);
const NormalizationTypeKeys = Object.keys(NormalizationType);
const AGCUnitsKeys = Object.keys(AGCUnits);
const noiseReductionKeys = Object.keys(NoiseReductionMode);
const noiseReductionKey = (value) => noiseReductionKeys.find((key) => NoiseReductionMode[key] === value);

const getAjv = (options) => {
    const createAjv = (patch) => {
        let schema = JSON.parse(JSON.stringify(schemas));
        patch = JSON.parse(JSON.stringify(patch));
        const modify = (schema, patch) => {
            for (const id in patch) {
                if (schema['id'] === id || schema['$id'] === id) {
                    for (const key in patch[id]) {
                        const value = patch[id][key];
                        if (value != null) {
                            set(schema, key, value);
                        } else {
                            delete schema[key];
                        }
                    }
                }
            }
            for (const key in schema) {
                if (typeof schema[key] === 'object' && schema[key] != null) {
                    schema[key] = modify(schema[key], patch);
                }
            }
            return schema;
        };
        if (patch) {
            schema = modify(schema, patch);
        }
        const ajv = create({
            multipleOfPrecision: 3,
            addUsedSchema: false
        });
        ajv.addFormat('color', /^((0x){0,1}|#{0,1})([0-9A-F]{8}|[0-9A-F]{6})$/i);
        ajv.addSchema(schema);
        return ajv;
    };
    return createAjv({
        '/geotoolkit.seismic.widgets.SeismicWidget': {
            'charts': {
                'type': 'array',
                'items': {
                    'type': 'object',
                    'properties': {
                        'linestyle': {
                            'type': 'string',
                            'label': 'Color',
                            'format': 'color'
                        },
                        'name': {
                            'type': 'string',
                            'label': 'Chart name',
                            'enum': options.chartNames
                        }
                    }
                }
            },
            'headers': {
                'type': 'array',
                'items': {
                    'type': 'object',
                    'properties': {
                        'color': {
                            'type': 'string',
                            'label': 'Color',
                            'format': 'color'
                        },
                        'name': {
                            'type': 'string',
                            'label': 'Chart name',
                            'enum': options.chartNames
                        }
                    }
                }
            }
        },
        '/geotoolkit.seismic.pipeline.SeismicPipeline/properties/normalization/properties/scale': {
            'minimum': 0.1,
            'maximum': 5,
            'multipleOf': 0.1,
            'default': 0.3
        },
        '/geotoolkit.seismic.pipeline.SeismicPipeline/properties/colors/properties/colorMap': {
            'type': 'string',
            'enum': colorMaps
        },
        '/geotoolkit.seismic.pipeline.processor.AGC/properties/units': {
            'type': 'string',
            'enum': AGCUnitsKeys
        },
        '/geotoolkit.seismic.pipeline.processor.AGC/properties/noiseReduction': {
            'type': 'string',
            'enum': noiseReductionKeys
        }
    });
};

const getNodeProps = (node) => {
    const context = new MementoSerializationContext(null, registry);
    const widget = node.getWidget();
    context.setObject(widget);
    const props = context.getMemento();
    props.scale.tracescale = MathUtil.round(props.scale.tracescale, 10);
    props.scale.samplescale = MathUtil.round(props.scale.samplescale, 100);
    props.pipeline.interpolation.traces.type = InterpolationTypeKeys[props.pipeline.interpolation.traces.type - 1];
    props.pipeline.interpolation.samples.type = InterpolationTypeKeys[props.pipeline.interpolation.samples.type - 1];
    props.pipeline.normalization.type = NormalizationTypeKeys[props.pipeline.normalization.type];
    props.pipeline.normalization.limits.low = MathUtil.round(props.pipeline.normalization.limits.low, 100);
    props.pipeline.normalization.limits.high = MathUtil.round(props.pipeline.normalization.limits.high, 100);
    props.isServerRendering = true;
    if (props.pipeline.dataProcessors.AGC != null) {
        props.isServerRendering = false;
        props.pipeline.dataProcessors.AGC.units = AGCUnitsKeys[props.pipeline.dataProcessors.AGC.units];
    }
    props.charts = props.auxiliarychart.charts.map((chart) => ({
        linestyle: chart.linestyle,
        name: chart.name
    }));
    const headers = [];
    props.axes.headers.fields.forEach((field) => {
        if (field.visible) {
            headers.push({
                color: field.color,
                name: field.name
            });
        }
    });
    props.headers = headers;
    const agcProcess = props.pipeline.processes.find((process) => process.name === 'AGC');
    if (agcProcess) {
        agcProcess.units = AGCUnitsKeys[agcProcess.units];
        agcProcess.noiseReduction = noiseReductionKey(agcProcess.noiseReduction);
    }
    return props;
};
const setNodeProps = (node, props) => {
    props = cloneDeep(props);
    const widget = node.getWidget();
    const headerNames = node.getChartNames();
    props.pipeline.interpolation.traces.type = InterpolationType[props.pipeline.interpolation.traces.type];
    props.pipeline.interpolation.samples.type = InterpolationType[props.pipeline.interpolation.samples.type];
    props.pipeline.normalization.type = NormalizationType[props.pipeline.normalization.type];
    if (!props.isServerRendering) {
        props.pipeline.dataProcessors.AGC.units = AGCUnits[props.pipeline.dataProcessors.AGC.units];
    }

    const charts = [];
    props.charts.forEach((chart) => {
        if (chart.name !== '') {
            charts.push({
                linestyle: chart.linestyle,
                name: chart.name,
                visible: true
            });
        }
    });
    props.auxiliarychart.charts = charts;
    const fields = headerNames.map((name) => {
        const index = props.headers.findIndex((header) => header.name === name);
        const color = index !== -1 ? props.headers[index].color || 'black' : 'black';
        const visible = index !== -1;
        return {color, name, visible};
    });
    props.axes.headers.fields = fields;

    const context = new MementoDeserializationContext(props, registry);
    const deserializer = context.getRegistry().getSerializer(widget.getClassName());
    if (deserializer != null) {
        deserializer.load(context, widget);
        widget.invalidate();
    }
    return props;
};
export {getAjv, getNodeProps, setNodeProps};
