console.log("Hello World");

/* Syntax :
function functionName() {
    code block (statement)
}
*/
// function keyword - used to define a javascript function
// functionName = the funtion name itself, functions are named to be able to use later in the code.
// function block ({}) - the statements which comprise the body of the function, this is where the code is to be executed.


function printName(){
    console.log("My Name is John");
};

printName();


// Semicolons are used to separate executable Javascript statements:

// Function Invocation
// The code block and statements inside a function is not immediately executed when the function is defined.. It executes when it is invoke or called.

// Ex.

printName();

// declaredFunction; - this results in an error, much like variables, we cannot invoke a function we have yet to define. 

// Function declarations vs expressions
    // Function Declaration

    // A function can be created through function declaration by using the function keyword and adding a function name.

    // Declared functions are not executed immediately. They are "saved for later use", and will be executed later, when they are invoked (called upon).
    
    declaredFunction(); // declared functions can be hoisted, As long as the function has been defined.

    function declaredFunction() {
        console.log("Hello world from declaredFunction()")
    }

    declaredFunction();

    // A function can also be stored in a variable. This is called a function expression.

    // A function expression is an anonymous function assigned to the variableFunction

    // Anonymus function - is a function without a name

    // variableFunction();

    let variableFunction = function() {
        console.log("Hello Again!");
    };

    variableFunction();

    // We can also create function exp[ression] named function.
    // However, to invoke the function expression, we invoke it by its variable name and not by its functionname.
    // Function expressions are always invoked (called) using the variable name.

    let funcExpression = function funcName() {

        console.log("Hello From The Other Side");

    };

    funcExpression();
    


    // You can reassign declared function and function expression to new anonymous functions.

    declaredFunction = function() {
        console.log("updated declaredFunction");
    }

    declaredFunction();

    funcExpression = function () {
        console.log("Updated funcExpression");
    }

    funcExpression();

    // However, we cannot re-assign a function expression initialized with const

    // const constantFunc = function(){
    //     console.log("Initialized with const!")
    // }

    // constantFunc();

    // constantFunc = function(){
    //     console.log("Cannot be re-assigned!")
    // }

    // constantFunc();

    // Function Scoping

        /* Scope is the accessibility (visibility) of variables within our program.
        Javascript Variables has 3 types of scope:
        1. local scope
        2. global scope
        3. function scope

        */

        {
            let localVar = "Armando Perez";
        }

        let globalVar = "Mr. Worldwide"

        console.log(globalVar);

        // This would result to an error because a local-scoped variable cannot be accessed outside of its code block.
        // console.log(localVar);

        // Function Scope

        /*
        Javascript has function scope: Each function creates a new scope.
        Variables defined inside a function, are not accessible (visible)
        from the outside of the function.

        Variables declared with va, let and const are quote similar when declared inside a function.
        */

        function showNames() {
            // Function scoped variables:
            var functionVar = "Joe";
            const functionConst = "John";
            let functionLet = "Jane";

            console.log(functionVar);
            console.log(functionConst);
            console.log(functionLet);
        }

        showNames();

        // console.log(functionVar);
        // console.log(functionConst);
        // console.log(functionLet)

        /*

        The Variables, functionVar, functionConst, and functionLet, are function scoped and cannot be accessed outside of the function they were declared in.

        */

        // Nested Functions

        // You can create another fuynction inside a function. This is called nested function. This nested function, being inside the myNewFRunction will have access to the variable, name as they are within the scope /code block.
        
        
        function myNewFunction() {
            let name ="Jane";

            function nestedFunction(){
                let nestedName = "John"
                console.log(nestedName);
            }
            // NestedName variable, being declared in the nestedFunction cannot be accessed outside of the function it was declared in.
            nestedFunction();
        }
        myNewFunction();
        // Moreover, since this function is declared inside myNewFunction, it too cannot be invoked outside of the function it was declared in.


        // Function and global scoped variables
            
            // Global Scoped Variable
            let globalName = "Alexandro";

            function myNewFunction2(){
                let nameInside = "Renz"

                // Variable declared globally (outside any function) have global scope.

                // Global variables can be accessed from anywhere in a Javascript.
                // Program including from inside a function.
                console.log(globalName);
            }

            myNewFunction2();
            // Console.log(nameInside); it would result to an error.
            // nameInside is function scoped, it cannot be accessed globally.

/*
    The "return" statement allows us to output a value from a function to be passed to the line/block of code that invoke/called the function.
    returnUserName() is a function that is a able to return a value which can then be saved in a variable.
*/    
            function returnFullName() {
                return "Jeffrey" + ' ' + "Smith" + ' ' + "Bezoz";
                console.log("This message will not be printed");
            }

            let fullName = returnFullName();
            console.log(fullName);

            console.log(returnFullName());

/*
This way, a function is able to return a value we can further use/manipulate in our program instead of only printing/displaying it in the console.
Notice that the console.log() after the return is no longer printer in the console that is because ideally any line/block of code that comes after the return statement is ignored because it ends the function execution.
returnFullName() function.

*/

console.log(returnFullName());

/*
        You can also create a variable inside the function to contain the result and return that variable instead. You can do this for ease of use or fore code readability.
*/

    function returnFullAddress (){
        let fullAddress = {
            street: "#44 Maharlika St.",
            city: "Cainta",
            province: "Rizal",
        };
        return fullAddress;
    }

    let myAddress = returnFullAddress();
    console.log(myAddress);

// On the other hand, when a function that only has console.log() to display its result, it will return an underfined:

    function printPlayerInfo() {
        console.log("Username: " + "white_knight");
        console.log("Level " + 95);
        console.log("Job: " + "Paladin");
    }
    let user1 = printPlayerInfo();
    console.log(user1);

    // Returns undefined because printPlayerIno returns nothing. It only displayes the details in the console.

    // You cannot save any value from printPlayerInfo() because it does not return anything.

    // You can return almost any data types from a function.

    function returnSumOf5and10() {
        return 5 + 10
    }

    let sumOf5and10 = returnSumOf5and10();
    console.log(sumOf5and10)

    let total = 100 + returnSumOf5and10();
    console.log(total);

    // Simulates getting an array of user names from a database
    function getGuildMembers() {
        return ["white_knight", "healer2000", "masterThief100", "andrewthehero", "ladylady99"]
    }

    console.log(getGuildMembers());

    // Function Naming Conventions
    // Function names should be definitive of the task it will perform. It usually contains a verb

    function getCourses() {
        let courses = ["Science 101", "Math 101", "English 101"];
        return courses;
    }
    let courses = getCourses();
    console.log(courses);
    
    // Avoid generic names to avoid confusion within your code.