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

# Build-in / Std
import os, sys, time, platform, random
import re, json, cookielib
from getpass import getpass

# requirements
import requests, termcolor


requests = requests.Session()
requests.cookies = cookielib.LWPCookieJar('cookies')
try:
    requests.cookies.load(ignore_discard=True)
except:
    pass

class Logging:
    flag = True

    @staticmethod
    def error(msg):
        if Logging.flag == True:
            print "".join(  [ termcolor.colored("ERROR", "red"), ": ", termcolor.colored(msg, "white") ] )
    @staticmethod
    def warn(msg):
        if Logging.flag == True:
            print "".join(  [ termcolor.colored("WARN", "yellow"), ": ", termcolor.colored(msg, "white") ] )
    @staticmethod
    def info(msg):
        # attrs=['reverse', 'blink']
        if Logging.flag == True:
            print "".join(  [ termcolor.colored("INFO", "magenta"), ": ", termcolor.colored(msg, "white") ] )
    @staticmethod
    def debug(msg):
        if Logging.flag == True:
            print "".join(  [ termcolor.colored("DEBUG", "magenta"), ": ", termcolor.colored(msg, "white") ] )
    @staticmethod
    def success(msg):
        if Logging.flag == True:
            print "".join(  [ termcolor.colored("SUCCES", "green"), ": ", termcolor.colored(msg, "white") ] )

# Setting Logging
Logging.flag = True

class LoginPasswordError(Exception):
    def __init__(self, message):
        if type(message) != type("") or message == "": self.message = u"帐号密码错误"
        else: self.message = message
        Logging.error(self.message)

class NetworkError(Exception):
    def __init__(self, message):
        if type(message) != type("") or message == "": self.message = u"网络异常"
        else: self.message = message
        Logging.error(self.message)
class AccountError(Exception):
    def __init__(self, message):
        if type(message) != type("") or message == "": self.message = u"帐号类型错误"
        else: self.message = message
        Logging.error(self.message)





def download_captcha():
    url = "https://www.zhihu.com/captcha.gif"
    r = requests.get(url, params={"r": random.random(), "type": "login"}, verify=False)
    if int(r.status_code) != 200:
        raise NetworkError(u"验证码请求失败")
    image_name = u"verify." + r.headers['content-type'].split("/")[1]
    open( image_name, "wb").write(r.content)
    """
        System platform: https://docs.python.org/2/library/platform.html
    """
    Logging.info(u"正在调用外部程序渲染验证码 ... ")
    if platform.system() == "Linux":
        Logging.info(u"Command: xdg-open %s &" % image_name )
        os.system("xdg-open %s &" % image_name )
    elif platform.system() == "Darwin":
        Logging.info(u"Command: open %s &" % image_name )
        os.system("open %s &" % image_name )
    elif platform.system() in ("SunOS", "FreeBSD", "Unix", "OpenBSD", "NetBSD"):
        os.system("open %s &" % image_name )
    elif platform.system() == "Windows":
        os.system("%s" % image_name )
    else:
        Logging.info(u"我们无法探测你的作业系统,请自行打开验证码 %s 文件,并输入验证码。" % os.path.join(os.getcwd(), image_name) )

    sys.stdout.write(termcolor.colored(u"请输入验证码: ", "cyan") )
    captcha_code = raw_input( )
    return captcha_code

def search_xsrf():
    url = "http://www.zhihu.com/"
    r = requests.get(url, verify=False)
    if int(r.status_code) != 200:
        raise NetworkError(u"验证码请求失败")
    results = re.compile(r"\<input\stype=\"hidden\"\sname=\"_xsrf\"\svalue=\"(\S+)\"", re.DOTALL).findall(r.text)
    if len(results) < 1:
        Logging.info(u"提取XSRF 代码失败" )
        return None
    return results[0]

def build_form(account, password):
    if re.match(r"^1\d{10}$", account): account_type = "phone_num"
    elif re.match(r"^\S+\@\S+\.\S+$", account): account_type = "email"
    else: raise AccountError(u"帐号类型错误")

    form = {account_type: account, "password": password, "remember_me": True }

    form['_xsrf'] = search_xsrf()
    form['captcha'] = download_captcha()
    return form

