#!/usr/bin/env python
# -*- coding=UTF-8 -*-
# Copyright (C) 2011  Alibaba Cloud Computing
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.
#
import ConfigParser
import os
import sys
import time

from collections import namedtuple
from optparse import OptionParser

from oas.oas_ops import OASCMD

AuthInfo = namedtuple(
    'AuthInfo', ['host', 'port', 'accessId', 'accessKey'], verbose=False)

DEFAULT_HOST = 'cn-hangzhou.oas.aliyuncs.com'
DEFAULT_PORT = 80
DEFAULT_CONFIGFILE = os.path.expanduser('~') + '/.oascredentials'
CONFIGSECTION = 'OASCredentials'
HELP = \
    '''Usage: oascmd [operation] [options]:

Vault Operations:
    createvault          oas://vault
    deletevault          oas://vault
    listvault            [--marker marker] [--limit limit]         
    getvaultdesc         oas://vault

Archive Operations:
    postarchive          oas://vault local_file md5sum [--desc desc]
    deletearchive        oas://vault archive_id

Multipart Archive Operations:
    createmupload        oas://vault part_size [--desc desc]
                         NOTE: part_size is size of per part , should be large than 64M(67108864) and mod 1M equals 0
    listmupload          oas://vault [--marker marker] [--limit limit]
    completemupload      oas://vault upload_id file_size md5sum
                         NOTE: file_size is the total byte size of uploaded part
    deletemupload        oas://vault upoad_id
    postmpart            oas://vault upload_id local_file start end md5sum
                         NOTE: upload file size should be equal multipart upload part setting, the last one can be less than it
    listmpart            oas://vault upload_id [--maker marker] [--limit limit]

Job Operations:
    createjob            oas://vault type[archive-retrieval|inventory-retrieval] [--archive_id archive_id] [--desc desc]
                         NOTE: when type is archive archive_id must be set
    getjobdesc           oas://vault job_id
    fetchjoboutput       oas://vault job_id local_file [--start start] [--end end] [--verbose]
    listjob              oas://vault [--marker marker] [--limit limit]

Other Operations:
    config               [--host host] [-p,--port port] -i,--id=accessid -k,--key=accesskey
    help
    
Options:
    --config_file        config_file
'''


def print_result(cmd, res):
    '''
    Print HTTP Response if failed.
    '''
    try:
        if res.status / 100 != 2:
            print 'Error Headers:\n'
            print res.getheaders()
            print 'Error Body:\n'
            print res.read(1024)
            print 'Error Status:\n'
            print res.status
            print cmd, 'Failed!'
    except AttributeError:
        pass


def fetch_credentials(options):
    config = ConfigParser.ConfigParser()
    host = None
    port = None
    accessid = None
    accesskey = None
    try:
        # set config_file
        if options.config_file is not None:
            configure_file = options.config_file
        else:
            configure_file = DEFAULT_CONFIGFILE

        config.read(configure_file)

        # set host
        if options.host is not None:
            host = options.host
        else:
            try:
                host = config.get(CONFIGSECTION, 'host')
            except Exception:
                host = DEFAULT_HOST

        # set port
        if options.port is not None:
            port = int(options.port)
        else:
            try:
                port = int(config.get(CONFIGSECTION, 'port'))
            except Exception:
                print 'Warning: port is invalid, use default port %s' % DEFAULT_PORT
                port = DEFAULT_PORT

        # set accessid
        if options.accessid is not None:
            accessid = options.accessid
        else:
            accessid = config.get(CONFIGSECTION, 'accessid')

        # set accesskey
        if options.accesskey is not None:
            accesskey = options.accesskey
        else:
            accesskey = config.get(CONFIGSECTION, 'accesskey')

        return AuthInfo(host, port, accessid, accesskey)
    except Exception, e:
        if not accessid or not accesskey:
            print "Cannot get accessid/accesskey, setup use: config [--host host] [-p,--port port] -i,--id=accessid -k,--key=accesskey"
        else:
            print 'Fetch credentials info error! error msg is %s ' % e
        sys.exit(1)


def cmd_configure(args, options):
    if options.accessid is None or options.accesskey is None:
        print '%s miss parameters, use [--host host] [-p,--port port] -i,--id=accessid -k,--key=accesskey to specify id/key pair' % args[0]
        sys.exit(1)
    config = ConfigParser.RawConfigParser()
    config.add_section(CONFIGSECTION)
    if options.host is not None:
        config.set(CONFIGSECTION, 'host', options.host)
    else:
        config.set(CONFIGSECTION, 'host', DEFAULT_HOST)

    if options.port is not None:
        config.set(CONFIGSECTION, 'port', options.port)
    else:
        config.set(CONFIGSECTION, 'port', DEFAULT_PORT)
    config.set(CONFIGSECTION, 'accessid', options.accessid)
    config.set(CONFIGSECTION, 'accesskey', options.accesskey)
    # set config_file
    if options.config_file is not None:
        configure_file = options.config_file
    else:
        configure_file = DEFAULT_CONFIGFILE
    cfgfile = open(configure_file, 'w+')
    config.write(cfgfile)
    print 'Your configuration is saved to %s .' % configure_file
    cfgfile.close()


def cmd_help(args, options):
    print HELP

if __name__ == '__main__':
    parser = OptionParser(add_help_option=False)

    parser.add_option('--host', dest='host', help='specify service host')
    parser.add_option('-p', '--port', dest='port', help='specify service port')
    parser.add_option('-i', '--id', dest='accessid', help='specify access id')
    parser.add_option(
        '-k', '--key', dest='accesskey', help='specify access key')
    parser.add_option('--config_file', dest='config_file',
                      help='the file which stores id-key pair')

    parser.add_option('', '--marker', dest='marker', help='list start mark')
    parser.add_option(
        '', '--limit', dest='limit', help='list result size limit')
    parser.add_option('', '--start', dest='start', help='start')
    parser.add_option('', '--end', dest='end', help='end')
    parser.add_option(
        '', '--archive_id', dest='archive_id', help='targe archive id')
    parser.add_option('', '--desc', dest='desc', help='description')
    parser.add_option('-v', '--verbose', action='store_true', default=False)

    # when not specify a option, the value is None
    (options, args) = parser.parse_args()

    # check args len
    if len(args) < 1:
        print HELP
        sys.exit(1)

    cmd = args[0]
    # set config parameters, from config_file or command
    if cmd == 'help':
        cmd_help(args, options)
        sys.exit(1)
    elif cmd == 'config':
        cmd_configure(args, options)
        sys.exit(1)

    # fetch auth_info
    auth_info = fetch_credentials(options)

    # error command
    method = OASCMD.__dict__.get('cmd_%s' % cmd)
    if method == None:
        print 'Unsupported command: %s ' % cmd
        print 'Use help for more information'
        sys.exit(1)

    # execute command
    begin = time.time()
    oas_cmd = OASCMD(auth_info)
    res = method(oas_cmd, args, options)
    print_result(cmd, res)
    end = time.time()
    sys.stderr.write('%.3f(s) elapsed\n' % (end - begin))
