#!/usr/bin/python
#coding: utf-8

import datetime
import json
import mylogger
import os
import time
import traceback
import urllib
import urllib2
import url_rsp

from api_const import WXUrl
from err import IException
from err import ErrCode

from py3rd import MultipartPostHandler

from lib import singleton
from lib import url_rsp

self.log.basicConfig(level=logging.INFO, format='<%(levelname)s> %(asctime)s (%(process)d,'
    ' %(thread)d) [%(module)s.%(funcName)s:%(lineno)d] %(message)s')


MAX_RETRY_COUNT = 2

@singleton.singleton
class ApiTool(object):
    def __init__(self, appid, secretkey, timeout, logger):
        self.__appid = appid
        self.__secretkey = secretkey
        self.__timeout = timeout
        self.log = log.getlogger()
        self.access_token = None

    def __handle_errcode(self, respdict):
        if 'errcode' not in respdict:
            return True
        if respdict['errcode'] == 0:
            return True
        # Need to refresh token
        if (respdict['errcode'] == 42001 or 
            respdict['errcode'] == 41001 or 
            respdict['errcode'] == 40001):
            self.refresh_accesstoken()
            return False
        else:
            self.log.error('wx api err,%s' % respdict['errcode'])
            return False

    def refresh_accesstoken(self):
        self.log.info('ENTRANCE: appid:%s, secretkey:%s',
              self.__appid, self.__secretkey)
        try:
            url = WXUrl.ACCESS_TOKEN_URL % (self.__appid, self.__secretkey)
            headers = {'Content-Type':'application/json; encoding=utf-8'}
            resp = url_rsp.send_url_request(url=url, headers=headers)
            self.log.info(resp)

            # save new access_token
            respdict = json.loads(resp)
            if 'access_token' not in respdict:
                raise IException(ErrCode.WXApiErr, 'access_token not in rsp')
            self.access_token = respdict['access_token']
            self.log.info('new ACCESS_TOKEN:%s', self.access_token)
        except Exception, e:
            raise IException(ErrCode.APPErr, 'url exception')
        return self.access_token

    

    def create_menu(self, button_list):			
        menu_list = {
            'button': button_list
        }
        self.log.info('ENTRANCE: button list:%s', str(menu_list))
        postdata = json.dumps(menu_list, ensure_ascii=False).encode('utf-8')
        if  self.access_token is None:
            self.refresh_accesstoken()
        try:
            count = MAX_RETRY_COUNT
            while count >= 0:
                url = WXUrl.MENU_PUT_URL % self.access_token
                self.log.info('menu url:%s', url)
                headers = {'Content-Type':'application/json; encoding=utf-8'}
                post = postdata

                resp = url_rsp.send_url_request(url=url, headers=headers, post=post)
                self.log.info(resp)
                respdict = json.loads(resp)
                if not self.__handle_errcode(respdict):
                    continue #access token expired, retry
        except Exception, e:
            raise IException(ErrCode.WXApiErr, 'url exception')
        self.log.info('create menu list ok') 

    def get_menu(self):
        if  self.access_token is None:
            self.refresh_accesstoken()

        try:
            url = WXUrl.MENU_GET_URL % self.access_token
            headers = {'Content-Type':'application/json; encoding=utf-8'}

            resp = url_rsp.send_url_request(url=url, headers=headers)
            self.log.info(resp)
            respdict = json.loads(resp)
            if not self.__handle_errcode(respdict):
                continue
            else:
                return respdict
        except Exception, e:
            raise IException(ErrCode.WXApiErr, 'url exception')
        raise IException(ErrCode.WXApiErr, 'the number of retry excced')

    def delete_menu(self):
        if  self.access_token is None:
            self.refresh_accesstoken()
        try:
            url = WXUrl.MENU_DEL_URL % self.access_token
            headers = {'Content-Type':'application/json; encoding=utf-8'}

            resp = url_rsp.send_url_request(url=url, headers=headers)
            self.log.info(resp)
            respdict = json.loads(resp)
            if not self.__handle_errcode(respdict):
                continue #access token expired, retry
        except Exception, e:
            raise IException(ErrCode.WXApiErr, 'url exception')
        self.log.info('delete menu ok')

    def get_followers(self):
        self.log.info('ENTRANCE:')
        try:
            uinlist = []
            next = ''
            url = (WXUrl.FOLLOWER_URL % (self.access_token, next))
            headers = {'Content-Type':'application/json; encoding=utf-8'}

            resp = url_rsp.send_url_request(url=url, headers=headers)
            self.log.info(resp)
            respdict = json.loads(resp)
            ret = self.__handle_errcode(respdict)
                    if ret == 0:
                        break
                    elif ret == 1:
                        count -= 1
                        continue  # access token expired, retry
                    else:
                        return (ret, [])
                openids = []
                if 'data' in respdict:
                    openids = respdict['data']['openid']
                next = respdict['next_openid']
                uinlist += openids
                # Finish all, break while loop
                if next == '':   
                    break
            return uinlist
        except Exception, e:
            self.log.error(traceback.format_exc())
            return (-1, [])

