#!/usr/bin/env python

# s3edgex : a command line to access stores that use the s3 protocol 
#        e.g. AWS S3, NexentaEdge S3, Minio S3 etc 

import platform
import getopt
import sys
import pprint
import os
from os.path import expanduser
import re
import simplejson as json

import edgex_access

DEFAULT_CONFIG = "/.s3edgex"
S3EA_LOG="s3edgex.log"


sample_config = ' \
{ \
	"stores" : [ \
	{ \
		"NAME" : "edgex", \
		"STORE_TYPE" :"S3", \
		"ACCESS" : "", \
		"SECRET" : "", \
		"REGION" : "", \
		"ENDPOINT" : "https://edge.nexenta.com", \
		"TOKEN" : "", \
		"SSL" : "False", \
		"BUCKET" : "", \
		"TAG" : "edgex" \
	}, \
	{ \
		"NAME" : "ix", \
		"STORE_TYPE" :"FS", \
		"TOKEN" : "", \
		"BUCKET" : "/Users/sample.user/Workspace", \
		"TAG" : "ix"  \
	} \
	], \
	"PRIMARY" : "edgex", \
	"SYNCIO" : "QUEUED", \
	"DEBUG" : 5 \
} \
'



def s3edgex_test(edgex_cfg, test_name=""):
    if (test_name == "syncio"):
        print("Not Yet!!")
    elif (test_name == "basic"):
        print("Not Yet!!")
    else:
        print("Unknown test case: " + test_name)

def s3edgex_store(edgex_cfg, args):
    if args is None:
        stores = edgex_cfg.get_stores()
        primary_store = edgex_cfg.get_primary_store()
        for st in stores:
            store = edgex_cfg.get_store(st)
            if (st == primary_store.get_name()):
                print(st + "\t" + store.get_type() + "\t" + "PRIMARY")
            else:
                print(st + "\t" + store.get_type())
    else:
        subcmd = args[0]
        if (subcmd == "list"):
            stores = edgex_cfg.get_stores()
            primary_store = edgex_cfg.get_primary_store()
            for st in stores:
                store = edgex_cfg.get_store(st)
                if (st == primary_store.get_name()):
                    print(st + "\t" + store.get_type() + "\t" + "PRIMARY")
                else:
                    print(st + "\t" + store.get_type())
        elif (subcmd == "add"):
            print("Not Yet!")
        elif (subcmd == "del"):
            print("Not Yet!")
        else:
            print("Unknown command: " + subcmd)

def s3edgex_setup(args):
    if args is None:
        print("setup create | show ")
    else:
        subcmd = args[0]
        cfg_file = expanduser("~") + DEFAULT_CONFIG 
        if (subcmd == "create"):
            if (os.path.exists(cfg_file)):
                print(cfg_file + " already exists")
                return
            dot_config = json.loads(sample_config)
            with open(cfg_file, 'a+') as f:
                json.dump(dot_config, f)
        elif (subcmd == "show"):
            if (os.path.exists(cfg_file)):
                with open(cfg_file, 'r') as f:
                    dot_config = json.load(f)
                pprint.pprint(json.dumps(dot_config, indent=4))
        else:
            print("Unknown command: " + subcmd)

def list_recursive(cfg, dest_obj, local_obj, dest_base):
    if dest_obj.isfolder:
        edgex_op = edgex_access.edgex_operation('list')
        lso = edgex_op.list(dest_obj)
        for f in lso:
            if not local_obj:
                next_name = dest_base + f
            else:
                next_name = dest_obj.basename() + f
            dest_r_obj = edgex_access.edgex_object(cfg, next_name, dest_obj.get_store(), local_pwd=local_obj)
            if (f.endswith("/")):
                print("" + f)
                list_recursive(cfg, dest_r_obj, local_obj, dest_base)
            else:
                if local_obj:
                    print("\t" + f)
                else:
                    print("" + f)
    else:
        print(dest_obj.pathname())

