#!/usr/bin/python
## -*- coding: utf-8 -*-
import io
import os
import sys
import logging
from optparse import OptionParser, Option, IndentedHelpFormatter
import json
import requests
from wcs.commons.putpolicy import PutPolicy

def output(message):
    sys.stdout.write(message + '\n')
    sys.stdout.flush()

def format_commands(progname, commands_list):
    help = "Commands:" + "\n"
    for cmd in commands_list:
        help += "   %s\n    	%s %s %s \n" % (cmd["label"], progname, cmd["cmd"], cmd["param"])
    return help

def get_commands_list():
    return [
        {"cmd":"list", "label":"List objects","param":"wcs://BUCKET RESFILE","func":cmd_list,"argc":2},
        {"cmd":"listbucket", "label":"List buckets","param":"","func":cmd_listbucket,"argc":0},
        {"cmd":"get","label":"Download file","param":"URL", "func":cmd_get,"argc":1},
        {"cmd":"del","label":"Delete a file", "param":"wcs://BUCKET/OBJECT","func":cmd_del, "argc":1},
        {"cmd":"mv","label":"Move a file from src bucket to des bucket", "param":"wcs://srcBUCKET/srcOBJECT wcs://dstBUCKET/desOBJECT","func":cmd_mv,"argc":2},
        {"cmd": "cp", "label": "Copy a file from src bucket to des bucket","param": "wcs://srcBUCKET/srcOBJECT wcs://dstBUCKET/desOBJECT", "func":cmd_cp,"argc": 2},
        {"cmd":"setdeadline", "label":"Set deadline of file","param":"wcs://BUCKET/OBJECT deadline","func":cmd_setdeadline,"argc":2},
        {"cmd":"stat","label":"Get file info","param":"wcs://BUCKET/OBJECT","func":cmd_stat, "argc":1},
        {"cmd":"put","label":"Upload a local file to WCS","param":"wcs://BUCKET/OBJECT LOCALFILE", "func":cmd_put,"argc":2},
        {"cmd":"multiput","label":"Multipart upload a local file to WCS","param":"wcs://BUCKET/OBJECT LOCALFILE ", "func":cmd_multiput,"argc":2},
        {"cmd":"deletePrefix","label":"Delete multiple files according to prefix","param":"wcs://BUCKET PREFIX","func":cmd_delprefix,"argc":2}
    ]

def cmd_list(args):
    cfg = Config()
    cli = Client(cfg)
    bucket = args[0].split("wcs://")[1]
    resfile = os.path.join(os.getcwd(),args[1])
    code, response, reqid = cli.bucket_list(bucket,cfg.prefix, cfg.marker, int(cfg.limit),int(cfg.mode))
    if code == 200:
        try:
            with open(resfile, 'w') as f:
                f.write(json.dumps(response))
        except IOError:
            debug("Save Fail")
            sys.exit()
        debug("Request result saved to %s" % (resfile))
        debug("Request ID is: %s" % (reqid['x-reqid']))
    else:
        error("Request fail, more detail please pay attention to reqid: %s " % (reqid['x-reqid']))
    
def cmd_get(args):
    url = args[0]
    key = './'+ url.split('/')[-1]
    try:
        res = requests.get(url)
        if res.status_code == 200:
            debug("Download suc.")
            try:
                with open(key,'wb') as f:
                    f.write(res.content)
            except IOError as e:
                error("Create local file fail for %s" % e)
                sys.exit()
    except:
        error("Download Fail,for %d, %s" (res.status_code, res.text))

def _simple_arg_parse(arg):
    tmp = arg.split("wcs://")[1]
    bucket = tmp.split('/')[0]
    object = tmp.split(bucket+'/')[1]
    return bucket, object
    
def cmd_del(args):
    cli = Client(Config())
    bucket, object = _simple_arg_parse(args[0])
    debug(cli.delete(bucket,object))