''' 
    def upload_media(self, file_path, media_type):
        self.log.info('ENTRANCE: file path:%s, media type:%s', file_path, media_type)
        try:
            count = MAX_RETRY_COUNT
            while count >= 0:
                opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(), 
                    MultipartPostHandler.MultipartPostHandler)
                params = {'media':open(file_path, 'rb')}
                result = opener.open('http://file.api.weixin.qq.com/cgi-bin/media/upload?access_token=%s&type=%s' % (self.access_token, media_type), params)
                resp = result.read()
                self.log.info(resp)
                respdict = json.loads(resp)
                ret = self.__handle_errcode(respdict)
                if ret == 0:
                    if 'media_id' in respdict:
                        return (0, respdict['media_id'])
                    return (0, '')
                elif ret == 1:
                    count -= 1
                    continue  # access token expired, retry
                else:
                    return ret
        except Exception, e:
            self.log.error(traceback.format_exc())
            return (-1, '')
        
    def get_media(self, media_id):
        self.log.info('ENTRANCE: media id:%s', media_id)
        try:
            count = MAX_RETRY_COUNT
            while count >= 0:
                cookies = urllib2.HTTPCookieProcessor()
                opener = urllib2.build_opener(cookies)
                request = urllib2.Request(
                    url = ('http://file.api.weixin.qq.com/cgi-bin/media/get?access_token=%s&media_id=%s' % 
                           (self.access_token, media_id)),
                    headers = {'Content-Type':'application/json; encoding=utf-8'})
                result = opener.open(request)

                # Get real file name.
                if 'Content-Disposition' in result.info():
                    file_name = result.info()['Content-Disposition'].split('filename=')[1]
                    file_name = file_name.replace('"', '').replace("'", "")
                else:
                    # if we were redirected, the real file name we take from the final URL
                    import urlparse
                    file_name = os.path.basename(urlparse.urlsplit(result.url)[2])
                self.log.info('file name:%s', file_name)
                resp = result.read()
                if resp.find('errcode') != -1:
                    self.log.info(resp)
                    respdict = json.loads(resp)
                    ret = self.__handle_errcode(respdict)
                    if ret == 0:
                        return 0
                    elif ret == 1:
                        count -= 1
                        continue  # access token expired, retry
                    else:
                        return ret
                # Save file.
                f = file(file_name, 'wb')
                f.write(resp)
                f.close()
                return 0
        except Exception, e:
            self.log.error(traceback.format_exc())
            return -1

    def get_user_info(self, openid):
        self.log.info('ENTRANCE: openid:%s', openid)
        try:
            count = MAX_RETRY_COUNT
            while count >= 0:
                url = ('https://api.weixin.qq.com/cgi-bin/user/info?access_token=%s&openid=%s' % 
                       (self.access_token, openid))
                headers = {'Content-Type':'application/json; encoding=utf-8'}

                resp = url_rsp.send_url_request(url=url, headers=headers)
                self.log.info(resp)
                respdict = json.loads(resp)
                ret = self.__handle_errcode(respdict)
                if ret == 0:
                    return (0, respdict)
                elif ret == 1:
                    count -= 1
                    continue  # access token expired, retry
                else:
                    return (ret, {})
        except Exception, e:
            self.log.error(traceback.format_exc())
            return (-1, {})
    
    
    
    def custom_send_text_msg(self, openid, text):
        self.log.info('ENTRANCE: openid:%s, text:%s', openid, text)
        datadict = {
            'touser':openid,
            'msgtype':'text',
            'text':{
                'content': text
            }
        }
        postdata = json.dumps(datadict, ensure_ascii=False).encode('utf-8')  # encode utf-8 manually
        try:
            count = MAX_RETRY_COUNT
            while count >= 0:
                url = 'https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=' + self.access_token
                headers = {'Content-Type':'application/json; encoding=utf-8'}
                post = postdata

                resp = url_rsp.send_url_request(url=url, headers=headers, post=post)
                self.log.info(resp)
                respdict = json.loads(resp)
                ret = self.__handle_errcode(respdict)
                if ret == 0:
                    return 0
                elif ret == 1:
                    count -= 1
                    continue  # access token expired, retry
                else:
                    return ret
        except Exception, e:
            self.log.error(traceback.format_exc())
            return -1	
       
    def custom_send_music_msg(self, openid, title, desc, musicurl, hqmusicurl, thumb_media_id, media_file_path):
        self.log.info('ENTRANCE: openid:%s, title:%s, desc:%s, musicurl:%s, hqmusicurl:%s, thumb_media_id:%s, media_file_path:%s', 
            openid, title, desc, musicurl, hqmusicurl, thumb_media_id, media_file_path)
        datadict = {
            'touser':openid,
            'msgtype':'music',
            'music':
            {
                'title':tile,
                'description':desc,
                'musicurl':musicurl,
                'hqmusicurl':hqmusicurl,
                'thumb_media_id':thumb_media_id
            }
        }
        postdata = json.dumps(datadict, ensure_ascii=False).encode('utf-8')  # encode utf-8 manually
        try:
            count = MAX_RETRY_COUNT
            while count >= 0:
                url = 'https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=' + self.access_token
                headers = {'Content-Type':'application/json; encoding=utf-8'}
                post = postdata

                resp = url_rsp.send_url_request(url=url, headers=headers, post=post)
                self.log.info(resp)
                respdict = json.loads(resp)
                ret = self.__handle_errcode(respdict)
                if ret == 0:
                    return 0
                elif ret == 1:
                    count -= 1
                    continue  # access token expired, retry
                else:
                    return ret
        except Exception, e:
            self.log.error(traceback.format_exc())
            return -1	
       
    def custom_send_image_msg(self, openid, media_id):
        self.log.info('ENTRANCE: openid:%s, media_id:%s', openid, media_id)
        datadict = {
            'touser':openid,
            'msgtype':'image',
            'image':
            {
                'media_id':media_id
            }
        }
        postdata = json.dumps(datadict, ensure_ascii=False).encode('utf-8')  # encode utf-8 manually
        try:
            count = MAX_RETRY_COUNT
            while count >= 0:
                url = 'https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=' + self.access_token
                headers = {'Content-Type':'application/json; encoding=utf-8'}
                post = postdata

                resp = url_rsp.send_url_request(url=url, headers=headers, post=post)
                self.log.info(resp)
                respdict = json.loads(resp)
                ret = self.__handle_errcode(respdict)
                if ret == 0:
                    return 0
                elif ret == 1:
                    count -= 1
                    continue  # access token expired, retry
                else:
                    return ret
        except Exception, e:
            self.log.error(traceback.format_exc())
            return -1	
        
    def custom_send_voice_msg(self, openid, media_id):
        self.log.info('ENTRANCE: openid:%s, media_id:%s', openid, media_id)
        datadict = {
            'touser':openid,
            'msgtype':'voice',
            'voice':
            {
                'media_id':media_id
            }
        }
        postdata = json.dumps(datadict, ensure_ascii=False).encode('utf-8')  # encode utf-8 manually
        try:
            count = MAX_RETRY_COUNT
            while count >= 0:
                url = 'https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=' + self.access_token
                headers = {'Content-Type':'application/json; encoding=utf-8'}
                post = postdata

                resp = url_rsp.send_url_request(url=url, headers=headers, post=post)
                self.log.info(resp)
                respdict = json.loads(resp)
                ret = self.__handle_errcode(respdict)
                if ret == 0:
                    return 0
                elif ret == 1:
                    count -= 1
                    continue  # access token expired, retry
                else:
                    return ret
        except Exception, e:
            self.log.error(traceback.format_exc())
            return -1	
       
    def custom_send_video_msg(self, openid, media_id, title, desc):
        self.log.info('ENTRANCE: openid:%s, media_id:%s, title:%s, desc:%s', openid, media_id, title, desc)
        datadict = {
            'touser':openid,
            'msgtype':'video',
            'video':
            {
                'media_id':media_id,
                'title':title,
                'description':desc
            }
        }
        postdata = json.dumps(datadict, ensure_ascii=False).encode('utf-8')  # encode utf-8 manually
        try:
            count = MAX_RETRY_COUNT
            while count >= 0:
                url = 'https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=' + self.access_token
                headers = {'Content-Type':'application/json; encoding=utf-8'}
                post = postdata

                resp = url_rsp.send_url_request(url=url, headers=headers, post=post)
                self.log.info(resp)
                respdict = json.loads(resp)
                ret = self.__handle_errcode(respdict)
                if ret == 0:
                    return 0
                elif ret == 1:
                    count -= 1
                    continue  # access token expired, retry
                else:
                    return ret
        except Exception, e:
            self.log.error(traceback.format_exc())
            return -1	
      
    def custom_send_news_msg(self, openid, news_list):
        self.log.info('ENTRANCE: openid:%s, newslist:%s', openid, str(news_list))
        datadict = {
            'touser':openid,
            'msgtype':'news',
            'news':{
                'articles':news_list
            }
        }
        postdata = json.dumps(datadict, ensure_ascii=False).encode('utf-8')  # encode utf-8 manually
        try:
            count = MAX_RETRY_COUNT
            while count >= 0:
                url = 'https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=' + self.access_token
                headers = {'Content-Type':'application/json; encoding=utf-8'}
                post = postdata

                resp = url_rsp.send_url_request(url=url, headers=headers, post=post)
                self.log.info(resp)
                respdict = json.loads(resp)
                ret = self.__handle_errcode(respdict)
                if ret == 0:
                    return 0
                elif ret == 1:
                    count -= 1
                    continue  # access token expired, retry
                else:
                    return ret
        except Exception, e:
            self.log.error(traceback.format_exc())
            return -1	
       
    def template_send_msg(self, openid, template_id, contentdict):
        self.log.info('ENTRANCE: openid:%s, template_id:%s, content:%s', openid, template_id, str(contentdict))
        datadict = {
            'touser':openid,
            'template_id':template_id,
            'data':contentdict
        }
        postdata = json.dumps(datadict, ensure_ascii=False).encode('utf-8')  # encode utf-8 manually
        try:
            count = MAX_RETRY_COUNT
            while count >= 0:
                url = 'https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=' + self.access_token
                headers = {'Content-Type':'application/json; encoding=utf-8'}
                post = postdata

                resp = url_rsp.send_url_request(url=url, headers=headers, post=post)
                self.log.info(resp)
                respdict = json.loads(resp)
                ret = self.__handle_errcode(respdict)
                if ret == 0:
                    return 0
                elif ret == 1:
                    count -= 1
                    continue  # access token expired, retry
                else:
                    return ret
        except Exception, e:
            self.log.error(traceback.format_exc())
            return -1	
           
    def active_send_news_msg(self, openid, news_list):
        self.log.info('ENTRANCE: openid:%s, news list:%s', openid, str(news_list))
        datadict = {
            'touser':openid,
            'msgtype':'news',
            'news':{
                'articles':news_list 
            }
        }
        postdata = json.dumps(datadict, ensure_ascii=False).encode('utf-8')  # encode utf-8 manually
        try:
            count = MAX_RETRY_COUNT
            while count >= 0:
                url = 'https://api.weixin.qq.com/cgi-bin/message/send?access_token=' + self.access_token
                headers = {'Content-Type':'application/json; encoding=utf-8'}
                post = postdata

                resp = url_rsp.send_url_request(url=url, headers=headers, post=post)
                self.log.info(resp)
                respdict = json.loads(resp)
                ret = self.__handle_errcode(respdict)
                if ret == 0:
                    return 0
                elif ret == 1:
                    count -= 1
                    continue  # access token expired, retry
                else:
                    return ret
        except Exception, e:
            self.log.error(traceback.format_exc())
            return -1	
        
    def create_qrcode(self, sceneid):
        self.log.info('ENTRANCE: sceneid:%s', sceneid)
        datadict = {
            'expire_seconds':1800,
            'action_name':'QR_SCENE',
            'action_info':
            {
                'scene':
                {
                    'scene_id':sceneid
                }
            }
        }
        postdata = json.dumps(datadict, ensure_ascii=False).encode('utf-8')  # encode utf-8 manually
        try:
            count = MAX_RETRY_COUNT
            while count >= 0:
                url = 'https://api.weixin.qq.com/cgi-bin/qrcode/create?access_token=' + self.access_token
                headers = {'Content-Type':'application/json; encoding=utf-8'}
                post = postdata

                resp = url_rsp.send_url_request(url=url, headers=headers, post=post)
                self.log.info(resp)
                respdict = json.loads(resp)
                ret = self.__handle_errcode(respdict)
                if ret == 0:
                    if 'ticket' in respdict:
                        return (0, respdict['ticket'])
                    else:
                        return (0, '')
                elif ret == 1:
                    count -= 1
                    continue  # access token expired, retry
                else:
                    return (ret, '')
        except Exception, e:
            self.log.error(traceback.format_exc())
            return (-1, '')
    
    def show_qrcode(self, ticket):
        self.log.info('ENTRANCE: ticket:%s', ticket)
        new_ticket = urllib.quote(ticket) # Note: encode ticket here, or will sometimes failed
        self.log.info('urlencode ticket: %s', new_ticket)
        try:
            url = 'https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=' + new_ticket

            resp = url_rsp.send_url_request(url=url)
            return resp
        except Exception, e:
            self.log.error(traceback.format_exc())
            return None
'''

HELP = '''
    [1] Create menu
    [2] Get menu
    [3] Delete menu
    [4] Get user info
    [5] Upload media and get media file
    [7] Get followers list
    [8] Custom send text message
    [9] Custom send image message
    [10] Custom send news message
    [11] Send template message
    [12] Active send news message
    [13] Create qrcode and show qrcode
    [-1] Bye!'''

'''
def main():
    # booksearch
    APPID = 'wx39264df2ba256ed5'
    SECRETKEY = 'c9ac985968682e36c1a33befb28e64b8'
    tool = ApiTool(APPID, SECRETKEY)
    print HELP
    while True:
        opt = raw_input()
        if opt == '-1':
            print 'Bye!'
            break
        elif opt == '1':
            etlogger('./wx.log')