def put_recursive(cfg, source_obj, dest_obj, local_obj, source_base):
    if source_obj.isfolder:
        edgex_op = edgex_access.edgex_operation('list')
        lso = edgex_op.list(source_obj)
        for f in lso:
            dest_next = dest_obj.basename() + f
            if not local_obj:
                source_next = source_obj.basename() + f
            else:
                # TODO: 
                # source_next = source_base + f
                source_next = source_obj.objname() + f
            put_s_obj = edgex_access.edgex_object(cfg, source_next, store=source_obj.get_store(), local_pwd=local_obj)
            put_d_obj = edgex_access.edgex_object(cfg, dest_next, store=dest_obj.get_store())
            if f.endswith("/"):
                put_recursive(cfg, put_s_obj, put_d_obj, local_obj, dest_base)
            else:
                edgex_op_write = edgex_access.edgex_operation('put')
                edgex_op_read = edgex_access.edgex_operation('get')
                try:
                    edgex_op_write.put(put_d_obj, edgex_op_read.get(put_s_obj))
                except:
                    pass
        print("")

def get_recursive(cfg, source_obj, dest_obj, local_obj, dest_base):
    
    if source_obj.isfolder:
        edgex_op = edgex_access.edgex_operation('list')
        lso = edgex_op.list(source_obj)

        for f in lso:
            source_next = source_obj.basename() + f
            if not local_obj:
                dest_next = dest_obj.basename() + f
            else:
                dest_next = dest_base + f

            get_s_obj = edgex_access.edgex_object(cfg, source_next, store=source_obj.get_store())
            get_d_obj = edgex_access.edgex_object(cfg, dest_next, store=dest_obj.get_store(), local_pwd=local_obj)
            
            if f.endswith("/"):
                get_recursive(cfg, get_s_obj, get_d_obj, local_obj, dest_base)
            else:
                edgex_op_write = edgex_access.edgex_operation('put')
                edgex_op_read = edgex_access.edgex_operation('get')
                try:
                    edgex_op_write.put(get_d_obj, edgex_op_read.get(get_s_obj))
                except:
                    pass
        print("")

def remove_recursive(cfg, dest_obj, local_obj, dest_base):
    if dest_obj.isfolder:
        edgex_op = edgex_access.edgex_operation('list')
        lso = edgex_op.list(dest_obj)
        for f in lso:
            if not local_obj:
                next_name = dest_base + f
            else:
                next_name = dest_obj.objname() + f

            dest_r_obj = edgex_access.edgex_object(cfg, next_name, dest_obj.get_store(), local_pwd=local_obj)
            
            if (f.endswith("/")):
                remove_recursive(cfg, dest_r_obj, local_obj, dest_base)
            else:
                try:
                    edgex_op = edgex_access.edgex_operation('delete')
                    edgex_op.remove(dest_r_obj)
                except:
                    pass

            try:
                edgex_op = edgex_access.edgex_operation('delete')
                edgex_op.remove(dest_obj)
            except:
                pass

        print("")


