/*
*  Copyright 2017-2018 Michail Vourlakos <mvourlakos@gmail.com>
*
*  This file is part of Latte-Dock
*
*  Latte-Dock is free software; you can redistribute it and/or
*  modify it under the terms of the GNU General Public License as
*  published by the Free Software Foundation; either version 2 of
*  the License, or (at your option) any later version.
*
*  Latte-Dock is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*  GNU General Public License for more details.
*
*  You should have received a copy of the GNU General Public License
*  along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

//!         FILLWIDTH/FILLHEIGHT COMPUTATIONS
//! Computations in order to calculate correctly the sizes for applets
//! that are requesting fillWidth or fillHeight

//! qBound style function that is specialized in Layouts
//! meaning that -1 values are ignored for fillWidth(s)/Height(s)
function layoutBound(min, pref, max){
    if (max === -1) {
        max = pref === -1 ? min : pref;
    }

    if (pref === -1) {
        pref = max === -1 ? min : pref;
    }

    return Math.min(Math.max(min,pref),max);
}


//! initialize AppletItems flag "inFillCalculations" in order
//! to inform them that new calculations are taking place
function initLayoutForFillsCalculations(layout) {
    for(var i=0; i<layout.children.length; ++i) {
        var curApplet = layout.children[i];
        if (curApplet.needsFillSpace) {
            curApplet.inFillCalculations = true;
        }
    }
}

//! during step1/pass1 all applets that provide valid metrics (minimum/preferred/maximum values)
//! they gain a valid space in order to draw themeselves
function computeStep1ForLayout(layout, availableSpace, sizePerApplet, noOfApplets) {
    for(var i=0; i<layout.children.length; ++i) {
        var curApplet = layout.children[i];

        if (curApplet.needsFillSpace) {
            if (curApplet && curApplet.applet && curApplet.applet.Layout) {
                var minSize = root.isVertical ? curApplet.applet.Layout.minimumHeight : curApplet.applet.Layout.minimumWidth;
                var prefSize = root.isVertical ? curApplet.applet.Layout.preferredHeight : curApplet.applet.Layout.preferredWidth;
                var maxSize = root.isVertical ? curApplet.applet.Layout.maximumHeight : curApplet.applet.Layout.maximumWidth;

                // console.log( " s3_0 " + curApplet.applet.pluginName + " : (" +minSize+","+prefSize+","+maxSize+") ");

                minSize = minSize>=0 && minSize!==Infinity ? minSize : -1;
                prefSize = minSize>=0 && prefSize!==Infinity ? prefSize : -1;
                maxSize = maxSize>=0 && maxSize!== Infinity ? maxSize : -1;

                var appliedSize = -1;

                //! check if the applet does not provide any valid metrics and for that case
                //! the system must decide what space to be given after the applets that provide
                //! nice metrics are assigned their sizes
                var systemDecide = ((minSize<0) && (prefSize<0) && (maxSize<0));

                if (!systemDecide) {
                    if (noOfApplets>1) {
                        appliedSize = layoutBound(minSize, prefSize, maxSize);

                        // console.log( " s3_1 " + curApplet.applet.pluginName + " : (" +minSize+","+prefSize+","+maxSize+") -> " + appliedSize);
                    } else if (noOfApplets===1) {
                        //! at this step if only one applet has remained for which the max size is not null,
                        //! then for this applet we make sure the maximum size does not exceed the available space
                        //! in order for the applet to not be drawn outside the boundaries
                        appliedSize = layoutBound(minSize, prefSize, Math.min(maxSize, sizePerApplet));

                        // console.log( " s3_2 " + curApplet.applet.pluginName + " : (" +minSize+","+prefSize+","+maxSize+") -> " + appliedSize);
                    }

                    //! appliedSize is valid and is also lower than the availableSpace, if it is not lower then
                    //! for this applet the needed space will be provided as a second pass in a fair way
                    //! between all remained applets that did not gain a valid fill space
                    if (appliedSize>=0 && appliedSize<=sizePerApplet) {
                        var properSize = Math.min(appliedSize, availableSpace);
                        var thickness = root.isVertical ? root.width : root.height;
                        var adjustedSize = curApplet.isHidden ? 0 : Math.max(thickness, properSize);

                        curApplet.sizeForFill = adjustedSize;
                        curApplet.inFillCalculations = false;
                        availableSpace = Math.max(0, availableSpace - curApplet.sizeForFill);
                        noOfApplets = noOfApplets - 1;
                        sizePerApplet = noOfApplets > 1 ? Math.floor(availableSpace / noOfApplets) : availableSpace;

                        // console.log( " s3_3 " + curApplet.applet.pluginName + " assigned: " + curApplet.sizeForFill);
                    }
                }

                // console.log("s3_r " +curApplet.applet.pluginName + " : " + availableSpace + " _ " + sizePerApplet + " _ " + noOfApplets + "\n");
            }
        }
    }

    return [availableSpace, sizePerApplet, noOfApplets];
}

//! during step2/pass2 all the applets with fills
//! that remained with no computations from pass1
//! are updated with the algorithm's proposed size
function computeStep2ForLayout(layout, sizePerApplet, noOfApplets) {
    if (sizePerApplet>0) {
        if (noOfApplets === 0) {
            //! when all applets have assigned some size and there is still free space, we must find
            //! the most demanding space applet and assign the remaining space to it

            var mostDemandingAppletSize = 0;
            var mostDemandingApplet = undefined;

            //! applets with no strong opinion
            var neutralAppletsNo = 0;
            var neutralApplets = [];

            for(var i=0; i<layout.children.length; ++i) {
                var curApplet = layout.children[i];

                //! the most demanding applet is the one that has maximum size set to Infinity
                //! AND is not Neutral, meaning that it provided some valid metrics
                //! AND at the same time gained from step one the biggest space
                if (curApplet && curApplet.needsFillSpace && curApplet.applet && curApplet.applet.Layout) {
                    var minSize = root.isVertical ? curApplet.applet.Layout.minimumHeight : curApplet.applet.Layout.minimumWidth;
                    var prefSize = root.isVertical ? curApplet.applet.Layout.preferredHeight : curApplet.applet.Layout.preferredWidth;
                    var maxSize = root.isVertical ? curApplet.applet.Layout.maximumHeight : curApplet.applet.Layout.maximumWidth;

                    var isNeutral = (minSize<=0 && prefSize<=0);

                    // console.log( " s4_0 " + curApplet.applet.pluginName + " : (" +minSize+","+prefSize+","+maxSize+") ");

                    if (!isNeutral && maxSize===Infinity && curApplet.sizeForFill>mostDemandingAppletSize) {
                        mostDemandingApplet = curApplet;
                        mostDemandingAppletSize = curApplet.sizeForFill;
                    } else if (isNeutral) {
                        neutralAppletsNo = neutralAppletsNo + 1;
                        neutralApplets.push(curApplet);
                    }
                }
            }

            if (mostDemandingApplet) {
                //! the most demanding applet gains all the remaining space
                mostDemandingApplet.sizeForFill = mostDemandingApplet.sizeForFill + sizePerApplet;

                // console.log("s4_1  "+ mostDemandingApplet.applet.pluginName + " assigned: "  + mostDemandingApplet.sizeForFill + "\n");
            } else if (neutralAppletsNo>0) {
                //! if no demanding applets was found then the available space is splitted equally
                //! between all neutralApplets
                var adjustedAppletSize = (sizePerApplet / neutralAppletsNo);
                for (var j=0; j<neutralApplets.length; ++j) {
                     // console.log("s4_2.0  "+ neutralApplets[j].applet.pluginName + " _ " + neutralApplets[j].sizeForFill + " _ " + adjustedAppletSize);

                     neutralApplets[j].sizeForFill = neutralApplets[j].sizeForFill + adjustedAppletSize;

                     // console.log("s4_2  "+ neutralApplets[j].applet.pluginName + " assigned: "  + sizePerApplet + "\n");
                }
            }
        } else {
            for(var i=0; i<layout.children.length; ++i) {
                var curApplet = layout.children[i];

                if (curApplet && curApplet.needsFillSpace && curApplet.inFillCalculations) {
                    curApplet.sizeForFill = sizePerApplet;
                    // console.log("s4_3  "+ curApplet.applet.pluginName + " assigned: "  + sizePerApplet + "\n");
                    curApplet.inFillCalculations = false;
                }
            }
        }
    }
}

//! initialize the three layouts and execute the step1/phase1
//! it is used when the Centered (Main)Layout is used only or when the Main(Layout)
//! is empty in Justify mode
function initializationPhase(availableSpace, sizePerApplet, noOfApplets){
    if (root.panelAlignment === Latte.Types.Justify) {
        initLayoutForFillsCalculations(startLayout);
        initLayoutForFillsCalculations(endLayout);
    }
    initLayoutForFillsCalculations(mainLayout);

    // console.log("s3...");

    //! first pass in order to update sizes for applet that want to fill space
    //! but their maximum metrics are lower than the sizePerApplet
    var res = computeStep1ForLayout(mainLayout, availableSpace, sizePerApplet, noOfApplets);
    availableSpace = res[0]; sizePerApplet = res[1]; noOfApplets = res[2];

    // console.log( " i1 : " + availableSpace + " _ " + sizePerApplet + " _ " + noOfApplets );

    if (root.panelAlignment === Latte.Types.Justify) {
        res = computeStep1ForLayout(startLayout, availableSpace, sizePerApplet, noOfApplets);
        availableSpace = res[0]; sizePerApplet = res[1]; noOfApplets = res[2];
        // console.log( " i2 : " + availableSpace + " _ " + sizePerApplet + " _ " + noOfApplets );

        res = computeStep1ForLayout(endLayout, availableSpace, sizePerApplet, noOfApplets);
        availableSpace = res[0]; sizePerApplet = res[1]; noOfApplets = res[2];
        // console.log( " i3 : " + availableSpace + " _ " + sizePerApplet + " _ " + noOfApplets );
    }

    return [availableSpace, sizePerApplet, noOfApplets];
}


function updateSizeForAppletsInFill() {
    if ((!visibilityManager.thickAnimated && !root.inConfigureAppletsMode)
            || (behaveAsPlasmaPanel && root.inConfigureAppletsMode)) {
        // console.log("-------------");
        // console.log("s1...");
        var noA = startLayout.fillApplets + mainLayout.fillApplets + endLayout.fillApplets;

        if (noA === 0)
            return;

        // console.log("s2...");
        if (mainLayout.shownApplets === 0 || root.panelAlignment !== Latte.Types.Justify) {
            var availableSpace = Math.max(0, root.maxLength - startLayout.sizeWithNoFillApplets - mainLayout.sizeWithNoFillApplets - endLayout.sizeWithNoFillApplets - root.panelEdgeSpacing);
            var sizePerApplet = availableSpace / noA;

            var res = initializationPhase(availableSpace, sizePerApplet, noA);
            availableSpace = res[0];  sizePerApplet = res[1]; noA = res[2];

            // console.log("s4...");

            //! after step1 there is a chance that all applets were assigned a valid space
            //! but at the same time some space remained free. In such case we make sure
            //! that remained space will be assigned to the most demanding applet.
            //! This is achieved by <layout>No values. For step2 passing value!=0
            //! means default step2 behavior BUT value=0 means that remained space
            //! must be also assigned at the end.
            var remainedSpace = (noA === 0 && sizePerApplet > 0) ? true : false

            var startNo = -1;
            var mainNo = -1;
            var endNo = -1;

            if (remainedSpace) {
                if (startLayout.fillApplets > 0) {
                    startNo = 0;
                } else if (endLayout.fillApplets > 0) {
                    endNo = 0;
                } else if (mainLayout.fillApplets > 0) {
                    mainNo = 0;
                }
            }

            //! second pass in order to update sizes for applet that want to fill space
            //! these applets get the direct division of the available free space that
            //! remained from step1 OR the the free available space that no applet requested yet

            computeStep2ForLayout(startLayout, sizePerApplet, startNo); //default behavior
            computeStep2ForLayout(mainLayout, sizePerApplet, mainNo); //default behavior
            computeStep2ForLayout(endLayout, sizePerApplet, endNo); //default behavior

            //console.log("s5...");
        } else {
            //! Justify mode in all remaining cases

            //! compute the two free spaces around the centered layout
            //! they are called start and end accordingly
            var halfMainLayout = mainLayout.sizeWithNoFillApplets / 2;
            var availableSpaceStart = Math.max(0, root.maxLength/2 - startLayout.sizeWithNoFillApplets - halfMainLayout - root.panelEdgeSpacing/2);
            var availableSpaceEnd = Math.max(0, root.maxLength/2 - endLayout.sizeWithNoFillApplets - halfMainLayout - root.panelEdgeSpacing/2);
            var availableSpace = availableSpaceStart + availableSpaceEnd - mainLayout.sizeWithNoFillApplets;

            var sizePerAppletMain = mainLayout.fillApplets > 0 ? availableSpace / noA : 0 ;

            var noStart = startLayout.fillApplets;
            var noMain = mainLayout.fillApplets;
            var noEnd = endLayout.fillApplets;

            //! initialize the computations
            initLayoutForFillsCalculations(startLayout);
            initLayoutForFillsCalculations(mainLayout);
            initLayoutForFillsCalculations(endLayout);

            //console.log("s3...");
            var res;

            //! first pass
            if (mainLayout.fillApplets > 0){
                res = computeStep1ForLayout(mainLayout, availableSpace, sizePerAppletMain, noMain);
                sizePerAppletMain = res[1]; noMain = res[2];
                var dif = (availableSpace - res[0]) / 2;
                availableSpaceStart = availableSpaceStart - dif;
                availableSpaceEnd = availableSpaceEnd - dif;
            }

            var sizePerAppletStart = startLayout.fillApplets > 0 ? availableSpaceStart / noStart : 0 ;
            var sizePerAppletEnd = endLayout.fillApplets > 0 ? availableSpaceEnd / noEnd : 0 ;

            if (startLayout.fillApplets > 0) {
                res = computeStep1ForLayout(startLayout, availableSpaceStart, sizePerAppletStart, noStart);
                availableSpaceStart = res[0]; sizePerAppletStart = res[1]; noStart = res[2];
            }
            if (endLayout.fillApplets > 0) {
                res = computeStep1ForLayout(endLayout, availableSpaceEnd, sizePerAppletEnd, noEnd);
                availableSpaceEnd = res[0]; sizePerAppletEnd = res[1]; noEnd = res[2];
            }

            ////
            //! second pass

            // console.log(" S ::: " +startLayout.fillApplets + " _ " + sizePerAppletStart + " _ " + noStart);

            if (mainLayout.fillApplets > 0) {
                computeStep2ForLayout(mainLayout, sizePerAppletMain, noMain); //default behavior
            }

            if (startLayout.fillApplets > 0) {
                computeStep2ForLayout(startLayout, sizePerAppletStart, noStart);
            }

            if (endLayout.fillApplets > 0) {
                computeStep2ForLayout(endLayout, sizePerAppletEnd, noEnd);
            }
        }
    }
}
