--#######################################################################################################################################--
--Note:This script is evoked/started/utilised by start_bak.lua file
--#######################################################################################################################################--
local lfs = require("lfs") 
--local localbackup_directory = "/etc/rsm/backup/local_backup/" 
local localbackup_directory = "/home/staging/"
local config_file = "/etc/rsm/backup.conf"
local backup_conf = "/etc/rsm/backup/"
local destination_ip,user,service_directory,destination_directory = dofile(config_file)
local mac = ""
local site_label = ""
local mac_dir = ""
local mac_des = ""
local remote_home = ""
local restoration_directory =  destination_directory
local project_name_check="default"
local mac_state = false
local mkdir_path = ""
local mkdir_path_assemble = ""
local ite = 1
local nodelist = {}
local refined_nodelist = {}
local obj = {}
local name_arg = nil
local list_var = "default"
local set_restore_mac = ""

function check_connection()
  assert(os.execute("rsync --list-only "..user.."@"..destination_ip..":"..destination_directory))
end
function err_connection()
  print("Error:Backup Host connection fail")
  print("Check remote connection or backup config details")
  os.exit(0)
end

function init ()
  xpcall(check_connection,err_connection)
  
  print("Remote connection check succesful")
end

function check_dir_exists(path)
   local dir2check = string.match(path,'/.-/')
   
   if dir2check==nil then
    print("dir2check is nil")
    return true,nil,dir2check      
   end

   --Append the current dir to the path for building the dir tree
   mkdir_path = mkdir_path..dir2check
   mkdir_path = string.gsub(mkdir_path,"//","/") 
   local dir_attr = lfs.attributes(mkdir_path)
   print("mkdir_path "..mkdir_path)
   if dir_attr.mode=="directory" then
    local next_dir_path = string.gsub(path,dir2check,"/")
    return true,next_dir_path,dir2check
   elseif dir2check==nil then
    print("dir2check is not a directory")
    return false,nil,dir2check
    else
    print("End of path string")
    return true,nil,dir2check
   end
end

function backitup(baklist)
--Make staging directory
lfs.mkdir(localbackup_directory)
--set backup directory path
mac_dir = localbackup_directory..mac.."/"
--make mac directory in local backup
lfs.mkdir(mac_dir)

if name_arg==nil then
  mac_des = mac_dir.."default/"
  remote_backup_session_home = destination_directory..mac.."/default/"
else
  mac_des = mac_dir..name_arg.."/"
  remote_backup_session_home = destination_directory..mac.."/"..name_arg.."/"
end

lfs.mkdir(mac_des)
print("Backup Name directory: "..mac_des)

  for key,bak in ipairs(baklist)do
    local response,new_path,dir2make = check_dir_exists(bak)
    ::Check_again::
    if (response == true and new_path == nil and dir2make == nil) then
      print("Directory tree is built")
      --start new path
      mkdir_path=""
    elseif (response==true and new_path~=nil and dir2make~=nil) then
      
      --To append to the directory tree 
      mkdir_path_assemble = mkdir_path_assemble..dir2make
      mkdir_path_assemble = string.gsub(mkdir_path_assemble,"//","/")
      dir2make_path = mac_des..mkdir_path_assemble
      dir2make_path = string.gsub(dir2make_path,"//","/")
      
      lfs.mkdir(dir2make_path)
      
      response,new_path,dir2make = check_dir_exists(new_path)
      goto Check_again-- check_again is inside this "for key,bak in ipairs(baklist)do" loop up top
    else
      lfs.mkdir(dir2make_path)
      --Directory tree done. Start new path
      mkdir_path = ""
    end
    --We have to make the directory tree folder by folder and then send the newly made folder on the server
    os.execute("rsync -av "..bak.." "..dir2make_path)
    --Star the assemble path from scratch after send to server
    mkdir_path_assemble = ""
    --append the newly created (and on growing) directory path and tree to the manifest.lua file
    local manifest_file = io.open(mac_des.."manifest.lua","a")
    
    --Making the table on the manifest file for the particular site. This only runs once per instance/backup
    if mac_state==false then
     manifest_file:write("local mac"..mac.." = {}\nmac"..mac..".site_label = "..site_label.."\n")
     mac_state=true
    end

    --Seperate mac addr tables into the different projects
    if (project_name_check~=project_name and project_name_check~="default") then
      project_name = project_name:gsub(".lua","")
      
      --Open bracket for the new project table
      manifest_file:write("last\"}\nmac"..mac.."."..project_name.." = {\"")
      --Write first iteration of the backupglob.If you dont that info is lost
      manifest_file:write(bak.."\",\"")
      --Updates the project_name_check value to check for any project name changes
      project_name_check=project_name
    elseif (project_name_check~=project_name) then
      --Open bracket for the new project table
      project_name = project_name:gsub(".lua","")
      --Write first iteration of the backupglob.If you dont that info is lost
      manifest_file:write("mac"..mac.."."..project_name.." = {\"")
      manifest_file:write(bak.."\",\"")
      --Updates the project_name_check value to check for any project name changes
      project_name_check=project_name
    else
      --Appends globs to the specified project name
      manifest_file:write(bak.."\",\"")
    end
    manifest_file:close()
  end

  --if manifest file is not excluded unfinished bits of it are sent everytime. So rather send the final manifest at the end
  os.execute("rsync -av --exclude 'manifest.lua' -e ssh "..localbackup_directory.." "..user.."@"..destination_ip..":"..destination_directory)
