import isObject from 'lodash/isObject';
import isArray from 'lodash/isArray';
import get from 'lodash/get';
import find from 'lodash/find';
import first from 'lodash/first';
import { isPositiveNumber } from 'Utils/Math/isPositiveNumber';
import {
    getDesignAreaColorAmount,
    getDesignAreaPreSelection,
    getSelectedDesignProductionMethodForDesignArea,
} from './Selectors';
import { visualizationModeCheck } from '../Configurator/Selectors';

/**
 * Creates the state from the JSON object returned by the server.
 *
 * Loops through the design views and builds a merged list from the design areas (that will contains only
 * one instance from all the available areas across the design views). It sets the designView origin for
 * every design area object, so later it can be identified.
 *
 * @param configurationModes
 * @returns {{filteredDesignAreas: Object[], selectedDesignArea: {}}}
 */
export function processConfigurationModes(configurationModes) {
    if (!isObject(configurationModes) || !isArray(configurationModes.designViews)) {
        // eslint-disable-next-line no-console
        console.warn('Invalid argument: configurationModes!', configurationModes);
        return {};
    }
    const { designViews, uploadMaxSize, maxZoom2d } = configurationModes;
    const filteredDesignAreas = [];
    const designAreaMap = {};
    for (let designView of designViews) {
        const designAreas = get(designView, 'designAreas') || [];
        for (let designArea of designAreas) {
            const { identifier } = designArea;
            if (identifier && !designAreaMap[identifier]) {
                // for the first occurrence of the design area, just add it to the list and the area map
                const da = {
                    ...designArea,
                    designView,
                };
                designAreaMap[identifier] = da;
                filteredDesignAreas.push(da);
            } else {
                // try to guess the main view for the design area
                const defaultViewIdentifier = get(designArea, 'defaultViewIdentifier'); // 2D
                const selectedCameraView = get(designArea, 'customData.selectedCameraView'); // 3D
                const viewIdentifier = defaultViewIdentifier || selectedCameraView;
                if (
                    (viewIdentifier && viewIdentifier === designView.identifier) ||
                    (!viewIdentifier && designAreas.length === 1) // it's the only area of the view
                ) {
                    Object.assign(designAreaMap[identifier], designArea, { designView });
                }
            }
        }
    }

    sortDesignAreas(filteredDesignAreas);

    return {
        filteredDesignAreas,
        selectedDesignArea: {},
        uploadMaxSize,
        initialCanvasPosition: filteredDesignAreas,
        maxZoom2d,
    };
}

const sortDesignAreas = designAreas =>
    designAreas.sort((a, b) => {
        if (a.sequenceNumber !== b.sequenceNumber) {
            return parseInt(a.sequenceNumber) < parseInt(b.sequenceNumber) ? -1 : 1;
        } else {
            return 0;
        }
    });

const createDesignAreaPatch = (designArea, sizeRatio, visualMode) => {
    const { widthRatio, heightRatio, aspectRatio } = sizeRatio;
    const position = { ...designArea.position };
    const baseShape = { ...designArea.baseShape };

    position.width = Math.round(designArea.position.width * widthRatio);
    position.height = Math.round(position.width / aspectRatio);

    // skip update if width or height is not valid
    if (!(isPositiveNumber(position.width) && isPositiveNumber(position.height))) {
        return false;
    }

    if (visualMode.is2D()) {
        // adjust the base shape size to fit the selected design area
        const { angle = 0 } = position;
        const sinAbs = Math.abs(Math.sin(angle));
        const cosAbs = Math.abs(Math.cos(angle));
        const bbWidthRatio = sinAbs * heightRatio + cosAbs * widthRatio;
        const bbHeightRatio = cosAbs * heightRatio + sinAbs * widthRatio;

        baseShape.width = Math.round(designArea.baseShape.width * bbWidthRatio);
        baseShape.height = Math.round(designArea.baseShape.height * bbHeightRatio);
        position.x = position.x * bbWidthRatio;
        position.y = position.y * bbHeightRatio;

        // recalculate control points if they are being used
        if (baseShape.useControlPoints && designArea.baseShape.points) {
            baseShape.points = {};
            for (const [pointKey, pointCoords] of Object.entries(designArea.baseShape.points)) {
                baseShape.points[pointKey] = {
                    x: pointCoords.x * bbWidthRatio,
                    y: pointCoords.y * bbHeightRatio,
                };
            }
        }
    }

    return { position, baseShape };
};