def upload_form(form):
    if "email" in form:
        url = "https://www.zhihu.com/login/email"
    elif "phone_num" in form:
        url = "https://www.zhihu.com/login/phone_num"
    else:
        raise ValueError(u"账号类型错误")

    headers = {
        'User-Agent': "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36",
        'Host': "www.zhihu.com",
        'Origin': "http://www.zhihu.com",
        'Pragma': "no-cache",
        'Referer': "http://www.zhihu.com/",
        'X-Requested-With': "XMLHttpRequest"
    }

    r = requests.post(url, data=form, headers=headers, verify=False)
    if int(r.status_code) != 200:
        raise NetworkError(u"表单上传失败!")

    if r.headers['content-type'].lower() == "application/json":
        try:
            # 修正  justkg 提出的问题: https://github.com/egrcc/zhihu-python/issues/30
            result = json.loads(r.content)
        except Exception as e:
            Logging.error(u"JSON解析失败!")
            Logging.debug(e)
            Logging.debug(r.content)
            result = {}
        if result["r"] == 0:
            Logging.success(u"登录成功!" )
            return {"result": True}
        elif result["r"] == 1:
            Logging.success(u"登录失败!" )
            return {"error": {"code": int(result['errcode']), "message": result['msg'], "data": result['data'] } }
        else:
            Logging.warn(u"表单上传出现未知错误: \n \t %s )" % ( str(result) ) )
            return {"error": {"code": -1, "message": u"unknown error"} }
    else:
        Logging.warn(u"无法解析服务器的响应内容: \n \t %s " % r.text )
        return {"error": {"code": -2, "message": u"parse error"} }


def islogin():
    # check session
    url = "https://www.zhihu.com/settings/profile"
    r = requests.get(url, allow_redirects=False, verify=False)
    status_code = int(r.status_code)
    if status_code == 301 or status_code == 302:
        # 未登录
        return False
    elif status_code == 200:
        return True
    else:
        Logging.warn(u"网络故障")
        return None


def read_account_from_config_file(config_file="config.ini"):
    # NOTE: The ConfigParser module has been renamed to configparser in Python 3.
    #       The 2to3 tool will automatically adapt imports when converting your sources to Python 3.
    #       https://docs.python.org/2/library/configparser.html
    from ConfigParser import ConfigParser
    cf = ConfigParser()
    if os.path.exists(config_file) and os.path.isfile(config_file):
        Logging.info(u"正在加载配置文件 ...")
        cf.read(config_file)

        email = cf.get("info", "email")
        password = cf.get("info", "password")
        if email == "" or password == "":
            Logging.warn(u"帐号信息无效")
            return (None, None)
        else: return (email, password)
    else:
        Logging.error(u"配置文件加载失败!")
        return (None, None)




def login(account=None, password=None):
    if islogin() == True:
        Logging.success(u"你已经登录过咯")
        return True

    if account == None:
        (account, password) = read_account_from_config_file()
    if account == None:
        sys.stdout.write(u"请输入登录账号: ")
        account  = raw_input()
        password = getpass("请输入登录密码: ")

    form_data = build_form(account, password)
    """
        result:
            {"result": True}
            {"error": {"code": 19855555, "message": "unknown.", "data": "data" } }
            {"error": {"code": -1, "message": u"unknown error"} }
    """
    result = upload_form(form_data)
    if "error" in result:
        if result["error"]['code'] == 1991829:
            # 验证码错误
            Logging.error(u"验证码输入错误,请准备重新输入。" )
            return login()
        elif result["error"]['code'] == 100005:
            # 密码错误
            Logging.error(u"密码输入错误,请准备重新输入。" )
            return login()
        else:
            Logging.warn(u"unknown error." )
            return False
    elif "result" in result and result['result'] == True:
        # 登录成功
        Logging.success(u"登录成功!" )
        requests.cookies.save()
        return True

if __name__ == "__main__":
    # login(account="xxxx@email.com", password="xxxxx")
    login()