#!/usr/bin/env python
import sys
import os
from os import path
import re
import tarfile
import logging
import time
import random
import subprocess

from dirwalker.dirwalker import file_lister
import config_handler
import pash
import termcolor
import menu_generator

# a function stolen from the internet to handle human-readable file sizes
def sizeof_fmt(num, suffix='B'):
    for unit in ['','Ki','Mi','Gi','Ti','Pi','Ei','Zi']:
        if abs(num) < 1024.0:
            return "%3.1f%s%s" % (num, unit, suffix)
        num /= 1024.0
    return "%.1f%s%s" % (num, 'Yi', suffix)

# decide where the configuration file should reside and look for it 
#file_name = os.path.abspath(sys.argv[0])
#dir_path = os.path.dirname(file_name)
config_path = '/etc/cybak.cfg'
if os.path.exists(config_path) != True:
    print "No configuration file found. There should be a file"\
            " named \'cybak.cfg\' in the project's folder. Copy this"\
            " file to \'/etc/cybak.cfg\'." 
    print "Using defaults for this run."
else:
    config = config_handler.Parser(filename=config_path)


# a function to build lists of exceptions from a setting
def build_exceptions(except_option):
    try:
        except_list = config.get_setting('EXCEPT',
                        except_option).split(',')
        if except_list[-1] == ['']:
            except_list.pop()
    except (AttributeError, NameError):
        except_list = []
    return except_list

# build the file and directory exception lists 
dir_exceptions = build_exceptions('dir_except')
file_exceptions = build_exceptions('file_except')

# a setting builder
def build_setting(section, option, default):
    setting = default
    try:
        if config.get_setting(section, option) != None:
            setting = config.get_setting(section, option)
    except NameError:
        pass
    return setting

# set the destination directory
dest_dir = build_setting('DIR','dest_dir','/backup')
# set the source directory
source_dir = build_setting('DIR','src_dir', os.environ['HOME'])
#set the backup file name
file_handle = build_setting('NAME','filename','MyBackup')


timestamp = time.strftime('%Y%m%d.%H%M')
file_name = (file_handle + "-" + timestamp
         + '.' + str(random.randint(100,999)))

# make backup directory if it does not already exist
if not os.path.exists(dest_dir):
	print "Creating \'", dest_dir,"\' as a destination for backups."
	os.makedirs(dest_dir)
	assert os.path.exists(dest_dir), "We tried to make a directory, but couldn't find it afterwards."
else:
	print "Found destination directory:", dest_dir

# begin printing exception-related messages
print ""

# set ignore_hidden
ignore_hidden = False
try:
    ignore_hidden_str = config.get_setting('EXCEPT','ignore_hidden')
    if ignore_hidden_str == "True" or ignore_hidden_str == "true":
        print "Ignoring hidden files and folders..."
        ignore_hidden = True
except NameError:
    pass    

# print files and directories that will be ignored
print "Files that will be ignored:"
for exc in file_exceptions:
    print " "*4, exc
print "Directories that will be ignored:"
for exc in dir_exceptions:
    print " "*4, exc

print("Building file list and reading file sizes...")
# build list of files
total_list = file_lister(source_dir, ignore_hidden=True, file_exceptions=file_exceptions, dir_exceptions=dir_exceptions)
# read file sizes
proc = pash.ShellProc()
total_size = 0
num_patt = re.compile("^[0-9]*")
for fil in total_list:
    du_out = subprocess.Popen(["du","-b",fil],
        stdout=subprocess.PIPE)
    line_val = du_out.communicate()[0]
    total_size = total_size + int(num_patt.match(line_val).group())


# check filesize for consent from user
print("The total size of the files to be backed up is: "),
termcolor.cprint(sizeof_fmt(total_size), 'cyan')
print("The compressed backup will likely be smaller than this.\n")
# if 'auto' was passed to the script, do not ask for user confirmation
result = False
if len(sys.argv) > 1:
    if sys.argv[1] == 'auto':
        result = True
else:
    yn = menu_generator.YN_Menu(default="no")
    result = yn.run()
if result == False:
    print("Operation cancelled, exiting.")
    sys.exit(1)

# create a tarfile object that is opened for appending,
# then recursively append each target to it
tar = tarfile.open(name=dest_dir+file_name+'.tar',mode='w')
for i in total_list:
	tar.add(name=i,recursive=True)
tar.close()
handle = dest_dir +  file_name + ".tar"

print("\nApplying lzma compression... (this may take several minutes)")

command = 'lrzip ' + handle
proc.run(command)
data = proc.get_val('stdout').rstrip('\n').split('\n')

print("Compression complete.")

# waits for the backup file to exist, then removes the tar
print ("Ensuring the backup file exists..."),
while os.path.exists(handle+".lrz") == False:
	pass
print("found it!\n")

print data[0]

command = "du -sh " + data[0].split(':')[1].lstrip(' ') + "| column -t | cut -d ' ' -f 1"
proc.run(command)
sys.stdout.write("Compressed backup size: ") 
termcolor.cprint(proc.get_val('stdout'), 'cyan')


for index, i in enumerate(data[1].split(' ')):
	if i == 'Compression' and data[1].split(' ')[index+1] == 'Ratio:':
		comp_ratio = data[1].split(' ')[index+2].rstrip('.\n')

sys.stdout.write("Compression ratio: ") 
termcolor.cprint(comp_ratio, 'cyan')


print("\nDeleting the uncompressed tarball.")
os.remove(handle)

print("Backup complete. Have a nice day.")