export function recalculateSingleDesignArea(state, designArea) {
    const visualMode = visualizationModeCheck(state);

    const { designProductionMethods } = designArea;
    const sizeObject =
        designArea.width && designArea.height ? designArea : first(designProductionMethods) || {};
    const dpm = getSelectedDesignProductionMethodForDesignArea(state, designArea);

    const widthRatio = dpm.width / sizeObject.width;
    const heightRatio = dpm.height / sizeObject.height;
    const aspectRatio = dpm.width / dpm.height;

    const sizeRatio = { widthRatio, heightRatio, aspectRatio };

    const patch = createDesignAreaPatch(designArea, sizeRatio, visualMode);

    return {
        ...designArea,
        ...patch,
    };
}

export function recalculateDesignAreas(designAreaState, designProductionMethodMap, visualMode) {
    const designAreas = designAreaState.selectedDesignArea.identifier
        ? [designAreaState.selectedDesignArea]
        : designAreaState.filteredDesignAreas;
    const filteredDesignAreas = [...designAreaState.filteredDesignAreas];

    let selectedDesignArea = designAreaState.selectedDesignArea;

    for (const _designArea of designAreas) {
        const designArea = { ..._designArea };

        const { designProductionMethods } = designArea;
        const sizeObject =
            designArea.width && designArea.height ? designArea : first(designProductionMethods) || {};
        const dpm = designProductionMethodMap[designArea.identifier];

        const widthRatio = dpm.width / sizeObject.width;
        const heightRatio = dpm.height / sizeObject.height;
        const aspectRatio = dpm.width / dpm.height;

        const sizeRatio = { widthRatio, heightRatio, aspectRatio };

        const original = find(designAreaState.initialCanvasPosition, {
            identifier: designArea.identifier,
        });

        const patch = createDesignAreaPatch(original, sizeRatio, visualMode);

        if (!patch) {
            continue;
        }

        const designView = {
            ...designArea.designView,
            designAreas: designArea.designView.designAreas.map(da => {
                return da !== designArea && da.identifier === designArea.identifier
                    ? { ...da, ...patch }
                    : da;
            }),
        };

        Object.assign(designArea, { ...patch, designView });

        const index = filteredDesignAreas.indexOf(_designArea);
        if (index !== -1) {
            filteredDesignAreas[index] = designArea;
        }

        if (selectedDesignArea && selectedDesignArea.identifier === designArea.identifier) {
            selectedDesignArea = designArea;
        }
    }

    return {
        ...designAreaState,
        selectedDesignArea,
        filteredDesignAreas,
    };
}

export function createInitialDesignDataFromDesignAreas(designAreas, designData = {}) {
    const initialDesignData = {};
    for (let area of designAreas) {
        const data = isObject(designData)
            ? {
                  ...designData[area.identifier],
              }
            : {};

        const preSelection = getDesignAreaPreSelection(area.identifier);

        let method;

        if (preSelection.productionMethod) {
            const preSelectionMethod = find(area.designProductionMethods, {
                identifier: preSelection.productionMethod,
            });

            if (preSelectionMethod) {
                method = preSelectionMethod;
            }
        }

        if (!method) {
            method =
                find(area.designProductionMethods, { isDefault: true }) ||
                first(area.designProductionMethods);
        }

        if (!data.canvasData) {
            data.canvasData = null;
        }

        if (
            !data.designProductionMethodIdentifier ||
            !data.canvasData ||
            !find(area.designProductionMethods, { identifier: data.designProductionMethodIdentifier })
        ) {
            data.designProductionMethodIdentifier = method ? method.identifier : 'print';
            data.canvasData = null;
        }
        let preselectedColorAmount = data.colorAmount ? data.colorAmount : preSelection.colorAmount;

        const colorAmount = getDesignAreaColorAmount(
            +get(method, 'options.maxColorAmount', 0),
            +preselectedColorAmount
        );

        if (colorAmount !== null) {
            data.colorAmount = colorAmount;
        }

        initialDesignData[area.identifier] = data;
    }
    return initialDesignData;
}
