#Copyright (c) Microsoft. All rights reserved.
#Licensed under the MIT license. See LICENSE file in the project root for full license information.

### This option would be used by build scripts so to release a project directly to the mbed repo sights.  No compiles
### would occur.
###
### To use add -Drelease_the_project:bool=ON
option(release_the_project "Used to just push the project and adjust the appending of _bld" off)

cmake_minimum_required(VERSION 2.8.11)
set(HG_COMMIT_MSG "Automatic Build Commit" CACHE STRING "Passed to the mercurial commit")

project(${mbed_project_base})

### Functions
function(verifyRequiredParameters)
    set(ALL_PARAMETERS_PROVIDED TRUE)

    if(NOT DEFINED ENV{MBED_HG_USER})
        message(WARNING "MBED_HG_USER environment variable not defined.")
        set(ALL_PARAMETERS_PROVIDED FALSE)
    endif()

    if(NOT DEFINED ENV{MBED_USER})
        message(WARNING "MBED_USER environment variable not defined.")
        set(ALL_PARAMETERS_PROVIDED FALSE)
    endif()

    if(NOT DEFINED ENV{MBED_PWD})
        message(WARNING "MBED_PWD environment variable not defined.")
        set(ALL_PARAMETERS_PROVIDED FALSE)
    endif()

    if (NOT ${ALL_PARAMETERS_PROVIDED})
        message(FATAL_ERROR "Not all expected parameters provided. Please provide the parameters listed above.")
    endif()
endfunction(verifyRequiredParameters)


function(verifyTarIsInstalled)
    execute_process(COMMAND tar "--version" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} RESULT_VARIABLE result_code OUTPUT_QUIET)

    if(WIN32)
        if(${result_code} MATCHES "The system cannot find the file specified")
            message(FATAL_ERROR "Please install 'tar' and make it available on $PATH.")
        endif()
    endif()
endfunction(verifyTarIsInstalled)


function(verifyMercurialIsInstalled)
    execute_process(COMMAND hg "--version" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} RESULT_VARIABLE result_code OUTPUT_QUIET)

    if(WIN32)
        if(${result_code} MATCHES "The system cannot find the file specified")
            message(FATAL_ERROR "Please install 'Mercurial' and make it available on $PATH.")
        endif()
    endif()
endfunction(verifyMercurialIsInstalled)


function(verifyCompileMbedTool)
    if(WIN32)
        file(GLOB expanded_compile_mbed_tool ${compile_mbed_tool})
        if (NOT EXISTS ${expanded_compile_mbed_tool})
            message(FATAL_ERROR "Required tool " ${expanded_compile_mbed_tool} " could not be found. Please compile it first.")
        endif()
    endif()
endfunction(verifyCompileMbedTool)


function(verifyRequiredTools)
    verifyMercurialIsInstalled()
    verifyCompileMbedTool()
endfunction(verifyRequiredTools)


function(cloneMBEDRepository)
    set(mbed_repo_address https://$ENV{MBED_USER}:$ENV{MBED_PWD}@os.mbed.com/users/$ENV{MBED_USER}/code/${mbed_repo_name}/)

    message(STATUS "Trying to delete " ${local_repo_path})
    message(STATUS "Trying to delete " ${local_sublib_dir})
    message(STATUS "Trying to clone " ${mbed_repo_address})

    if (EXISTS ${local_sublib_dir})
        message(STATUS "Removing old sublib directory: " ${local_sublib_dir})
        file(REMOVE_RECURSE ${local_sublib_dir})

        if(EXISTS ${local_subdir_path})
            message(FATAL_ERROR "Failed removing sublib directory: " ${local_sublib_dir})
        endif()
    endif()
    file(MAKE_DIRECTORY  ${local_sublib_dir})
    if(EXISTS ${local_repo_path})
        message(STATUS "Removing existing cloned directory.")
        file(REMOVE_RECURSE ${local_repo_path})

        if(EXISTS ${local_repo_path})
            message(FATAL_ERROR "Failed removing existing repository clone.")
        endif()
    endif()


    message(STATUS "Cloning MBED code.")
    execute_process(COMMAND hg "clone" "--insecure" ${mbed_repo_address} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} RESULT_VARIABLE result_code)

    if(NOT ${result_code} EQUAL 0)
        message(FATAL_ERROR "Failed cloning MBED repository '" ${mbed_repo_name} "' (error code " ${result_code} ")")
    endif()