def process_command(command, elog, args=None):
    cfg_file = expanduser("~") + DEFAULT_CONFIG 
    if (command == "setup"):
        s3edgex_setup(args)
        return

    edgex_cfg = edgex_access.edgex_config()
    try:
        edgex_cfg.load_file(cfg_file)
    except:
        elog.log_error(" Error loading " + cfg_file  + " config file")
        return
    primary_store = edgex_cfg.get_primary_store()
    if (command == "list"):
        edgex_store = primary_store
        if not args:
            edgex_store = primary_store
            name, items = edgex_store.list_buckets()
            print(name)
            for it in items:
                print("\t" + it)
            return
        recursive = False
        local_obj = False
        if (args[0] == "-r"):
            recursive = True
        elif (args[0] == "-l") :
            local_obj = True
        else:
            recursive = False
            local_obj = False
        if args:
            if (len(args) > 1):
                if (args[1] == "-l"):
                    local_obj = True
            if recursive and not local_obj:
                objname = args[1]
            elif local_obj and not recursive:
                objname = args[1]
            elif recursive and local_obj:
                objname = args[2]
            else: # not recursive and not local_obj
                objname = args[0]
            
            try:
                edgex_obj = edgex_access.edgex_object(edgex_cfg, objname, local_pwd=local_obj)

                if recursive:
                    list_recursive(edgex_cfg, edgex_obj, localObj, edgex_obj.basename())
                else:
                    edgex_op = edgex_access.edgex_operation('list')
                    items = edgex_op.list(edgex_obj)
                    print(objname)
                    for it in items:
                        print("\t" + it)
            except Exception as e:
                print(str(e))
                return
    elif (command == "store"):
        s3edgex_store(edgex_cfg, args)
    elif (command == "syncio"):
        s3edgex_syncio(edgex_cfg, args)
    elif (command == "lsb"):
        edgex_store = primary_store
        try:
            name, items = edgex_store.list_buckets()
        except:
            return
        print(name)
        for it in items:
            print("\t" + it)
    elif (command == "exists"):
        if (args[0] == "-l"):
            local_obj = True
            oname = args[1]
        else:
            local_obj = False
            oname = args[0]

        edgex_obj = edgex_access.edgex_object(edgex_cfg, oname, local_pwd=local_obj)
        edgex_op = edgex_access.edgex_operation('exists')
        try:
            f = edgex_op.exists(edgex_obj)
        except:
            return
        print(oname + " : " + str(f))
    elif (command == "info"):
        if (args[0] == "-l"):
            local_obj = True
            oname = args[1]
        else:
            local_obj = False
            oname = args[0]

        edgex_obj = edgex_access.edgex_object(edgex_cfg, oname, local_pwd=local_obj)
        edgex_op = edgex_access.edgex_operation('meta')
        try:
            f = edgex_op.meta(edgex_obj)
        except:
            return
        pprint.pprint(str(f))

    elif (command == "put"):
        recursive = False
        local_obj = False
        if (args[0] == "-r"):
            recursive = True
        elif (args[0] == "-l") :
            local_obj = True
        else:
            recursive = False
            local_obj = False
        if args:
            if (len(args) > 1):
                if (args[1] == "-l"):
                    local_obj = True
            if recursive and not local_obj:
                dest_objname = args[1]
                source_objname = args[2]
            elif local_obj and not recursive:
                dest_objname = args[1]
                source_objname = args[2]
            elif recursive and local_obj:
                dest_objname = args[2]
                source_objname = args[3]
            else: # not recursive and not localObj
                dest_objname = args[0]
                source_objname = args[1]
            try:
                source_obj = edgex_access.edgex_object(edgex_cfg, source_objname, local_pwd=local_obj)
                dest_obj = edgex_access.edgex_object(edgex_cfg, dest_objname)
                if recursive:
                    put_recursive(edgex_cfg, source_obj, dest_obj, local_obj, dest_obj.objname())
                else:
                    edgex_op_write = edgex_access.edgex_operation('put')
                    edgex_op_read = edgex_access.edgex_operation('get')

                    edgex_op_write.put(dest_obj, edgex_op_read.get(source_obj))

            except Exception as e:
                print(str(e))
                return

    elif (command == "get"):

        recursive = False
        local_obj = False
        if (args[0] == "-r"):
            recursive = True
        elif (args[0] == "-l") :
            local_obj = True
        else:
            recursive = False
            local_obj = False
        if args:
            if (len(args) > 1):
                if (args[1] == "-l"):
                    local_obj = True
            if recursive and not local_obj:
                source_objname = args[1]
                dest_objname = args[2]
            elif local_obj and not recursive:
                source_objname = args[1]
                dest_objname = args[2]
            elif recursive and local_obj:
                source_objname = args[2]
                dest_objname = args[3]
            else: # not recursive and not localObj
                source_objname = args[0]
                dest_objname = args[1]

            try:
                source_obj = edgex_access.edgex_object(edgex_cfg, source_objname)
                dest_obj = edgex_access.edgex_object(edgex_cfg, dest_objname, local_pwd=local_obj)
                dest_obj.stat(create=True)

                if recursive:
                    get_recursive(edgex_cfg, source_obj, dest_obj, local_obj, dest_obj.objname())
                else:
                    edgex_op_write = edgex_access.edgex_operation('put')
                    edgex_op_read = edgex_access.edgex_operation('get')

                    edgex_op_write.put(dest_obj, edgex_op_read.get(source_obj))

            except Exception as e:
                print(str(e))
                return

    elif (command == "del"):

        recursive = False
        local_obj = False
        if (args[0] == "-r"):
            recursive = True
        elif (args[0] == "-l") :
            local_obj = True
        else:
            recursive = False
            local_obj = False

        if args:
            if (len(args) > 1):
                if (args[1] == "-l"):
                    local_obj = True
            if recursive and not local_obj:
                del_objname = args[1]
            elif local_obj and not recursive:
                del_objname = args[1]
            elif recursive and local_obj:
                del_objname = args[2]
            else: # not recursive and not localObj
                del_objname = args[0]
            try:
                del_obj = edgex_access.edgex_object(edgex_cfg, del_objname, local_pwd=local_obj)
                if recursive:
                    remove_recursive(edgex_cfg, del_obj, local_obj, del_obj.objname())
                else:
                    edgex_op = edgex_access.edgex_operation('delete')
                    edgex_op.remove(del_obj)
            except Exception as e:
                print(str(e))
                return

    elif (command == "test"):
        if args:
            s3edgex_test(edgex_cfg, testName=args[0])
        else:
            s3edgex_test(edgex_cfg, testName="basic")
    else:
        elog.log_error("Unknown command: " + command)