def _couple_args_parse(args):
    try:
        src = args[0].split("wcs://")[1]
        dst = args[1].split("wcs://")[1]
        srcbucket = src.split('/')[0]
        srcobject = src.split(srcbucket)[1]
        srcobject = srcobject.split('/')[1]
        dstbucket = dst.split('/')[0]
        dstobject = dst.split(dstbucket)[1]
        dstobject = dstobject.split('/')[1]
        return srcbucket, srcobject, dstbucket, dstobject
    except Exception:
        error("Invalid arguments")
        sys.exit()

def cmd_mv(args):
    cli = Client(Config())
    srcbk, srcob, dstbk, dstob = _couple_args_parse(args)
    debug(cli.move(srcbk, srcob, dstbk, dstob))


def cmd_cp(args):
    cli = Client(Config())
    srcbk, srcob, dstbk, dstob = _couple_args_parse(args)
    debug(cli.copy(srcbk, srcob, dstbk, dstob))

def cmd_setdeadline(args):
    cli = Client(Config())
    bucket, object = _simple_arg_parse(args[0])
    deadline = args[1]
    debug(cli.setdeadline(bucket,object,deadline))

def cmd_stat(args):
    cli = Client(Config())
    bucket, object = _simple_arg_parse(args[0])
    debug(cli.stat(bucket,object))

def cmd_put(args):
    cfg = Config()
    cli = Client(cfg)
    bucket, object = _simple_arg_parse(args[0])
    local = args[1]
    debug(cli.simple_upload(local, bucket,object))
        
def cmd_multiput(args):
    cfg = Config()
    cli = Client(cfg)
    bucket, object = _simple_arg_parse(args[0])
    local = args[1]
    debug(cli.multipart_upload(local, bucket, object, cfg.upload_id))

def cmd_listbucket(args):
    cli = Client(Config())
    debug(cli.list_buckets())

def cmd_delprefix(args):
    bucket = args[0].split("wcs://")[1]
    prefix = args[1]
    cli = Client(Config())
    fops = 'bucket/%s/prefix/%s' % (urlsafe_base64_encode(bucket), urlsafe_base64_encode(prefix))
    debug(cli.prefix_delete(fops))

def run_configure(config_file):
    if os.path.exists(config_file):
        cfg = Config(config_file)
    else:
        cfg = Config()
    options = [
        ("access_key","Access Key", "Access key and Secret key are your identifiers for WCS"),
        ("secret_key","Secret Key"),
        ("put_url","PUT_URL","Upload domain for WCS"),
        ("mgr_url","MGR_URL","Manager domain for WCS"),
        ("block_size","Block_Size","Mulpart Upload Block Size"),
        ("bput_size","Bput_Size","Mulpart Upload Bput Size"),
        ("connection_retries","Connection_Retires","Request Connection Retires"),
        ("connection_timeout","Connection_Timeout","Request Connection Timeout"),
        ("mkblk_retries","Mkblk_Retries","Multipart Upload Mkblk retries"),
        ("bput_retries", "Bput_Retries", "Multipart Upload Bput retries"),
        ("mkfile_retries","Mkfile_Retries","Multipart Upload Mkfile retries"),
        ("tmp_record_folder","Tmp_Record_Folder","Multipart Upload record folder"),
        ("concurrency","Concurrency","Multipart Upload Concurrency"),
        ("ishttps","IsHttps","Use https or not")
    ]
    try:
        while True:
            output(u"Enter new values or accept defaults in brackets with Enter")
            output(u"Refer to user manual for detailed description of all options")
            for option in options:
                prompt = option[1]
                try:
                   
                    val = getattr(cfg, option[0])
                    if type(val) is bool:
                        val = val and "Yes" or "No"
                    if val not in (None, ""):
                        prompt += " [%s]" % val
                except AttributeError:
                    pass

                if len(option) >= 3:
                    output(u"\n%s" % option[2])
                val = raw_input(prompt + ": ")
                if val != "":
                    if type(getattr(cfg, option[0])) is bool:
                        val = val.lower().startswith('y')
                    setattr(cfg, option[0], val)
            output(u"\nNew settings:")
            for option in options:
                output(u"  %s: %s" % (option[0], getattr(cfg, option[0])))
            val = raw_input("\nSave settings? [y/N] ")
            if val.lower().startswith("y"):
                break
            val = raw_input("Retry configuration? [Y/n] ")
            if val.lower().startswith("n"):
                raise EOFError()
        try:
            with io.open(config_file, 'w') as fp:
                cfg.dump_config(fp)
        except IOError as e:
            raise e
        output(u"Configuration saved to '%s'" % config_file)        

    except (EOFError, KeyboardInterrupt):
        output(u"\nConfiguration aborted. Changes were NOT saved.")
        return
    except IOError, e:
        error(u"Writing config file failed: %s: %s" % (config_file, e.strerror))
        sys.exit(EX_IOERR)