end
-- We'll need this to determine if a file is a lua script.
function getFileExtension(file)
  return file:match("^.+(%..+)$")
end

function backup_err()
   print ("Error occured in backitup(): Backup not complete.")
   print("Warning: Existing backup data my be corrupt or partially overriden by current backup attempt")
   os.execute("rm -r "..mac_dir)
   os.exit(0)
end

function send_metadata()
   --Sends site_meta to backup location
   os.execute("rsync -av -e ssh "..mac_dir.."metadata "..user.."@"..destination_ip..":"..destination_directory..mac)     
   --Sends the completed manifest file
   os.execute("rsync -av -e ssh "..mac_des.."manifest.lua "..user.."@"..destination_ip..":"..remote_backup_session_home)
   --Backup script depedant file (Sends to server)
   os.execute("rsync -av -e ssh "..backup_conf.."backup.conf "..user.."@"..destination_ip..":"..remote_backup_session_home..backup_conf)
   print("Metadata send: Succesful")

end
-- Checks if a file is a lua script and loads it. Then does project
-- related stuff to it.
function handleFile()

local backuplist = {}
local serviceModule 

   for key, file in ipairs(refined_nodelist)do
     --project_name is a global variable and it is used in the backitup function
     project_name = file
     serviceModule = dofile(service_directory..file)
     for idx, glob in ipairs(serviceModule.backup_globs) do
       backuplist[idx]=glob  
     end
     serviceModule.pre_backup()
     local error_check = pcall(backitup,backuplist)
     if error_check==false then
     backup_err()
     end
     serviceModule.post_backup()
   end
   --make site metadata
   local site_meta = io.open(mac_dir.."metadata","w")
   site_meta:write(site_label.."\n")
   site_meta:close()
   xpcall(send_metadata,backup_err)
end

-- Just a helper function to only handle files.
function handleNode()
   
for key,node in ipairs(nodelist)do
   local attr = lfs.attributes(service_directory..node)
   assert (type(attr) == "table","Error in function handleNode(): Incorrect file type")
   local extension = getFileExtension(node)
   if (attr.mode == "file") and  (extension == ".lua") then
      refined_nodelist[key] = node
   else
      print("##########################Error#in#handle(node)##############################")
      print("Error: "..node.." is a "..attr.mode.."\nor/and ")
      print(node.." is not a lua file\nor/and file does does not exit")
      print("#############################################################################")
      
   end
end
  if next(refined_nodelist,nil)~=nil then
   handleFile()
   else
   print("Error handleNode(): refined_nodelist is nil ")
  end
end
--Restore helper functions
function General_pre_install()
  print("::::::::::::::::::::::::::::::::::::::::::::::::::::")
  print("General pre install (Just stop all applications)")
  print("::::::::::::::::::::::::::::::::::::::::::::::::::::")