def system_info(debug_level):
    print("python \t\t: " + platform.python_version() + " " + platform.python_implementation() + " " + str(platform.python_build()))
    print("platform \t: " + platform.node() + " " + platform.system() + " " + platform.machine() + " " + platform.release())
    print("uname \t\t: " + platform.uname().version)
    print("debug_level \t: " + str(debug_level))

def usage():
    print(sys.argv[0] + " --help")
    print(sys.argv[0] + " --system")
    print(sys.argv[0] + " [ --debug <level> ] <command> <objname> <arg>")
    print("Commands:")
    print("\t\tsetup")
    print("\t\tstore")
    print("\t\tlist")
    print("\t\texists")
    print("\t\tput")
    print("\t\tget")
    print("\t\tdel")
    print("\t\tinfo")
    print("\t\ttest")
    print("Examples:")
    print("\t% " + sys.argv[0] + " [ --debug <level> ] setup [ create show ]")
    print("\t% " + sys.argv[0] + " [ --debug <level> ] store [ list add del ]")
    print("\t% " + sys.argv[0] + " [ --debug <level> ] list [ -r ]")
    print("\t% " + sys.argv[0] + " [ --debug <level> ] list [ -r ] <bucketname>")
    print("\t% " + sys.argv[0] + " get <bucketname/filename> <filename>")
    print("\t% " + sys.argv[0] + " get [ -r ] <bucketname/dirname> <dirname>")
    print("\t% " + sys.argv[0] + " put <bucketname/filename> <filename>")
    print("\t% " + sys.argv[0] + " put [ -r ] <bucketname/dirname> <dirname>")
    print("\t% " + sys.argv[0] + " del <bucketname/filename>")
    print("\t% " + sys.argv[0] + " del [ -r ] <bucketname/dirname>")
    print("\t% " + sys.argv[0] + " info <bucketname/filename>")
    print("\t% " + sys.argv[0] + " exists <bucketname/filename>")

def main():
    if sys.version_info[0] < 3:
        raise "Must be using Python 3 "
    debug_level = 4
    try:
        opts, remainder = getopt.getopt(sys.argv[1:], "hd:s", ["help", "debug", "system"])
    except getopt.GetoptError:
        usage()
        sys.exit(2)
    for o,a in opts:
        if o in ("-h", "--help"):
            usage()
            sys.exit(2)
        if o in ("-d", "--debug"):
            debug_level = int(a)
        if o in ("-s", "--system"):
            system_info(debug_level)
            sys.exit(0)
    if (len(remainder) < 1):
        usage()
        sys.exit(2)
 
    elog = edgex_access.edgex_logger(debug_level, S3EA_LOG) 
    elog.log_info(sys.argv[0] + " started")

    command = remainder[0]
    if len(remainder[1:]) >= 1:
        process_command(command, elog, remainder[1:])
    else:
        process_command(command, elog)

    elog.log_info(sys.argv[0] + " ended")

if __name__ == '__main__':
    main()