class MyHelpFormatter(IndentedHelpFormatter):
    def format_epilog(self, epilog):
        if epilog:
            return "\n" + epilog + "\n"
        else:
            return ""

def main():
    global cfg
    cfg = Config()
    commands_list = get_commands_list()
    commands = {}
    
    for cmd in commands_list:
        if 'cmd' in cmd:
            commands[cmd['cmd']] = cmd

    usage = 'Usage: wcscmd [options] COMMAND [parameters]'
    optparser = OptionParser(usage=usage, formatter=MyHelpFormatter())

    config_file = None
    from os.path import expanduser
    config_file = os.path.join(expanduser("~"), ".wcscfg")
    optparser.set_defaults(config = config_file)

    optparser.add_option(   "--configure", dest="run_config", action="store_true", help="Invoke interactive (re)configuration tool.")
    optparser.add_option("-c", "--config", dest="config", metavar="FILE", help="Config file name. Defaults to $HOME/.wcscfg")
    optparser.add_option(   "--dump-config",dest="dump_config", action="store_true", help="Dump current configuration after parsing config file and command line options and exit.")
    #optparser.add_option(   "--access_key", dest="access_key", help="WCS Access Key")
    #optparser.add_option(   "--secret_key", dest="secret_key", help="WCS Secret Key")
    optparser.add_option("-s", "--ssl", dest="ishttps", action="store_true", help="Use https connection when communicating with WCS")
    optparser.add_option(   "--upload-id", dest="upload_id", help="UploadId for Multipart Upload, in case you want continue an existing upload")
    optparser.add_option(   "--limit", dest="limit", help="Limit for list objects of bucket")
    optparser.add_option(   "--prefix", dest="prefix", help="Prefix for list objects of bucket")
    optparser.add_option(   "--mode", dest="mode", help="Mode for list objects of bucket")
    optparser.add_option(   "--marker", dest="marker", help="Start string for list")
    optparser.add_option(   "--relevance", dest="relevance", action="store_true", help="Whether modify deadline relevant .ts when modify deadline of m3u8 file" )
    optparser.add_option("-d", "--debug", dest="verbosity", action="store_const", const=logging.DEBUG, help="Enable debug output.")
    optparser.add_option(   "--output", dest="output", help="Save request result to specified key, like:<bucket>:<key>")
    optparser.add_option(   "--notify", dest="notifyURL", help="Notify url for request result")
    optparser.add_option(   "--separate", dest="separate", help="Whether separate notify, bool type")
    optparser.add_option(   "--overwrite", dest="overwirte", help="Whether overwrite existed key, bool type")
    optparser.add_option(   "--returnurl", dest="returl", help="Url for return")
    optparser.add_option(   "--returnbody", dest="retbody", help="Body of return")
    optparser.add_option(   "--callbackurl", dest="cburl", help="Url for callback")
    optparser.add_option(   "--callbackbody", dest="cbbody", help="Body of callback")
    optparser.add_option(   "--persistentntyurl", dest="persistentntyurl", help="Url for persistent ops notify")
    optparser.add_option(   "--persistentops", dest="persistentops", help="Persistent ops")
    optparser.add_option(   "--contentdetect", dest="contentdetect", help="Content detect type")
    optparser.add_option(   "--detectntyurl", dest="detectntyurl", help="Url for detect notify")
    optparser.add_option(   "--detectntyrule", dest="detectntyrule", help="RUle for ectect notify")
    
    
    
    optparser.set_description("Wcscmd is a tool for managing objects in WCS Object Storage."
                              + " It allows for uploading, downloading and removing objects form buckets")

    optparser.epilog = format_commands(optparser.get_prog_name(), commands_list)
    optparser.epilog += "\n\nFor more information, visit WCS website: https://wcs.chinanetcenter.com/index\n"

    (options, args) = optparser.parse_args()

    #if options.verbosity:
    #    logging.basicConfig(level=options.verbosity, format='%(levelname)s: %(message)s', stream=sys.stdderr)
    #if run_config:

    debug(u"wcscmd version 1.0.0")
    if options.run_config:
        run_configure(options.config)
        sys.exit()
    try:
        cfg = Config(options.config)
    except ValueError as exc:
        raise ParameterError(unicode(exc))
    except IOError as e:
        if options.run_configure:
            cfg = Config()
        else:
           error(u"%s: %s" % (options.config, e.strerror))
           error(u"Configuration file not available.")
           error(u"Consider using --configure parameter to create one.")
           sys.exit()

    if options.output:
        cfg.output = options.output
    if options.notifyURL:
        cfg.notifyurl = options.notifyURL
    if options.separate:
        cfg.separate = options.separate
    if options.limit:
        cfg.limit = options.limit
    if options.prefix:
        cfg.prefix = options.prefix
    if options.mode:
        cfg.mode = options.mode
    if options.marker:
        cfg.marker = options.marker 
    if options.overwirte:
        cfg.overwirte = options.overwirte
    if options.returl:
        cfg.returnUrl = options.returl
    if options.retbody:
        cfg.returnBody = options.retbody
    if options.cburl:
        cfg.callbackUrl = options.cburl
    if options.cbbody:
        cfg.callbackBody = options.cbbody
    if options.persistentntyurl:
        cfg.persistentNotifyUrl = options.persistentntyurl
    if options.persistentops:
        cfg.persistentOps = options.persistentops
    if options.contentdetect:
        cfg.contentDetect = options.contentdetect
    if options.detectntyurl:
        cfg.detectNotifyURL = options.detectntyurl
    if options.detectntyrule:
        cfg.detectNotifyRule = options.detectntyrule

    if options.dump_config:
        cfg.dump_config(sys.stdout)
        sys.exit()
    for option in cfg.option_list():
        try:
            value = getattr(options, option)
            if value != None:
                type(value) == type(b'')
            debug(u"Updating Config.Config %s->%s" % (option, value))
            cfg.update_option(option, value)
        except AttributeError:
            pass

    if int(cfg.block_size) <= 0 or (int(cfg.block_size) % 4194304) != 0:
        raise ParameterError("Block size %d B is not available" % (int(cfg.block_size)))
    if int(cfg.bput_size) <= 0 or (int(cfg.bput_size)) % 2 != 0:
        raise ParameterError("Bput size %d B is not available" % (int(cfg.bput_size)))

    if len(args) < 1:
        optparser.print_help()
        sys.exit()
   
    command = args.pop(0)
    try:
        debug(u"Command: %s" % commands[command]["cmd"])
        cmd_func = commands[command]["func"]
    except KeyError as e:
        error(u"Invalid command: %s" % command)
        sys.exit()

    if len(args) < commands[command]["argc"]:
        error(u"Not enough parameters for command '%s'" % command)
        sys.exit()
    if len(args) > commands[command]["argc"]:
        error(u"Too much parameters for command '%s'" % command)
        sys.exit()
    rc = cmd_func(args)
    if rc is None:
        rc = "Nothing"
    return rc
 
if __name__ == '__main__':
    from wcs.commons.config import Config
    from wcs.commons.logme import debug, error
    from wcs.services.client import Client
    from wcs.commons.util import urlsafe_base64_encode
    main()

