# -*- coding: UTF-8 -*-

import requests
from bs4 import BeautifulSoup, UnicodeDammit
import time
import os
import re

import log
import tools

class Get(object):
    # timeout, retry_interval -> seconds
    def __init__(self, url='', timeout=5, retry=5, retry_interval=2, proxies={}, headers={}, download_file=None, savepath='.'):
        self.log = log.Log()

        self.url = url
        self.timeout = timeout
        self.retry = retry
        self.retry_interval = retry_interval
        self.proxies = proxies
        self.headers = headers
        if download_file is None:
            self.download_file = False
        else:
            self.download_file = download_file
        self.savepath = savepath
        
        self.download_result = None

        self.__run()

    def config(self, url='', timeout=5, retry=5, retry_interval=2, proxies={}, headers={}, download_file=None, savepath=''):
        self.url = url
        self.timeout = timeout
        self.retry = retry
        self.retry_interval = retry_interval
        if len(proxies) > 0:
            self.proxies = proxies
        if not download_file is None:
            self.download_file = download_file
        if len(headers) > 0:
            self.headers = headers

        if savepath != '':
            self.savepath = savepath

        self.__run()

    def __run(self):
        if self.url != '':
            self.s = requests.Session()
            self.__get()
            if self.download_file:
                self.__download()
            else:
                self.__soup()

    def __get(self):
        if self.url == '':
            return False
        self.log.info('start get [%s]'%self.url)
        self.r = None
        for i in range(self.retry):
            try:
                self.r = self.s.get(self.url, timeout=self.timeout, proxies=self.proxies, headers=self.headers)
                break
            except Exception as e:
                self.log.error( '[retry %d] get [%s] fail. except [%s]'%(i+1, self.url, str(e)) )
                time.sleep(self.retry_interval)
        if self.r is None:
            self.log.error('get [%s] fail' % self.url)
            return False
        self.log.info('end, get [%s]' % self.url)
        return True

    def __soup(self):
        if not self.r:
            self.log.error('self.r is None, cannot get soup. url [%s]' % self.url)
            return False
        if self.download_file:
            self.log.info('to download url [%s], should not get soup' % self.url)
            return False

        self.soup = None
        try:
            self.soup = BeautifulSoup(self.r.content, 'html.parser')
            return True
        except Exception as e:
            log.error('contruct BeautifulSoup fail, url [%s], except [%s]' & (self.url, str(e)))
            return False

    def __download(self):
        self.log.info('start download [%s]' % self.url)
        if self.r is None:
            self.log.error('self.r is None. download fail. url [%s]' % self.url)
            return False

        filepath = self.savepath
        tools.mkdir(filepath)
        url = self.url

        # 获取headers中的content-length
        tot_size = 0
        try:
            tot_size = int( r.headers['content-length'] )
        except Exception as e:
            self.log.error('cannot get content-length, url [%s], headers [%s]' % (url, str(r.headers)) )
        # get file name
        filename = self.__get_file_name()

        chunk_size = 4096
        flag = 3
        # retry if size is not right.
        for i in range(3):
            now_size = 0
            try:
                #print filename, type(filename)
                with open( os.path.join(self.savepath, filename), 'wb' ) as f:
                    for chunk in r.iter_content(chunk_size):
                        now_size = now_size + len(chunk)
                        f.write(chunk)
            except Exception as e:
                self.log.error(u'something wrong. url [%s], exception [%s], 文件名 [%s], retry [%d]' % (url, unicode(e), filename, i+1) )
                flag = 3

            if tot_size == 0:
                self.log.info(u'获取文件size失败，无法校验。 获取的文件大小 [%d], 文件名 [%s], url [%s]' % (now_size, filename, url) )
                flag = 0
                break
            if now_size != tot_size:
                self.log.error('文件size不正确. 获取的文件大小 [%d], 正确文件大小 [%d], 文件名 [%s], url[%s], retry[%d]' % (now_size, tot_size, filename.encode('utf8'), url, i+1))
                flag = 4
            else:
                flag = 0
                break
            time.sleep(1)

        self.log.info('end download [%s]' % self.url)
        self.download_result = {'errno': flag, 'filename': filename}
        return self.download_result

    def __get_file_name(self):
        # 通过content-type获取后缀
        r = self.r
        url = self.url
        suf = ''
        try:
            ct = r.headers['content-type']
            ctl = ct.split(';')
            for i in ctl:
                try:
                    suf = constant.CONTENT_TYPE_REVERSE[i.strip()]
                except Exception as e:
                    pass
        except Exception as e:
            self.log.error('cannot get suffix, url[%s], headers [%s]' % (url, str(r.headers)))

        # 尝试获取Content-Disposition，并以该项中的文件名及后缀优先
        try:
            content_disposition = r.headers['Content-Disposition']
            fntmp = re.findall(r'filename=[\"\'](.*?)[\"\']', content_disposition)[0]
            pos = fntmp.rfind('.')
            if pos > -1:
                fn = fntmp[:pos]
                suf = fntmp[pos:]
            else:
                fn = fntmp
            if filename is None:
                filename = fn
                dammit = UnicodeDammit(filename, ['utf-8', 'gb2312', 'gbk'])
                filename = dammit.unicode_markup
        except Exception as e:
            pass

        # url中的文件名以及后缀
        pos = url.rfind("/") + 1
        if pos >= len(url) or pos == -1:
            fn = str(time.time()).replace(".", "")
        else:
            fn = url[pos:]
        pos = fn.rfind('.')
        if pos >= len(fn) or pos == -1:
            pass
        else:
            if suf == '':
                suf = fn[pos:]
            try:
                fn = fn[:pos]
            except Exception as e:
                pass

        filename = fn
        dammit = UnicodeDammit(filename, ['utf-8', 'gb2312', 'gbk'])
        filename = dammit.unicode_markup

        # 这里要判断一下有没有重名的文件，并做处理
        i = 0
        while True:
            if i == 0:
                if not os.path.exists( os.path.join(self.savepath, filename+suf) ):
                    break
            else:
                if not os.path.exists( os.path.join(self.savepath, filename+("(%d)"%i)+suf ) ):
                    filename = filename + ("(%d)"%i)
                    break
            i = i + 1

        filename = filename + suf

        # 确保文件名合法（windows）
        filename = tools.replaceBanCharacter(filename)
        return filename

    def download(self, url, savepath=''):
        self.url = url
        self.download_file = True
        if savepath != '':
            self.savepath = savepath
        return self.__download()

    def get(self):
        return self.r

    def soup(self):
        return self.soup

    def getsoup(self):
        return (self.r, self.soup)

    def clear_headers(self):
        self.headers = {}

    def clear_proxies(self):
        self.proxies = {}

    def stop(self):
        self.log.stop()

    def __del__(self):
        self.stop()

        r = self.r