end
function General_post_install()
  print("::::::::::::::::::::::::::::::::::::::::::::::::::::")
  print("General post install")
  print("::::::::::::::::::::::::::::::::::::::::::::::::::::")
end

function get_dev_mac()
  local pipe = assert(io.popen 'rsmc GET: TAG_SITE_CFG',"Error in get_dev_mac(): rsmc return nil")
   for line in pipe:lines() do
    if string.match(line,'GET')=="GET" then
      --Getting site ID
      local str_filter = string.match(line,'TAG_SITE_ID=".-"')
      str_filter = string.gsub(str_filter,":","")
      str_filter = string.gsub(str_filter,"\"","")
      str_filter = string.gsub(str_filter,"TAG_SITE_ID=","")
      mac = str_filter
      
      --Getting site label
      str_filter = string.match(line,'TAG_SITE_LABEL=".-"')
      str_filter = string.gsub(str_filter,"TAG_SITE_LABEL=","")
      str_filter = string.gsub(str_filter,"\"","")
      site_label = str_filter
    
    end
   end
end

--#######################################################################################################################################--
---------------------------------------------------------Function: main_backup------------------------------------------------------------
--#######################################################################################################################################--
--Description:Backup function
function obj.main_backup(dir_name)
  init()
  --Get site number from the device
  get_dev_mac()
  print("Backup mac: "..mac)
  if dir_name~=nil then
    name_arg=dir_name
  end

-- Iterate through all filesystem nodes in a directory(All project config files).
for node in lfs.dir(service_directory) do
   
   if node ~= "." and node ~= ".." then    
   nodelist[ite] = node
   ite=ite+1   
   end
end
--Error Handling:Check if nodelist is empty or not
if next(nodelist,nil)~=nil then
handleNode()
--Delete the local staging directory
print("Deleting...: "..localbackup_directory.." locally")
os.execute("rm -r "..localbackup_directory)


--Remove all the mac directories locally
 --for mac_num in lfs.dir(localbackup_directory) do
   --if mac_num ~= "." and mac_num ~= ".." then
   --print("Deleting...: "..localbackup_directory..mac_num.." locally")
   --os.execute("rm -r "..localbackup_directory..mac_num)
   --end
 --end
else
print("Error: Files to backup not found")
end
end

function restore_files_default(restore_name)
  os.execute("rsync -av -e ssh "..user.."@"..destination_ip..":"..destination_directory..set_restore_mac.."/"..restore_name.."/ /") 
end

function restore_files(restore_name,exclude_files)
  os.execute("rsync -av "..exclude_files.." -e ssh "..user.."@"..destination_ip..":"..destination_directory..set_restore_mac.."/"..restore_name.."/ /")
  print("excludes: "..exclude_files)
end

function restore_fail()
  os.execute("rm -r "..restoration_directory)
  print("Restoration fail!")
  print("Note: System data may be corrupt due to possible partial restoration")
  os.exit(0)
end

function edit_manifest()
  print("Edit Manifest.lua ")
  --restore_globs
  print ("restoration dir: "..restoration_directory)
  local restore_globs = io.open(restoration_directory.."manifest.lua","a")
  --write the return var in the manifest.lua file (COMPLETES THE MANIFEST.LUA FILE)
  restore_globs:write("last\"}\nreturn mac"..set_restore_mac.."\n")
  restore_globs:close()
  print("Manifest.lua edit: Successful")
end
--#######################################################################################################################################--
---------------------------------------------------------Function: main_restore------------------------------------------------------------
--#######################################################################################################################################--
--Description:Restore backup function
function obj.main_restore(restore_mac,name_param)
init()
--Restore 
local requested_service =""
local restore_name = ""
--if no restore_mac arg is given then by default the device's own mac is used to restore its own previous backup
if restore_mac==nil then
  get_dev_mac()
  set_restore_mac =mac 
else
  set_restore_mac = restore_mac
end

  if name_param==nil then
    restore_name = "default"
  else
    restore_name = name_param
  end