endfunction(cloneMBEDRepository)

function(updateMBEDSourceFiles)
    set(source_file_destination ${local_repo_path})
    set(exported_file_destination ${local_repo_path}/${mbed_project_base})
    #
    # go through and delete every non .lib .hg .bld file in the directory. Don't delete the exported file directory though!!!
    #
    file(GLOB existingFiles ${local_repo_path}/*)
    foreach (file_item ${existingFiles})
        STRING(FIND ${file_item} ".lib" string_with_lib)
        STRING(FIND ${file_item} ".bld" string_with_bld)
        STRING(FIND ${file_item} ".hg" string_with_hg)
        #
        # We try to see if the we have the subdirectory of the exported files.  We don't want to delete that directory
        # basically we are detecting if the subdirectory name is the very last thing in the file name.
        #
        STRING(FIND ${file_item} ${mbed_project_base} string_with_base REVERSE)
        if (NOT (${string_with_base} EQUAL -1))
            STRING(LENGTH ${file_item} file_item_length)
            STRING(LENGTH ${mbed_project_base} mbed_project_base_length)
            math(EXPR offset_of_end_of_base "${string_with_base} + ${mbed_project_base_length}")
            if (NOT (${offset_of_end_of_base} EQUAL ${file_item_length}))
                set(string_with_base -1)
            endif()
        endif()
        if ((${string_with_lib} EQUAL -1) AND
            (${string_with_hg} EQUAL -1) AND
            (${string_with_bld} EQUAL -1) AND
            (${string_with_base} EQUAL -1))
            file(REMOVE ${file_item})
        endif()
    endforeach()
    #
    # go through and delete every non .lib .hg .bld file in the exported file directory
    #
    file(GLOB existingFiles ${local_repo_path}/${mbed_project_base}/*)
    foreach (file_item ${existingFiles})
        STRING(FIND ${file_item} ".lib" string_with_lib)
        STRING(FIND ${file_item} ".bld" string_with_bld)
        STRING(FIND ${file_item} ".hg" string_with_hg)
        STRING(FIND ${file_item} ${mbed_project_base} string_with_base REVERSE)
        #
        # We try to see if the we have the subdirectory of the exported files.  We don't want to delete that directory
        # basically we are detecting if the subdirectory name is the very last thing in the file name.
        #
        if (NOT (${string_with_base} EQUAL -1))
            STRING(LENGTH ${file_item} file_item_length)
            STRING(LENGTH ${mbed_project_base} mbed_project_base_length)
            math(EXPR offset_of_end_of_base "${string_with_base} + ${mbed_project_base_length}")
            if (NOT (${offset_of_end_of_base} EQUAL ${file_item_length}))
                set(string_with_base -1)
            endif()
        endif()
        if ((${string_with_lib} EQUAL -1) AND
            (${string_with_hg} EQUAL -1) AND
            (${string_with_bld} EQUAL -1))
            file(REMOVE ${file_item})
        endif()
    endforeach()

    set(curr_destination)

    message(STATUS "Copying target project files.")
    foreach(file_item ${mbed_project_files})
        string(FIND ${file_item} "/internal/" position REVERSE)
        if (position EQUAL -1)
            set(curr_destination ${source_file_destination})
        else ()
            # There is an internal directory, set the curr_destination accordingly
            set(curr_destination "${source_file_destination}/internal/")
        endif ()

        file(GLOB expanded_file_item ${file_item})
        message(STATUS ${expanded_file_item})
        file(COPY ${expanded_file_item} DESTINATION ${curr_destination})
    endforeach()

    foreach(file_item ${mbed_exported_project_files})
        string(FIND ${file_item} "/internal/" position REVERSE)
        if (position EQUAL -1)
            set(curr_destination ${exported_file_destination})
        else ()
            # There is an internal directory, set the curr_destination accordingly
            set(curr_destination "${exported_file_destination}/internal/")
        endif ()

        file(GLOB expanded_file_item ${file_item})
        message(STATUS ${expanded_file_item})
        file(COPY ${expanded_file_item} DESTINATION ${curr_destination})
    endforeach()

endfunction(updateMBEDSourceFiles)

function(updateMBEDLibraries)

    file(GLOB libfiles ${local_repo_path}/*.lib)
    foreach(file_item ${libfiles})

        #
        # We go through all of the .lib files.  We only want to update what we own.
        #
        file (READ ${file_item} fileContents)
        STRING(FIND ${fileContents} /$ENV{MBED_USER}/ matchResult)
        if (NOT ${matchResult} EQUAL -1)

            #
            # Found one.  We want to get the the clone path of the library.  Go up to the slash and pound sign.  (The sha follows that.)
            #
            STRING(FIND ${fileContents} /\# uriEnd)
            if (NOT ${uriEnd} EQUAL -1)
                STRING(SUBSTRING ${fileContents} 0 ${uriEnd} libraryUri)

                #
                # Get the sha that exists in the file.  We'll compare after the clone to see if we need to change the file.
                # This is because even if we have the same sha, we could change the whitespace and this will cause hg to think the file has
                # changed.

                math(EXPR beginSha "${uriEnd} + 2}")
                STRING(SUBSTRING ${fileContents} ${beginSha} -1 rawOldSha)
                STRING(STRIP ${rawOldSha} oldSha)

                #
                # Get the name that the clone will go to.  (This is the last part of the uri).
                #
                STRING(FIND ${libraryUri} / finalSlash REVERSE)
                math(EXPR finalSlash "${finalSlash} + 1")
                STRING(SUBSTRING ${libraryUri} ${finalSlash} -1 libraryName)

                #
                # Our compile sandboxes are actually private.  So we need to pass the username and password to do the clone.
                # So we actually form the address all over again.  Sigh.
                #
                set(subLibraryCloningUri https://$ENV{MBED_USER}:$ENV{MBED_PWD}@os.mbed.com/users/$ENV{MBED_USER}/code/${libraryName}/)

                #
                # We got the parts we need.  Clone the lib and then go get the sha.
                #
                execute_process(COMMAND hg "clone" "--insecure" ${subLibraryCloningUri} WORKING_DIRECTORY ${local_sublib_dir} RESULT_VARIABLE result_code)
                if(NOT ${result_code} EQUAL 0)
                    message(FATAL_ERROR "Failed to clone the sub library " ${libraryName} " (error code " ${result_code} ")")
                endif()
                execute_process(COMMAND hg "id" "-i" WORKING_DIRECTORY ${local_sublib_dir}/${libraryName} RESULT_VARIABLE result_code OUTPUT_VARIABLE outputSha OUTPUT_STRIP_TRAILING_WHITESPACE)
                if(NOT ${result_code} EQUAL 0)
                    message(FATAL_ERROR "Failed to obtain the sha of the sublibrary" ${libraryName} "(error code " ${result_code} ")")
                endif()

                if (NOT ${outputSha} STREQUAL ${oldSha})
                    message(STATUS "Need to update the reference to libary at " ${libraryUri})
                    file(WRITE ${file_item} ${libraryUri} /\# ${outputSha})
                endif()
            else()
                message(FATAL "Improperly formed lib file: " ${file_item})
            endif()
        endif()
    endforeach()
endfunction(updateMBEDLibraries)

function(pushMBEDRepository)

    set(mbed_repo_address https://$ENV{MBED_USER}:$ENV{MBED_PWD}@os.mbed.com/users/$ENV{MBED_USER}/code/${mbed_repo_name}/)

    message(STATUS "Commiting changes to local repository.")

    execute_process(COMMAND hg "addremove" WORKING_DIRECTORY ${local_repo_path} RESULT_VARIABLE result_code)
    if(NOT ${result_code} EQUAL 0)
        message(FATAL_ERROR "Failed adding/removing local changes (error code " ${result_code} ")")
    endif()

    execute_process(COMMAND hg "commit" "-u" $ENV{MBED_HG_USER} "-m ${HG_COMMIT_MSG}" WORKING_DIRECTORY ${local_repo_path} RESULT_VARIABLE result_code)
    if(NOT ${result_code} EQUAL 0 AND NOT ${result_code} EQUAL 1)
        message(FATAL_ERROR "Failed commiting changes to local repository (error code " ${result_code} ")")
    endif()


    message(STATUS "Pushing changes to repo " ${mbed_repo_name})
    execute_process(COMMAND hg "push" "--insecure" ${mbed_repo_address} WORKING_DIRECTORY ${local_repo_path} RESULT_VARIABLE result_code)

    if(NOT ${result_code} EQUAL 0 AND NOT ${result_code} EQUAL 1)
        message(FATAL_ERROR "Failed pushing changes to remote MBED repository '" ${mbed_repo_name} "' (error code " ${result_code} ")")
    endif()
endfunction(pushMBEDRepository)


function(compileMBEDProject)
    set(mbed_repo_address http://os.mbed.com/users/$ENV{MBED_USER}/code/${mbed_repo_name}/)

    file(GLOB expanded_compile_mbed_tool ${compile_mbed_tool})
    if (DEFINED mbed_output_bin_path AND NOT ${mbed_output_bin_path} EQUAL "")
        execute_process(COMMAND ${expanded_compile_mbed_tool} -un $ENV{MBED_USER} -pwd $ENV{MBED_PWD} -r ${mbed_repo_address} -plat ${mbed_platform} -o ${mbed_output_bin_path} WORKING_DIRECTORY . RESULT_VARIABLE result_code)
    else()
        execute_process(COMMAND ${expanded_compile_mbed_tool} -un $ENV{MBED_USER} -pwd $ENV{MBED_PWD} -r ${mbed_repo_address} -plat ${mbed_platform} WORKING_DIRECTORY . RESULT_VARIABLE result_code)
    endif()

    if(NOT ${result_code} EQUAL 0)
        message(FATAL_ERROR "Failed compiling MBED repository '" ${mbed_repo_name} "' (error code " ${result_code} ")")
    endif()
endfunction(compileMBEDProject)

function(releaseMBEDRepository)
    verifyRequiredParameters()
    verifyMercurialIsInstalled()
    cloneMBEDRepository()
    updateMBEDLibraries()
    updateMBEDSourceFiles()
    pushMBEDRepository()
endfunction(releaseMBEDRepository)

function(compileMBEDRepository)
    releaseMBEDRepository()
    compileMBEDProject()
endfunction(compileMBEDRepository)

###
### The compilation environment had the normal project base name with an appended _bld  They will be constantly overwritten.
if (release_the_project)
        set(mbed_repo_name ${mbed_project_base} CACHE STRING "What will be cloned")
    else()
        set(mbed_repo_name ${mbed_project_base}_bld CACHE STRING "What will be cloned")
endif()

set(local_repo_path ${CMAKE_CURRENT_BINARY_DIR}/${mbed_repo_name})
set(local_sublib_dir ${CMAKE_CURRENT_BINARY_DIR}/temp)
set(compile_mbed_tool ${shared_util_base_path}/tools/compilembed/bin/release/compilembed.exe)

set(mbed_platform "FRDM-K64F")

# seperate out the file lists so that they can used by the release cmake
include (${mbed_project_base}_filelist.txt)

### Main
if(WIN32)
    if (${release_the_project})
        releaseMBEDRepository()
    else ()
        compileMBEDRepository()
    endif()
else()
    message(FATAL_ERROR "Apologies, but the only currently supported platform is Microsoft Windows.")
endif()
