/*jslint single: true, long:true, for:true, unordered:true*/
/*global ScenesCommon*/

const StargazerScene = (function () {
    const COUNT_OF_SETS = 360;

    const FADING_STEPS_MIN = 2;
    const FADING_STEPS_MAX = 10;
    const VISIBLE_STEPS_MIN = 5;
    const VISIBLE_STEPS_MAX = 30;

    function getName() {
        return "Stargazer";
    }

    function getIdentifier() {
        return "com.corsair.cc.sc.stargazer";
    }

    function getParameters() {
        return [
            {
                name: "color_hint",
                title: "Define 2 colors for your scene.",
                type: "hint"
            },
            {
                name: "foreground",
                title: "Foreground",
                type: "color",
                default: "#00c8c8"
            },
            {
                name: "background",
                title: "Background",
                type: "color",
                default: "#008000"
            },
            {
                name: "speed",
                title: "Speed",
                type: "int_enum",
/*jslint-disable*/
                values: {700: "Very Slow", 100: "Slow", 30: "Medium", 1: "Fast"},
/*jslint-enable*/
                default: 30
            },
            {
                name: "numStars",
                title: "Number of stars",
                type: "int",
                min: 0,
                max: 60,
                default: 30
            }
        ];
    }

    function generateScene(sceneID, sceneName, numLeds, parameters) {

        const config = getParameters();
        const background = ScenesCommon.colorFromHexString(ScenesCommon.valueOrDefaultFromParameters(parameters, config, 'background'));
        const foreground = ScenesCommon.colorFromHexString(ScenesCommon.valueOrDefaultFromParameters(parameters, config, 'foreground'));
        const numStars = ScenesCommon.valueOrDefaultFromParameters(parameters, config, 'numStars');
        const durationPerSet = ScenesCommon.valueOrDefaultFromParameters(parameters, config, 'speed');

        // Config
        const segmentSize = Math.round(parseFloat(numLeds) / parseFloat(numStars));

        // Generate stars
        let stars = generateStars(numStars, segmentSize);

        // Create scene data
        let sceneData = ScenesCommon.createSceneData(sceneID, numLeds, sceneName);
        let set = 0;

        // Cycle through all sets
        for (set = 0; set < COUNT_OF_SETS; set += 1) {

            // Create set
            let sceneDataSet = getSceneDataSet(background, foreground, numLeds, durationPerSet, set, stars);

            // Add set
            sceneData.sceneDatas.push(sceneDataSet);
        }

        return sceneData;
    }

    function getIcon() {
        return `<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="100%" height="100%" viewBox="0 0 16 16">
    <path id="icon" fill="#d8d8d8" fill-rule="evenodd" d="M9.5,1.5l1.08,2.92l2.92,1.08l-2.92,1.08l-1.08,2.92l-1.08,-2.92l-2.92,-1.08l2.92,-1.08l1.08,-2.92Zm-5,5l0.675,1.825l1.825,0.675l-1.825,0.675l-0.675,1.825l-0.675,-1.825l-1.825,-0.675l1.825,-0.675l0.675,-1.825Zm7.5,2.5l0.675,1.825l1.825,0.675l-1.825,0.675l-0.675,1.825l-0.675,-1.825l-1.825,-0.675l1.825,-0.675l0.675,-1.825Z"/>
    </svg>`;
    }

    function getSceneDataSet(background, foreground, numLeds, duration, set, stars) {

        // Create scene data set
        let sceneDataSet = ScenesCommon.createSceneDataSet(numLeds, duration);
        let led = 0;

        // Set colors
        for (led = 0; led < numLeds; led += 1) {
            if (!setStarsInSceneDataSet(sceneDataSet, background, foreground, set, led, stars)) {
                sceneDataSet.rgbArray.push(background.r);
                sceneDataSet.rgbArray.push(background.g);
                sceneDataSet.rgbArray.push(background.b);
            }
        }

        return sceneDataSet;
    }

    function colorForStar(background, foreground, set, led, star) {
        if (led !== star.position) {
            return null;
        }

        if (set < star.start) {
            return null;
        }

        let totalLength = star.length + 2 * star.fadingLength;

        if (set > star.start + totalLength) {
            // remove the star as it is completely added to the scene
            return {remove: true};
        }

        // Calculate opacity
        let foregroundOpacity = 1.0;
        const fadeInEnd = star.start + star.fadingLength;
        const fadeOutStart = fadeInEnd + star.length;
        const fadeOutEnd = fadeOutStart + star.fadingLength;

        if (set >= star.start && set <= fadeInEnd) {
            foregroundOpacity = (set - star.start) / star.fadingLength;
        } else if (set >= fadeOutStart && set <= fadeOutEnd) {
            foregroundOpacity = (fadeOutEnd - set) / star.fadingLength;
        }

        return {color: overlayColor(background, foreground, foregroundOpacity)};
    }

    function setStarsInSceneDataSet(sceneDataSet, background, foreground, set, led, stars) {
        let starsLength = stars.length;
        let i = 0;
        let result = false;

        for (i = 0; i < starsLength; i += 1) {
            let star = stars[i];
            let color = colorForStar(background, foreground, set, led, star);

            if (color !== null) {
                if (color.color) {
                    setColorInByteArray(sceneDataSet.rgbArray, led, color.color);
                    result = true;
                    break;
                }

                if (color.remove === true) {
                    stars.splice(i, 1);
                    i -= 1;
                    starsLength -= 1;
                }
            }
        }

        return result;
    }

    function generateStars(numSegments, segmentSize) {
        let stars = [];
        let segment = 0;

        for (segment = 0; segment < numSegments; segment += 1) {

            let set = 0;
            while (set <= COUNT_OF_SETS) {

                // Set star config
                let star = {
                    position: segment * segmentSize + Math.ceil(Math.random() * segmentSize),
                    start: set,
                    length: VISIBLE_STEPS_MIN + Math.ceil(Math.random() * VISIBLE_STEPS_MAX),
                    fadingLength: FADING_STEPS_MIN + Math.ceil(Math.random() * FADING_STEPS_MAX)
                };

                // Check total length
                let end = star.start + 2 * star.fadingLength + star.length;
                if (end > COUNT_OF_SETS) {
                    if (end - star.length < COUNT_OF_SETS) {
                        star.length = star.length - (end - COUNT_OF_SETS);
                    } else {
                        break;
                    }
                }

                // Add star
                stars.push(star);

                // Next set
                set = set + star.length + 2 * star.fadingLength;
            }
        }

        return stars;
    }

    function setColorInByteArray(byteArray, position, color) {

        // Set byte values
        const pos = position * 3;
        byteArray[pos] = color.r;
        byteArray[pos + 1] = color.g;
        byteArray[pos + 2] = color.b;
    }

    function overlayColor(backgroundColor, foregroundColor, foregroundOpacity) {

        const backgroundOpacity = 1.0 - foregroundOpacity;
        // Calculate RGB values
        let color = {
            r: Math.min(backgroundColor.r * backgroundOpacity + foregroundColor.r * foregroundOpacity, 255),
            g: Math.min(backgroundColor.g * backgroundOpacity + foregroundColor.g * foregroundOpacity, 255),
            b: Math.min(backgroundColor.b * backgroundOpacity + foregroundColor.b * foregroundOpacity, 255)
        };

        return color;
    }

    return {
        name: getName,
        identifier: getIdentifier,
        parameters: getParameters,
        generate_scene: generateScene,
        icon: getIcon
    };
}());


function getName() {
    return StargazerScene.name();
}

function getIdentifier() {
    return StargazerScene.identifier();
}

function getParameters() {
    return StargazerScene.parameters();
}

function generateScene(sceneID, sceneName, numLeds, parameters) {
    return StargazerScene.generate_scene(sceneID, sceneName, numLeds, parameters);
}

function getIcon() {
    return StargazerScene.icon();
}