print("restore_name: "..restore_name)
print("Restore mac: "..set_restore_mac)
--Download manifest file to find out what to restore
os.execute("rsync -av -e ssh "..user.."@"..destination_ip..":"..destination_directory..set_restore_mac.."/"..restore_name.."/manifest.lua "..restoration_directory)

xpcall(edit_manifest,restore_fail)

--Checks if the dir exist locally. if it does dont download it from the server
local search_file_attr = lfs.attributes(service_directory)

if search_file_attr~=nil then
  print ("Directory does exist")
  local exclude_files = ""
  local exclude_flag = "--exclude"
  --Makes sure that only .lua files that do not exist are the files that are downloaded for backup
  for conf_file in lfs.dir(service_directory) do
    if conf_file~=".." and conf_file~="." then
      --print ("service_d contents: "..conf_file)
      exclude_files = exclude_files..exclude_flag.." '"..conf_file.."' "
    end
  end  
  General_pre_install()
  --if .lua config file exist do not download them
  --os.execute("rsync -av "..exclude_files.." -e ssh "..user.."@"..destination_ip..":"..destination_directory..set_restore_mac.."/"..restore_name.."/ /")
  --print("excludes: "..exclude_files)
  local outcome = pcall(restore_files,restore_name,exclude_files)
  if outcome==false then
    restore_fail()
  end
else
print (".lua File DOES NOT exist!!!")
General_pre_install()
--os.execute("rsync -av -e ssh "..user.."@"..destination_ip..":"..destination_directory..set_restore_mac.."/"..restore_name.."/ /") 
local outcome = pcall(restore_files_default,restore_name)
  if outcome==false then
    restore_fail()
  end
end

for file in lfs.dir(service_directory) do
  if (file ~= "." and file ~= "..") then  
    service_file=dofile(service_directory..file)
    service_file.pre_restore()
    print("STARTUP "..file.." DAEMON!!!!!")
    service_file.post_restore()
  end
end
 print ("Removing manifest file")
 --This manifest.lua file comes inherently with the backup directory when restore
 os.execute("rm /manifest.lua")
 --This manifest.lua file is the one that is actually used
 os.execute ("rm -r "..restoration_directory)
General_post_install()

end

function rm_bakList()
  os.execute("rm bakList.lua")
  print("List Error: Couldnt list or display backed up sites")
  os.exit(0)
end

function acquire_list()
  local dir_check = true
  local bak_list = io.open("bakList.lua","w")
  local last_char = ""
  bak_list:close()
  bak_list = io.open("bakList.lua","r")
  os.execute("rsync --list-only "..user.."@"..destination_ip..":"..destination_directory.."/  >> bakList.lua")
  while (dir_check) do
  list_var = bak_list:read("*line")
 
    if list_var ~= nil then
      last_char = string.sub(list_var,-1)
      if last_char ~= "." and list_var ~= "default" then
        list_var =string.sub(list_var,-12)
        print("Site: "..list_var)

        --Get site label
        os.execute("rsync -av -e ssh "..user.."@"..destination_ip..":"..destination_directory..list_var.."/metadata .")
        local site_info = io.open("metadata","r")
        local site_data = site_info:read("*line")
        site_info:close()
        
        local display_list = io.open("DisplayList.lua","a")
        display_list:write("\n---------------------------------------------\n")
        display_list:write(list_var.."("..site_data..")/\n")
        display_list:write("---------------------------------------------\n")
        display_list:close()
        os.execute("rsync --list-only --exclude metadata "..user.."@"..destination_ip..":"..destination_directory..list_var.."/ >> DisplayList.lua")
        
      end
    else
      dir_check=false
    end    
  end
  bak_list:close()
end

--#######################################################################################################################################--
---------------------------------------------------------Function: backup_list------------------------------------------------------------
--#######################################################################################################################################--
--Description:View all backup sites on that are remotely backup
function obj.backup_list()
  init()
  xpcall(acquire_list,rm_bakList)

  --Display the file
  local display_list = io.open("DisplayList.lua","r")
  local view_folders = display_list:read("*all")
  print(view_folders)
  display_list:close()
  print("Deleting...: metadata and display files")
  os.execute("rm DisplayList.lua")
  os.execute("rm bakList.lua")  
  os.execute("rm metadata")
end

return obj
