from abc import ABC, abstractmethod
import json
from os import stat
import traceback

from django.conf import settings
from api.models.campaign.campaign import Campaign
from api.models.facebook.facebook_page import FacebookPage
from api.models.youtube.youtube_channel import YoutubeChannel
from api.models.instagram.instagram_profile import InstagramProfile
from api.models.user.user_subscription import UserSubscription
from api.models.user.static_assets import StaticAssets

from api.models.order.order import Order
from api.models.order.pre_order import PreOrder
from api.models.order.order_product import OrderProduct
from api.models.cart.cart import Cart

from lib.error_handle.error.api_error import ApiVerifyError
import service
from business_policy.subscription_plan import SubscriptionPlan
import hashlib
from api import models
from bson.objectid import ObjectId
import database

platform_dict = {'facebook': FacebookPage, 'youtube': YoutubeChannel, 'instagram': InstagramProfile}

class VerifyRequestFromWhome(ABC):

    @staticmethod
    @abstractmethod
    def verify(request, pk=None):
        ...

class Verify():

    @staticmethod
    def is_platform_admin(api_user, platform_name, platform):
        try:
            if platform_name == 'facebook':
                status_code, response = service.facebook.user.get_me_accounts(
                    api_user.facebook_info['token'])

                for item in response['data']:
                    if item['id'] == platform.page_id:
                        return True
                return False
            elif platform_name == 'youtube':
                # return api_user.youtube_info[''] == platform.xxx
                return True
            elif platform_name == 'instagram':
                # api_user.instagram_info['']

                return True
        except Exception as e:
            return False
        return False

    @staticmethod
    def language_supported(language):
        if language not in settings.SUPPORTED_LANGUAGES:
            raise ApiVerifyError('util.language_not_supported')

    @staticmethod
    def check_is_admin_by_token(token, platform_name, platform):
        try:
            if platform_name == 'facebook':
                status_code, response = service.facebook.user.get_me_accounts(
                    token)
                for item in response['data']:
                    if item['id'] == platform.page_id:
                        return True
                return False
            elif platform_name == 'youtube':
                # return api_user.youtube_info[''] == platform.xxx
                return True
            elif platform_name == 'instagram':
                # api_user.instagram_info['']

                return True
        except Exception as e:
            return False
        return False

    @staticmethod
    def get_customer_user(request):
        if not request.user.api_users.filter(type='customer').exists():
            raise ApiVerifyError('util.no_api_user_found')
        return request.user.api_users.get(type='customer')

    @staticmethod
    def get_seller_user(request):
        if not request.user.api_users.filter(type='user').exists():
            raise ApiVerifyError('util.no_api_user_found')
        api_user = request.user.api_users.get(type='user')
        if api_user.status != "valid":
            raise ApiVerifyError("util.not_activated_user")
        return api_user

    @staticmethod
    def get_seller_user_from_scope(scope):
        auth_user = scope.get('user')
        if not auth_user:
            raise ApiVerifyError('util.no_api_user_found')
        if not auth_user.api_users.filter(type='user').exists():
            raise ApiVerifyError('util.no_api_user_found')
        api_user = auth_user.api_users.get(type='user')
        if api_user.status != "valid":
            raise ApiVerifyError("util.not_activated_user")
        return api_user

    @staticmethod
    def verify_user(api_user):
        if not api_user:
            raise ApiVerifyError("util.no_api_user_found")
        elif api_user.status != "valid":
            raise ApiVerifyError("util.not_activated_user")

    @classmethod
    def get_platform(cls, api_user, platform_name, platform_id):
        if platform_name not in platform_dict:
            raise ApiVerifyError("util.no_platform_name_found")
        if not platform_dict[platform_name].objects.filter(id=platform_id).exists():
            raise ApiVerifyError("util.no_platform_found")
        platform = platform_dict[platform_name].objects.get(
            id=platform_id)
        # if not cls.is_platform_admin(api_user, platform_name, platform):
        #     raise ApiVerifyError("user is not platform admin")
        return platform

    @classmethod
    def get_platform_from_user_subscription(cls, user_subscription, platform_name, platform_id):
        if platform_name not in models.user.user_subscription.PLATFORM_ATTR:
            raise ApiVerifyError('util.not_support_platform')

        if not getattr(user_subscription, models.user.user_subscription.PLATFORM_ATTR[platform_name]['attr']).filter(id=platform_id).exists():
            raise ApiVerifyError('util.no_platform_found')

        return getattr(user_subscription, models.user.user_subscription.PLATFORM_ATTR[platform_name]['attr']).get(id=platform_id)

    @classmethod
    def get_platform_verify_with_token(cls, token, platform_name, platform_id):
        if platform_name not in platform_dict:
            raise ApiVerifyError("util.no_platform_name_found")
        if not platform_dict[platform_name].objects.filter(id=platform_id).exists():
            raise ApiVerifyError("util.no_platform_found")
        platform = platform_dict[platform_name].objects.get(
            id=platform_id)
        if not cls.check_is_admin_by_token(token, platform_name, platform):
            raise ApiVerifyError("util.user_is_not_platform_admin")
        return platform

    @staticmethod
    def get_support_stock_user_subscriptions_from_user_subscription(support_stock_user_subscription_id,self_user_subscription_subscription):
        support_stock_user_subscriptions = json.loads(json.dumps(self_user_subscription_subscription.meta_store.get("support_stock_user_subscriptions", [])))
        print(support_stock_user_subscriptions)
        for i in support_stock_user_subscriptions:
            try:
                if i.get("user_subscription_id","") == int(support_stock_user_subscription_id):
                    return models.user.user_subscription.UserSubscription.objects.get(id=support_stock_user_subscription_id)
            except:
                print(traceback.format_exc())
        raise ApiVerifyError("util.no_support_stock_user_subscription_found")
    @staticmethod
    def get_cart(cart_id):
        if not Cart.objects.filter(id=cart_id).exists():
            raise ApiVerifyError('util.no_cart_found')
        return Cart.objects.get(id=cart_id)

    @staticmethod
    def get_cart_from_user_subscription(user_subscription, cart_id):
        if not user_subscription.carts.filter(id=cart_id).exists():
            raise ApiVerifyError('util.no_cart_found')
        return user_subscription.carts.get(id=cart_id)

    @staticmethod
    def get_cart_with_oid(oid):
        try:
            _id=ObjectId(oid)
        except Exception:
            raise ApiVerifyError('util.no_cart_found')

        cart = database.lss.cart.Cart.get(_id=_id)
        if not cart:
            raise ApiVerifyError('util.no_cart_found')
        return Cart.objects.get(id=cart['id'])

    @staticmethod
    def get_user_register_with_oid(oid):
        try:
            _id=ObjectId(oid)
        except Exception:
            raise ApiVerifyError('no registeration found')

        user_register = database.lss.user_register.UserRegister.get(_id=_id)
        if not user_register:
            raise ApiVerifyError('no registeration found')
        return models.user.user_register.UserRegister.objects.get(id=user_register['id'])

    @staticmethod
    def get_pre_order(pre_order_id):
        if not PreOrder.objects.filter(id=pre_order_id).exists():
            raise ApiVerifyError('util.no_pre_order_found')
        return PreOrder.objects.get(id=pre_order_id)

    @staticmethod
    def get_pre_order_with_oid(oid):
        try:
            _id=ObjectId(oid)
        except Exception:
            raise ApiVerifyError('util.no_pre_order_found')

        pre_order = database.lss.pre_order.PreOrder.get(_id=_id)
        # pre_order=db.api_pre_order.find_one({"_id":_id})
        if not pre_order:
            raise ApiVerifyError('util.no_pre_order_found')
        return PreOrder.objects.get(id=pre_order['id'])

    @staticmethod
    def get_order_product(order_product_id):
        if not OrderProduct.objects.filter(id=order_product_id).exists():
            raise ApiVerifyError('util.no_order_product_found')
        return OrderProduct.objects.get(id=order_product_id)

    @staticmethod
    def get_order(order_id):
        if not Order.objects.filter(id=order_id).exists():
            raise ApiVerifyError('util.no_order_found')
        return Order.objects.get(id=order_id)

    @staticmethod
    def get_order_from_user_subscription(user_subscription, order_id):
        if not user_subscription.orders.filter(id=order_id).exists():
            raise ApiVerifyError('util.no_order_found')
        return user_subscription.orders.get(id=order_id)

    @staticmethod
    def get_order_with_oid(oid):
        try:
            _id=ObjectId(oid)
        except Exception:
            raise ApiVerifyError('util.no_order_found')
        # order=db.api_order.find_one({"_id":_id})
        order = database.lss.order.Order.get(_id=_id)
        if not order:
            raise ApiVerifyError('util.no_order_found')
        return Order.objects.get(id=order['id'])

    @staticmethod
    def get_order_by_api_user(api_user, order_id):
        if not api_user.orders.filter(id=order_id).exists():
            raise ApiVerifyError('util.no_order_found')
        return api_user.orders.get(id=order_id)

    @staticmethod
    def get_user_subscription(user_subscription_id):
        if not UserSubscription.objects.filter(id=user_subscription_id).exists():
            raise ApiVerifyError("util.user_subscription_not_found")
        return UserSubscription.objects.get(id=user_subscription_id)

    @staticmethod
    def get_user_subscription_from_platform(platform):
        user_subscriptions = platform.user_subscriptions.all()
        if not user_subscriptions:
            raise ApiVerifyError("util.platform_not_in_any_user_subscription")
        user_subscription = user_subscriptions[0]
        return user_subscription

    @staticmethod
    def get_customer_from_user_subscription(user_subscription, customer_id):
        if not user_subscription.customers.filter(id=customer_id).exists():
            raise ApiVerifyError("customer_not_found")
        return user_subscription.customers.get(id=customer_id)

    @staticmethod
    def get_user_subscription_from_api_user(api_user):
        user_subscription = api_user.user_subscription
        if not user_subscription:
            raise ApiVerifyError("util.user_subscription_not_found")
        return user_subscription

    @staticmethod
    def get_assets_from_user_subscription(user_subscription, id):
        if not user_subscription.assets.filter(id=id).exists():
            raise ApiVerifyError("util.static_assets_not_found")
        return user_subscription.assets.get(id=id)

    @staticmethod
    def get_dealer_user_subscription_from_api_user(api_user):
        user_subscription = api_user.user_subscription
        if not user_subscription:
            raise ApiVerifyError("util.user_subscription_not_found")
        if user_subscription.type != "dealer":
            raise ApiVerifyError("util.not_dealer")
        return user_subscription

    def get_user_subscription_from_dealer_user_subscription(dealer_user_subscription, user_subscription_id):
        if not dealer_user_subscription.subscribers.filter(id=user_subscription_id).exists():
            raise ApiVerifyError("util.user_subscription_not_found")
        return dealer_user_subscription.subscribers.get(id=user_subscription_id)

    @staticmethod
    def get_facebook_page_from_user_subscription(user_subscription, facebook_page_id):
        if not user_subscription.facebook_pages.filter(id=facebook_page_id).exists():
            raise ApiVerifyError("util.facebook_page_not_bound_to_user_subscription")
        return user_subscription.facebook_pages.get(id=facebook_page_id)

    @staticmethod
    def get_youtube_channel_from_user_subscription(user_subscription, youtube_channel_id):
        if not user_subscription.youtube_channels.filter(id=youtube_channel_id).exists():
            raise ApiVerifyError("util.youtube_channel_not_bound_to_user_subscription")
        return user_subscription.youtube_channels.get(id=youtube_channel_id)

    @staticmethod
    def get_instagram_profile_from_user_subscription(user_subscription, instagram_profile_id):
        if not user_subscription.instagram_profiles.filter(id=instagram_profile_id).exists():
            raise ApiVerifyError("util.instagram_profile_not_bound_to_user_subscription")
        return user_subscription.instagram_profiles.get(id=instagram_profile_id)

    @staticmethod
    def get_twitch_channel_from_user_subscription(user_subscription, twitch_channel_id):
        if not user_subscription.twitch_channels.filter(id=twitch_channel_id).exists():
            raise ApiVerifyError("util.twitch_channels_not_bound_to_user_subscription")
        return user_subscription.twitch_channels.get(id=twitch_channel_id)

    @staticmethod
    def get_tiktok_channel_from_user_subscription(user_subscription, tiktok_account_id):
        if not user_subscription.tiktok_accounts.filter(id=tiktok_account_id).exists():
            raise ApiVerifyError("util.tiktok_accounts_not_bound_to_user_subscription")
        return user_subscription.tiktok_accounts.get(id=tiktok_account_id)

    @staticmethod
    def get_campaign(campaign_id):
        if not Campaign.objects.filter(id=campaign_id).exists():
            raise ApiVerifyError("util.no_campaign_found")
        campaign = Campaign.objects.get(id=campaign_id)
        return campaign

    @staticmethod
    def get_campaign_from_order(order):
        if not order.campaign:
            raise ApiVerifyError("util.no_campaign_found")
        return order.campaign

    @staticmethod
    def get_campaign_from_pre_order(pre_order):
        if not pre_order.campaign:
            raise ApiVerifyError("util.no_campaign_found")
        return pre_order.campaign

    @staticmethod
    def get_campaign_from_cart(cart):
        if not cart.campaign:
            raise ApiVerifyError("util.no_campaign_found")
        return cart.campaign

    @staticmethod
    def get_campaign_product(campaign_product_id):
        if not models.campaign.campaign_product.CampaignProduct.objects.filter(id=campaign_product_id).exists():
            raise ApiVerifyError("util.no_campaign_product_found")
        return models.campaign.campaign_product.CampaignProduct.objects.get(id=campaign_product_id)

    @staticmethod
    def get_campaign_from_platform(platform, campaign_id):
        if not platform.campaigns.filter(id=campaign_id).exists():
            raise ApiVerifyError("util.no_campaign_found")
        campaign = platform.campaigns.get(id=campaign_id)
        return campaign

    @staticmethod
    def get_campaign_from_user_subscription(user_subscription, campaign_id):
        if not user_subscription.campaigns.filter(id=campaign_id).exists():
            raise ApiVerifyError("util.no_campaign_found")
        campaign = user_subscription.campaigns.get(id=campaign_id)
        return campaign

    @staticmethod
    def get_product_from_user_subscription(user_subscription, product_id):
        if not user_subscription.products.filter(id=product_id).exists():
            raise ApiVerifyError("util.no_product_found")
        return user_subscription.products.get(id=product_id)

    @staticmethod
    def get_product_category_from_user_subscription(user_subscription, product_category_id):
        if not user_subscription.product_categories.filter(id=product_category_id).exists():
            raise ApiVerifyError("util.no_product_category_found")
        return user_subscription.product_categories.get(id=product_category_id)

    @staticmethod
    def get_campaign_product_from_campaign(campaign, campaign_product_id):
        if not campaign.products.filter(id=campaign_product_id).exists():
            raise ApiVerifyError('util.no_campaign_product_found')
        campaign_product = campaign.products.get(id=campaign_product_id)
        return campaign_product

    @staticmethod
    def get_pre_order_from_campaign(campaign, pre_order_id):
        if not campaign.pre_orders.filter(id=pre_order_id).exists():
            raise ApiVerifyError('util.no_pre_order_found')
        return campaign.pre_orders.get(id=pre_order_id)

    @staticmethod
    def get_order_product_from_pre_order(pre_order, order_product_id):
        if not pre_order.order_products.filter(id=order_product_id).exists():
            raise ApiVerifyError("util.no_order_product_found")
        return pre_order.order_products.get(id=order_product_id)

    @staticmethod
    def get_campaign_product_from_pre_order(pre_order, campaign_product_id):
        if not pre_order.campaign.products.filter(id=campaign_product_id).exists():
            raise ApiVerifyError("util.no_campaign_product_found")
        return pre_order.campaign.products.get(id=campaign_product_id)

    @staticmethod
    def is_valid_country_code_for_user_plan(country_code):
        support_country_code = [cls_attribute.__name__  for cls_attribute in SubscriptionPlan.__dict__.values() if type(cls_attribute)==type] #TODO find a better way to do this
        if country_code not in support_country_code:
            raise ApiVerifyError("util.country_code_not_valid")

    @staticmethod
    def is_valid_country_code(country_code):
        if country_code not in ["SG", "PH", "IN", "ID", "MY", "TW", "CN"]:
            raise ApiVerifyError("util.country_code_not_valid")

    @staticmethod
    def get_discount_code_from_user_subscription(user_subscription, discount_code_id):

        if not user_subscription.discount_codes.filter(id=discount_code_id).exists():
            raise ApiVerifyError("util.no_discount_code_found")
        return user_subscription.discount_codes.get(id=discount_code_id)

    @staticmethod
    def get_auto_response_from_user_subscription(user_subscription, auto_response_id):

        if not user_subscription.auto_responses.filter(id=auto_response_id).exists():
            raise ApiVerifyError("util.no_auto_reply_found")
        return user_subscription.auto_responses.get(id=auto_response_id)

    @staticmethod
    def user_match_pre_order(api_user, pre_order):
        pass
        # platform_id = pre_order.get('platform_id')
        # platform_name = pre_order.get('platform')

        # if platform_name == 'facebook':
        #     pass
        #     if pre_order.get('customer_id') != api_user.get('facebook_info',{}).get("id"):
        #         raise ApiVerifyError('error!')
        # elif platform_name == 'youtube':
        #     if pre_order.get('customer_id') != api_user.get('google_info',{}).get("id"):
        #         raise ApiVerifyError('error!')
        #     pass
        # else:
        #     pass

    @staticmethod
    def user_match_order(api_user, order):
        pass
        # platform_name = order.get('platform')

        # if platform_name == 'facebook':
        #     pass
        #     if order.get('customer_id') != api_user.get('facebook_info',{}).get("id"):
        #         raise ApiVerifyError('error!')
        # elif platform_name == 'youtube':
        #     if order.get('customer_id') != api_user.get('google_info',{}).get("id"):
        #         raise ApiVerifyError('error!')
        #     pass
        # else:
        #     pass

    @staticmethod
    def is_hubspot_signature_valid(request):

        request_body = request.body.decode('utf-8')
        http_method = request.method
        http_uri = settings.GCP_API_LOADBALANCER_URL+request.get_full_path()
        source_string = settings.HUBSPOT_CLIENT_SECRET + http_method + http_uri + request_body

        print(http_method,http_uri)
        if request.META.get('HTTP_X_HUBSPOT_SIGNATURE') != hashlib.sha256(source_string.encode('utf-8')).hexdigest():
            raise ApiVerifyError('util.signature_error')

    @staticmethod
    def check_is_page_token_valid(platform_name, officiall_page_token, officiall_page_id=None):
        try:
            if platform_name == 'facebook':
                status_code, response = service.facebook.page.get_page_posts(page_token=officiall_page_token, page_id=officiall_page_id, limit=1)
                if status_code == 200:
                    return True
                return False
            elif platform_name == 'youtube':
                status_code, response = service.youtube.channel.get_list_channel_by_token(access_token=officiall_page_token)
                # print("response", response)
                if status_code == 200:
                    return True
                return False
            elif platform_name == 'instagram':
                status_code, response = service.instagram.profile.get_profile_live_media(page_token=officiall_page_token, profile_id=officiall_page_id, limit=1)
                if status_code == 200:
                    return True
                return False
            elif platform_name == 'twitch':
                status_code, response = service.twitch.twitch.api_twitch_validate_token(access_token=officiall_page_token)
                if status_code == 200:
                    return True
        except Exception as e:
            return False
        return False

    @staticmethod
    def get_lucky_draw(lucky_draw_id):
        if not models.campaign.campaign_lucky_draw.CampaignLuckyDraw.objects.filter(id=lucky_draw_id).exists():
            raise ApiVerifyError('util.lucky_draw_not_found')
        return models.campaign.campaign_lucky_draw.CampaignLuckyDraw.objects.get(id=lucky_draw_id)

    @staticmethod
    def get_quiz_game(quiz_game_id):
        if not models.campaign.campaign_quiz_game.CampaignQuizGame.objects.filter(id=quiz_game_id).exists():
            raise ApiVerifyError('util.quiz_game_not_found')
        return models.campaign.campaign_quiz_game.CampaignQuizGame.objects.get(id=quiz_game_id)

    @staticmethod
    def get_quiz_game_bundle(quiz_game_bundle_id):
        if not models.campaign.campaign_quiz_game_bundle.CampaignQuizGameBundle.objects.filter(id=quiz_game_bundle_id).exists():
            raise ApiVerifyError('util.quiz_game_not_found')
        return models.campaign.campaign_quiz_game_bundle.CampaignQuizGameBundle.objects.get(id=quiz_game_bundle_id)

    @staticmethod
    def get_user_register_by_email(user_register_email):
        if not models.user.user_register.UserRegister.objects.filter(email=user_register_email).exists():
            raise ApiVerifyError(f'user register not found by {user_register_email}')
        return models.user.user_register.UserRegister.objects.filter(email=user_register_email).order_by('-created_at')[0]

    @staticmethod
    def get_product_by_id(product_id):
        if not models.product.product.Product.objects.filter(id=product_id).exists():
            raise ApiVerifyError(f'product not found by {product_id}')
        return models.product.product.Product.objects.get(id=product_id)


"""Console script for crystfelparser."""

from collections import defaultdict
from xmlrpc.client import Boolean
from tqdm import tqdm
import numpy as np
import argparse
import sys

from crystfelparser.utils import save_dict_to_hdf5

# Just round up and down the time, we can extend this to a larger time window

def round_time(raw_spots, dt=0):
    """
    Function for rouding the time/angle

    Args:
      raw_spots: matrix where each row is [SPOT_X, SPOT_Y, FRAME]
      dt: time window to flatten into a single frame [-dt,+dt]

    Returns:
      A numpy array where each row is [SPOT_X, SPOT_Y, FRAME_ROUNDED]
    """

    # we could remove duplicates here
    # set_of_floats=set(list(map(tuple,raw_spots[:,:3])))
    max_frame = int(np.max(raw_spots[:, 2]))
    expanded_list = []
    for spot in raw_spots:
        # round to the time to the earest integer
        rounded = np.round(spot[2])
        # set the lower bound
        min_round = max(0, rounded - dt)
        # set the upper bound
        max_round = min(rounded + dt + 1, max_frame + 1)

        for new_time in np.arange(min_round, max_round):
            expanded_list.append((spot[0], spot[1], new_time))

    return np.asarray(expanded_list)

def match_spots_frame_expanded(spots, n_frame):
    """
    Given the list of spots found by XDS and a specific frame number,
    return the list of centers found matching the frame number given

    Args:
      spots: matrix where each row is [SPOT_X, SPOT_Y, FRAME]
      n_frame: the frame to select

    Returns:
      A numpy array containing the spots for the selected frame
    """

    # find all the spots in the same frame
    idx_rows = np.where(list(map(int, spots[:, 2])) == n_frame)
    tmp_list = spots[idx_rows, ([0], [1])].T

    # I use set to remove possible duplicates
    return np.asarray(list(set(list(map(tuple, tmp_list)))))

def get_list_spots_fromfile(filename, colspot=False, timerounded=True, shift_min=True, dt=3):
    """
    Load spot centers from a file and return a dictionary where each key
    is a frame number

    Args:
      filename: filepath containing the output from XDS [e.g. SPOT.XDS, XDS.XDS_ASCII.HKL]
      colspot: flag to specify if the file is XDS.XDS_ASCII.HKL or SPOT.XDS [Default: False]
      timerounded: flag to round or not the frame number [default: True]
      shift_min: flag to shif the minimum frame to zero [default: True]
      dt: window of frames to collaps within a central frame [-dt, dt] [default: 3]

    Returns:
      A dictionary with the spots per frame, where each frame is a frame number
    """

    dict_spots = defaultdict(lambda: [])

    if colspot:
        # reading SPOT.XDS
        spots_reflections_raw = np.loadtxt(filename)
    else:
        # eading XDS_ASCII.HKL
        spots_reflections_raw = []
        with open(filename, encoding='utf-8') as f:
            lines = f.readlines()
            for line in lines:
                if line.split()[0][0] != "!":
                    spots_reflections_raw.append(
                        list(map(float, line.split()[5:8])))
        spots_reflections_raw = np.asarray(spots_reflections_raw)

    if timerounded:
        spots_reflections_raw = round_time(spots_reflections_raw, dt)

    # get unique frames
    # frames=set(rounded_spots[:,2])
    frames = set(spots_reflections_raw[:, 2].astype(int))
    min_framenum = 0
    if shift_min:
        min_framenum = min(frames)

    for fr in tqdm(frames):
        dict_spots[fr -
                   min_framenum] = match_spots_frame_expanded(spots_reflections_raw, fr)

    return dict_spots

###################################################
# Script to be called from the command line

def str2bool(v):
    if isinstance(v, bool):
        return v
    if v.lower() in ('yes', 'true', 't', 'y', '1'):
        return True
    elif v.lower() in ('no', 'false', 'f', 'n', '0'):
        return False
    else:
        raise argparse.ArgumentTypeError('Boolean value expected.')

def parse_args():
    """Parser"""
    parser = argparse.ArgumentParser(
        description='Console script to parse XDS_ASCII.HKL (or SPOT.XDS)'
    )

    parser.add_argument(
        '--file', default="XDS_ASCII.HKL", help='XDS file to parse [e.g. XDS_ASCII.HKL]'
    )

    parser.add_argument("--colspot", type=str2bool, nargs='?', const=True, default=False,
                        help="Reading a SPOT.XDS file (COLSPOT output) [Default: False]."
                        )

    parser.add_argument(
        '--dt', type=int, default=0, help='Time window to flatten [-dt, dt] [Default: 0]'
    )

    parser.add_argument(
        '--output',
        default="parsed_xds.h5",
        help='Parsed file, stored in hdf5 format [default: parsed_xds.h5]',
    )

    if len(sys.argv) == 1:
        parser.print_help(sys.stderr)
    else:
        args = parser.parse_args()
        # args.func(**vars(args))

        return args

def main():
    """ """
    # read from the parser
    inputs = parse_args()

    # parse the input stream
    print("Parsing {}".format(inputs.file))

    timerounded = False
    if inputs.dt > 0:
        timerounded = True

    # for now shift_min is always True
    parsed_stream = get_list_spots_fromfile(
        inputs.file, inputs.colspot, timerounded, True, inputs.dt)

    # save the input stream to a .h5 file
    save_dict_to_hdf5(parsed_stream, inputs.output)
    print("Parsed frames saved in {}".format(inputs.output))

if __name__ == "__main__":
    sys.exit(main())


# coding=utf-8
# @Time    : 2021/3/26 10:43
# @Author  : lucas
# @File    : video_tools2.0.py
# @Project : pyqt
# @Software: PyCharm
from PyQt5.QtWidgets import QVBoxLayout, QAction, QTableView, QTabWidget, \
    QFormLayout, QComboBox, QCheckBox, QDialog, QDialogButtonBox, QSpinBox, \
    QStyleFactory, QLineEdit, QTableWidget, QMenu, QFileDialog, \
    QTableWidgetItem, QHeaderView
from PyQt5.QtCore import QDir, QUrl, QTimer
from PyQt5.QtGui import QDesktopServices
import sys
import resources
from ui_style import *
from utils import *
from ui_utils import *

init_db()
DOWNLOAD_DIR = get_download_dir()
update_progress(DOWNLOAD_DIR)
global download_list_data
download_list_data = get_download_list()

def download_task(self, name, url, episode):
    def update_download_list(value):
        global download_list_data
        origin = download_list_data.get(url, None)
        if not origin:
            download_list_data = get_download_list()
            print(f"更新数据 {download_list_data}")

    def after_download_complete():
        # status 为2表示下载完成、1表示暂停、0表示正在下载
        global download_list_data
        print("更新状态")
        get_db().execute(f"update downloadList set status=2 where url='{url}'")
        download_list_data = get_download_list()
        print("合并片段")
        # 将下载的片段合并并删除掉片段
        merge_file(DOWNLOAD_DIR, name, episode)
        print("合并结束")
        # QApplication.processEvents()

    progress = get_progress(url)
    if progress:
        if progress[2] == 2:
            information(f"{episode} 已经下载完成，不要重复下载!")
            return
        if progress[2] == 1:
            # 状态更新为正在下载
            get_db().execute(f"update downloadList set status=0 where url='{url}'")
            print(f"状态更新为正在下载")

    self.task_thread = DownloadThread(url=url, name=name, episode=episode, download_path=DOWNLOAD_DIR)
    self.task_thread.process.connect(update_download_list)
    self.task_thread.finished.connect(after_download_complete)
    self.task_thread.start()

@singleton
class DownloadList(QDialog):
    signal = pyqtSignal()

    def __init__(self, parent):
        super().__init__(parent)
        self.setWindowTitle("下载列表")
        self.setFixedSize(414, 400)
        self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint)
        self.setWindowIcon(QIcon(":logo.ico"))
        self.vbox = QVBoxLayout()
        self.download_manage = QTableView()
        self.download_manage.verticalHeader().setVisible(False)
        self.download_manage.setShowGrid(False)
        self.download_manage.setSelectionMode(QAbstractItemView.NoSelection)
        self.download_manage.horizontalHeader().setVisible(False)
        self.download_manage.horizontalHeader().setStyleSheet(download_header_style)
        self.download_manage.verticalHeader().setVisible(False)
        self.download_manage.setEditTriggers(QAbstractItemView.NoEditTriggers)
        self.hbox = QHBoxLayout()
        self.text = QLabel(f"共下载{len(download_list_data.values())}个文件！")
        self.text.setFont(QFont('Times', 10))
        self.begin_all = QPushButton("全部开始")
        self.begin_all.setFont(QFont('Times', 9))
        self.stop_all = QPushButton("全部暂停")
        self.stop_all.setFont(QFont('Times', 9))
        self.clean_all = QPushButton("清除所有记录")
        self.clean_all.setToolTip("只删除已完成或者未开始的记录")
        self.clean_all.setFont(QFont('Times', 9))
        if not download_list_data:
            # self.begin_all.setDisabled(True)
            # self.stop_all.setDisabled(True)
            # self.clean_all.setDisabled(True)
            self._disabled_button()
            self.text.setText("共下载0个文件！")
        self.begin_all.clicked.connect(self._begin_all)
        self.stop_all.clicked.connect(self._stop_all)
        self.clean_all.clicked.connect(self._clean_all)
        self.hbox.addWidget(self.text)
        self.hbox.addWidget(self.begin_all)
        self.hbox.addWidget(self.stop_all)
        self.hbox.addWidget(self.clean_all)
        self.vbox.addLayout(self.hbox)
        self.download_list_ui()
        self.setLayout(self.vbox)
        self.signal.connect(self.realtime_refresh_ui)

    def _disabled_button(self):
        self.begin_all.setDisabled(True)
        self.stop_all.setDisabled(True)
        self.clean_all.setDisabled(True)

    def _clean_all(self):
        print("# click clean all")
        global download_list_data
        msg_box = warning()
        reply = msg_box.exec()
        if reply == QMessageBox.AcceptRole:
            print('你选择了删除！')
            get_db().execute("delete from downloadList where status in (1, 2)")
            rows = self.download_manage.model().rowCount()
            for row in range(rows - 1, -1, -1):
                print(self.download_manage.model().index(row, 3))
                if list(download_list_data.values())[row][-1]:
                    self.download_manage.model().removeRow(row)
            download_list_data = get_download_list()
            if not download_list_data:
                self._disabled_button()
            self.text.setText(f"共下载{len(download_list_data.values())}个文件！")
        if reply == QMessageBox.RejectRole:
            print('你选择了取消！')

    def _begin_all(self):
        print("# click begin all")
        information("暂未实现，敬请期待")

    def _stop_all(self):
        print("# click stop all")
        information("暂未实现，敬请期待")

    @staticmethod
    def _format_data(dict_data):
        all_data = {}
        for key, value in dict_data.items():
            downloaded = get_num_of_files(DOWNLOAD_DIR, value[5], value[0])
            downloaded = value[3] if downloaded == "complete" else downloaded
            all_data[key] = [f"{value[5]}-{value[0]}", value[1], downloaded * 100 // value[3], value[4]]
        return all_data

    def realtime_refresh_ui(self):
        if not download_list_data:
            return
        all_data = self._format_data(download_list_data)
        data_ = list(all_data.values())
        if len(self.data) == len(data_):
            self.model._data = data_
            self.download_manage.viewport().repaint()
        else:
            self.data = data_
            self.download_list_ui()
        QApplication.processEvents()

    def delete_single(self, value):
        global download_list_data
        print("# delete single record")
        name, episode, row, status = value
        msg_box = warning()
        cb = QCheckBox('删除源文件')
        msg_box.setCheckBox(cb)
        reply = msg_box.exec()
        if reply == QMessageBox.AcceptRole:
            print('你选择了删除！')
            # 只可以删除状态为1，2（暂停、下载完成）的
            if status:
                get_db().execute(f"delete from downloadList where name='{name}' and episode='{episode}'")
                self.download_manage.model().removeRow(row)
                download_list_data = get_download_list()
                if not download_list_data:
                    self._disabled_button()
                self.text.setText(f"共下载{len(download_list_data.values())}个文件！")
                if cb.isChecked():
                    print("你勾选了删除本地文件")
                    # 删除本地文件
                    delete_files(DOWNLOAD_DIR, name, episode)
            else:
                information("不能删除正在下载的任务！")
        if reply == QMessageBox.RejectRole:
            print('你选择了取消！')

    def continue_download(self, value):
        global download_list_data
        name, episode, status = value
        url = [k for k, v in download_list_data.items() if name in v and episode in v][0]
        if status:
            print(f"继续下载{value}")
            get_db().execute(f"update downloadList set status=0 where url='{url}'")
            download_task(self, name, url, episode)
        else:
            # get_db().execute(f"update downloadList set status=1 where url='{url}'")
            # todo 停止任务
            pass
        download_list_data = get_download_list()

    def download_list_ui(self):
        all_data = self._format_data(download_list_data)
        self.data = list(all_data.values())
        button_delegate = ButtonDelegate(self.download_manage)
        button_delegate.signal.connect(self.continue_download)
        button_delegate.delete_signal.connect(self.delete_single)
        progress_delegate = ProgressBarDelegate(self.download_manage)
        self.download_manage.setItemDelegateForColumn(2, progress_delegate)
        self.download_manage.setItemDelegateForColumn(3, button_delegate)
        self.model = TableModel(self.data, ('名称', '日期', '下载进度', "操作"))
        self.download_manage.setModel(self.model)
        self.download_manage.horizontalHeader().setSectionResizeMode(QHeaderView.Stretch)
        self.download_manage.setColumnWidth(0, 80)
        self.download_manage.setColumnWidth(1, 70)
        self.download_manage.setColumnWidth(2, 120)
        self.download_manage.setColumnWidth(3, 110)
        self.vbox.addWidget(self.download_manage)

@singleton
class M3U8(QDialog):
    def __init__(self, parent):
        super().__init__(parent)
        self.setWindowTitle("M3U8 小助手")
        self.setFixedSize(350, 350)
        # 去掉dialog右上角的 ？
        self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint)
        self.setWindowIcon(QIcon(":m3u8.png"))
        # self.setStyleSheet('background-color:grey;color:red;')
        main_layout = QVBoxLayout()
        self.tab_widget = QTabWidget()
        self.player = QWidget()
        self.downloader = QWidget()
        self.tab_widget.addTab(self.player, "播放")
        self.tab_widget.addTab(self.downloader, "下载")
        self.tab_widget.setStyleSheet(label_style)
        main_layout.addWidget(self.tab_widget)
        self.setLayout(main_layout)
        self.player_ui()
        self.downloader_ui()
        self.play_btn.clicked.connect(self._play)
        self.downloader_btn.clicked.connect(self._download)

    def player_ui(self):
        layout = QFormLayout()
        self.player_combobox = QComboBox(parent=self)
        self.player_combobox.addItems(Constant.PLAYERS.keys())
        self.player_combobox.setMinimumHeight(24)
        self.player_combobox.setMaximumWidth(90)
        self.player_combobox.setMinimumWidth(80)
        self.player_combobox.setStyleSheet(combobox_style)
        self.play_input = QLineEdit(parent=self)
        self.play_input.setPlaceholderText("请输入M3U8链接地址")
        # just for debug
        # self.play_input.setText("http://chyd-sn.wasu.tv/tbvideo/20141108/a5715565-44de-43ff-864d-2e8c5011e361.m3u8")
        self.play_input.setStyleSheet(play_input_style)
        self.play_btn = QPushButton()
        self.play_btn.setFixedSize(40, 38)
        self.play_btn.setStyleSheet(play_btn_style)
        play_layout = QHBoxLayout()
        play_layout.addWidget(self.play_input)
        play_layout.addWidget(self.play_btn)
        layout.addRow(self.player_combobox)
        layout.addRow(play_layout)
        self.player.setLayout(layout)

    def downloader_ui(self):
        layout = QFormLayout()
        self.downloader_input = QLineEdit(parent=self)
        self.downloader_input.setPlaceholderText("请输入M3U8链接地址")
        self.downloader_input.setStyleSheet(play_input_style)
        self.downloader_btn = QPushButton()
        self.downloader_btn.setFixedSize(28, 28)
        self.downloader_btn.setStyleSheet(download_btn_style)
        downloader_layout = QHBoxLayout()
        downloader_layout.addWidget(self.downloader_input)
        downloader_layout.addWidget(self.downloader_btn)
        layout.addRow(downloader_layout)
        self.downloader.setLayout(layout)

    def _download(self):
        url = self.downloader_input.text()
        if is_m3u8_url(url):
            print(f"downloading the film: {url}")
            name = datetime.now().strftime("%Y%m%d%H%M%S")
            download_task(self, url, name, name)
            self.downloader_input.clear()
        else:
            information("请输入有效的M3U8链接!")

    def _play(self):
        player = self.player_combobox.currentText()
        url = self.play_input.text()
        if is_m3u8_url(url):
            if player.lower() == 'vlc':
                print("open the film with vlc")
                play_task(self, url)
                QApplication.processEvents()
            else:
                print(Constant.PLAYERS[player] + url)
                # http://chyd-sn.wasu.tv/tbvideo/20141108/a5715565-44de-43ff-864d-2e8c5011e361.m3u8
                QDesktopServices.openUrl(QUrl(url.strip()))
            self.play_input.clear()
        else:
            information("请输入有效的M3U8链接!")

@singleton
class Settings(QDialog):
    def __init__(self, parent):
        super().__init__(parent)
        self.con = get_db()
        self.settings = self.con.query("select modes, path, concurrencyNum, themeStyle from setting")
        self.setWindowTitle("偏好设置")
        self.setFixedSize(400, 400)
        self.setWindowIcon(QIcon(":settings.png"))
        # 去掉dialog右上角的 ？
        self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint)
        self.btn_open = QPushButton("")
        self.label = QLabel("下载路径: ")
        self.path_input = QLineEdit(self.settings[0][1], self)
        main_layout = QVBoxLayout()
        self.tab_widget = QTabWidget()
        self.setting = QWidget()
        self.tab_widget.addTab(self.setting, "设置")
        self.tab_widget.setStyleSheet(label_style)
        main_layout.addWidget(self.tab_widget)
        button_box = QDialogButtonBox(QDialogButtonBox.Save | QDialogButtonBox.Cancel)
        button_box.accepted.connect(self._save)
        button_box.rejected.connect(self._cancel)
        main_layout.addWidget(button_box)
        self.setLayout(main_layout)
        self.setting_ui()
        self.btn_open.clicked.connect(self._select_path)

    def closeEvent(self, QCloseEvent):  # real signature unknown; restored from __doc__
        """ closeEvent(self, QCloseEvent) """
        self._cancel()

    def _select_path(self):
        folder = QFileDialog.getExistingDirectory(self, "Choose Directory", "C:\\", QFileDialog.ShowDirsOnly)
        if folder:
            print(QDir.rootPath())
            print(QDir.homePath())
            folder = QDir.toNativeSeparators(folder)
        if os.path.isdir(folder):
            self.path_input.setText(folder)

    def _save(self):
        print("click the save button")
        # QLightPalette().set_app(video_tools)
        value = 1 if self.checkbox.isChecked() else 0
        update_setting = f"update setting set modes={value}, path='{self.path_input.text()}', " \
                         f"concurrencyNum={self.spin_box.text()}, themeStyle='{self.style_combobox.currentText()}' " \
                         f"where id=1"
        self.con.execute(update_setting)
        self.close()

    def _cancel(self):
        print("click the cancel button")
        modes, path, concurrency_num, theme_style = self.settings[0]
        if not modes:
            QLightPalette().set_app(video_tools)
            self.checkbox.setChecked(False)
        self.path_input.setText(path)
        self.spin_box.setValue(concurrency_num)
        self._change_style(theme_style)
        self.style_combobox.setCurrentText(theme_style)
        self.close()

    @staticmethod
    def _change_style(style_name):
        # 改变Style
        video_tools.setStyle(QStyleFactory.create(style_name))

    @staticmethod
    def _change_mode(state):
        # app = QApplication.instance()
        if state == Qt.Checked:
            QDarkPalette().set_app(video_tools)
        else:
            QLightPalette().set_app(video_tools)

    def setting_ui(self):
        form_layout = QFormLayout()
        self.path_input.setReadOnly(True)
        self.path_input.setStyleSheet(setting_input_style)
        self.btn_open.setFixedSize(30, 30)
        self.btn_open.setStyleSheet(btn_open_style)
        h_box_layout = QHBoxLayout()
        h_box_layout.addWidget(self.label)
        h_box_layout.addWidget(self.path_input)
        h_box_layout.addWidget(self.btn_open)
        self.spin_box = QSpinBox()
        self.spin_box.setValue(self.settings[0][2])
        self.spin_box.setStyleSheet(spin_box_style)

        self.style_combobox = QComboBox()
        # 获取本机支持的主题，并添加到QComboBox中
        self.style_combobox.addItems(QStyleFactory.keys())
        self.style_combobox.setCurrentText("Fusion")
        # 绑定槽函数，
        self.style_combobox.activated[str].connect(self._change_style)
        self.style_combobox.setStyleSheet(combobox_style)
        self.style_combobox.setMinimumHeight(22)
        self.style_combobox.setMaximumWidth(145)
        self.style_combobox.setMinimumWidth(80)

        self.checkbox = QCheckBox()
        self.checkbox.setStyleSheet(checkbox_style)
        if self.settings[0][0]:
            self.checkbox.setChecked(True)
        if self.checkbox.isChecked():
            QDarkPalette().set_app(video_tools)
        else:
            QLightPalette().set_app(video_tools)
        # self.checkbox.setChecked(True)
        self.checkbox.stateChanged.connect(self._change_mode)

        form_layout.addRow(h_box_layout)
        form_layout.addRow("并发数: ", self.spin_box)
        form_layout.addRow("主题风格: ", self.style_combobox)
        form_layout.addRow("黑暗模式: ", self.checkbox)
        self.setting.setLayout(form_layout)

class VideoToolsUi(QMainWindow):

    def __init__(self):
        super(VideoToolsUi, self).__init__()
        self._init_ui()

    def _init_ui(self):
        self.setWindowTitle("Video Tools")
        self.setFixedSize(550, 500)
        self.setWindowIcon(QIcon(":logo.ico"))
        self.general_layout = QVBoxLayout()
        self.result_layout = QVBoxLayout()
        self.search_layout = QHBoxLayout()
        self.central_widget = QWidget()
        self.result_table = QTableWidget()
        self.detail_table = QTableWidget()
        self.setCentralWidget(self.central_widget)
        self.central_widget.setLayout(self.general_layout)
        self._search_layout()
        self._result_layout()
        self._result_table()
        self._detail_table()
        self.general_layout.addLayout(self.search_layout)
        self.general_layout.addLayout(self.result_layout)
        self._create_menu_bar()
        self.download_list_dialog = DownloadList(self)
        self._setting = Settings(self)
        self._m3u8 = M3U8(self)

    # menu ui and action
    def _download_list(self):
        print("download list clicked")
        self.download_list_dialog.show()

    def _m3u8(self):
        print("m3u8 clicked")
        self._m3u8.show()

    def _downloader(self):
        print("downloader clicked")
        self._m3u8.show()
        self._m3u8.tab_widget.setCurrentIndex(1)

    def _vlc(self):
        print("open the vlc player")
        play_task(self)
        QApplication.processEvents()

    @staticmethod
    def _help():
        print("click the help button")
        QDesktopServices.openUrl(QUrl("https://github.com/lucas234/video-tool"))

    def _select_file(self):
        print("click the select_file button")
        # 单选
        file_name, _ = QFileDialog.getOpenFileName(self, 'Open a file', "C:\\", 'All Files (*.*)')
        # 多选
        # file_names, _ = QFileDialog.getOpenFileNames(self, 'Open a file', "C:\\", 'All Files (*.*)')
        if file_name:
            file_name = QDir.toNativeSeparators(file_name)
            # file_names = [QDir.toNativeSeparators(file) for file in file_names]
            print(file_name)

    def _settings(self):
        print("open the settings")
        self._setting.show()

    @staticmethod
    def _open_file():
        print("open file clicked")
        open_dir(DOWNLOAD_DIR)

    def _create_menu_bar(self):
        menu_bar = self.menuBar()
        menu_bar.setStyleSheet(menu_style)
        file_menu = QMenu("&文件(F)", self)
        tool_menu = QMenu("&工具(T)", self)
        help_menu = QMenu("&帮助(H)", self)
        menu_bar.addMenu(file_menu)
        menu_bar.addMenu(tool_menu)
        menu_bar.addMenu(help_menu)
        self.download_list = QAction(QIcon(":films_list.png"), "下载列表", self)
        self.settings = QAction(QIcon(":settings.png"), "设置", self)
        self.open_file = QAction(QIcon(":folder-open-film.png"), "打开文件夹", self)
        self.m3u8 = QAction(QIcon(":m3u8.png"), "M3U8")
        self.downloader = QAction(QIcon(":downloader.png"), "下载器")
        self.version = QAction(QIcon(":about.png"), "关于", self)
        self.help = QAction(QIcon(":help.png"), "帮助", self)
        tool_menu.addActions([self.m3u8, self.downloader])
        self.players = tool_menu.addMenu(QIcon(":disc-blue.png"), "播放器")
        self.vlc = QAction(QIcon(":vlc.png"), "VLC")
        self.players.addActions([self.vlc])
        file_menu.addActions([self.settings, self.download_list, self.open_file])
        help_menu.addActions([self.help, self.version])
        self.version.triggered.connect(lambda: information("当前版本 1.0 "))
        self.open_file.triggered.connect(self._open_file)
        self.download_list.triggered.connect(self._download_list)
        self.m3u8.triggered.connect(self._m3u8)
        self.downloader.triggered.connect(self._downloader)
        self.vlc.triggered.connect(self._vlc)
        self.settings.triggered.connect(self._settings)
        self.help.triggered.connect(self._help)

    def _search_layout(self):
        self.search_input = QLineEdit()
        self.search_button = QPushButton("")
        self.search_input.setPlaceholderText("搜索影片名")
        self.search_input.setStyleSheet(search_input_style)
        self.search_button.setStyleSheet(search_button_style)
        self.search_input.setFixedHeight(25)
        self.search_button.setFixedHeight(25)
        self.search_button.setFixedWidth(90)
        self.search_button.setIconSize(QSize(23, 23))
        self.search_button.setIcon(QIcon(":search.png"))
        self.search_layout.addWidget(self.search_input)
        self.search_layout.addWidget(self.search_button)

    def _result_layout(self):
        self.result_label = QLabel("搜索结果: (PS: 双击查看详情)")
        self.detail_label = QLabel("播放下载: (PS: 右键查看菜单)")
        self.result_label.setStyleSheet(label_style)
        self.detail_label.setStyleSheet(label_style)
        self.detail_label.setFixedHeight(30)
        self.result_label.setFixedHeight(30)
        self.result_layout.addWidget(self.result_label)
        self.result_layout.addWidget(self.result_table)
        self.result_layout.addWidget(self.detail_label)
        self.result_layout.addWidget(self.detail_table)

    def _result_table(self):
        Table(self.result_table, 0, 4, ['#', '名称', '类别', '更新时间'])
        self.result_table.setColumnWidth(0, 50)
        self.result_table.setColumnWidth(1, 270)
        self.result_table.setColumnWidth(2, 80)
        self.result_table.setColumnWidth(3, 130)
        self.result_table.sortItems(3, Qt.DescendingOrder)

    def _detail_table(self):
        Table(self.detail_table, 0, 3, ["#", "剧集", "播放地址"])
        self.detail_table.setColumnWidth(0, 50)
        self.detail_table.setColumnWidth(1, 150)
        self.detail_table.setColumnWidth(2, 330)

        def context_menu(pos):
            table_widget = self.detail_table
            row_num = table_widget.currentRow()
            # column_num = table_widget.currentColumn()
            menu = QMenu()
            download_all = menu.addAction(QIcon(":download_all.png"), u'全部下载')
            download = menu.addAction(QIcon(":download.png"), u'下载')
            play = menu.addAction(QIcon(":play.png"), u'播放')
            download_list = menu.addAction(QIcon(":films_list.png"), "下载列表")
            # copy_link = menu.addAction(QIcon(":copy.png"), u'复制链接')
            # copy_link.setShortcut(QKeySequence.Copy)
            action = menu.exec_(table_widget.mapToGlobal(pos))
            # 显示选中行的数据文本
            url = table_widget.item(row_num, 2).text()
            episode = table_widget.item(row_num, 1).text()
            result_table_row = self.result_table.currentRow()
            name = self.result_table.item(result_table_row, 1).text()

            if action == download_all:
                print('你选了{下载所有}：', url)
            if action == download:
                print('你选了{下载}：', episode, url)
                download_task(self, name, url, episode)
            if action == play:
                print('你选了{播放}：', url)
                play_task(self, link=url)
                QApplication.processEvents()
            if action == download_list:
                download_list_dialog = DownloadList(self)
                download_list_dialog.show()
                print('你选了{下载列表}：', url)

        self.detail_table.setContextMenuPolicy(Qt.CustomContextMenu)
        self.detail_table.customContextMenuRequested.connect(context_menu)

class VideoToolsCtrl(object):

    def __init__(self, view):
        self._view = view
        self.data = []
        self._connect_signals()

    def _connect_signals(self):
        def _display_search_results():
            self.table_widget = self._view.result_table
            self.table_widget.setRowCount(len(self.data))
            for i, value in enumerate(self.data):
                for j, v in enumerate([str(i), value['name'], value["type"], value["date"]]):
                    cell = QTableWidgetItem(v)
                    cell.setTextAlignment(Qt.AlignCenter)
                    self.table_widget.setItem(i, j, cell)

        def search_function():
            keyword = self._view.search_input.text()
            if not keyword:
                information("请输入要搜索的影片名")
            else:
                # 添加loading效果
                self.loading = LoadingMask(self._view, ":loading.gif")
                self.loading.show_loading(self._view)

                def handle_result(result):
                    self.data = result
                    if not self.data:
                        self._view.search_input.clear()
                        information("未搜到影片, 请重新输入新的影片名")
                    _display_search_results()

                self.search_thread = SearchThread(handle_result, keyword=keyword)
                self.search_thread.finished.connect(lambda: self.loading.deleteLater())
                self.search_thread.start()

        self._view.search_button.clicked.connect(search_function)
        # press Enter key to search
        self._view.search_input.returnPressed.connect(search_function)
        self._view.result_table.doubleClicked.connect(self._display_detail_results)

    def _display_detail_results(self):
        row = self._view.result_table.currentRow()
        # column = self._view.result_table.currentColumn()
        # print(self._view.result_table.item(row, 1).text())
        self.table_widget = self._view.detail_table
        links = self.data[row]['m3u8_links'] if self.data else []
        self.table_widget.setRowCount(len(links))
        for i, value in enumerate(links):
            episode, link = value.split("$")
            for j, v in enumerate([str(i), episode, link]):
                cell = QTableWidgetItem(v)
                cell.setTextAlignment(Qt.AlignCenter)
                self.table_widget.setItem(i, j, cell)

    def emit_signal(self):
        # if self._view.download_list_dialog.data:
        self._view.download_list_dialog.signal.emit()

if __name__ == "__main__":
    video_tools = QApplication(sys.argv)
    ui = VideoToolsUi()
    ui.show()
    v_ctrl = VideoToolsCtrl(view=ui)
    # 实时刷新下载列表
    timer = QTimer()
    timer.timeout.connect(v_ctrl.emit_signal)
    timer.start(1000)
    sys.exit(video_tools.exec_())


import wx
import string
import sys
import os, os.path
from animation import CAnimation
from newanimation import NewAnimationDialog
import pypoujol.animation
from pypoujol import Animation, get_parent_class
import pypoujol.animation as animation
import wx.lib.imagebrowser as ib
from os.path import realpath, normpath, normcase, exists, join
from depplatform import get_image_path

oiLineHeight = 20

class InspectorEditorControl:
    """ Interface for controls that edit values in the Inspector
        values are stored in the native type of the control       """

    def __init__(self, propEditor, value):
        self.propEditor = propEditor
        self.editorCtrl = None
        self.wID = wx.NewId()
        self.value = value

    def createControl(self):
        if self.editorCtrl: self.editorCtrl.SetFocus()

    def destroyControl(self):
        """ Close an open editor control """
        if self.editorCtrl:
            self.editorCtrl.Destroy()
            self.editorCtrl = None

    def getValue(self):
        """ Read value from editor control """
        return self.value

    def setValue(self, value):
        """ Write value to editor control """
        self.value = value

    # default sizing for controls that span the entire value width
    def setWidth(self, width):
        if self.editorCtrl:
            height = self.editorCtrl.GetSize().y
            self.editorCtrl.SetSize(wx.Size(width -1, height))

    def setIdx(self, idx):
        """ Move the to the given index """
        if self.editorCtrl:
            self.editorCtrl.SetPosition( (-2, idx*oiLineHeight -2) )

    def OnSelect(self, event):
        """ Post the value.

            Bind the event of the control that 'sets' the value to this method
        """
        event.Skip()
        self.propEditor.inspectorPost(False)

class BevelIEC(InspectorEditorControl):
    def destroyControl(self):
        if self.bevelTop:
            self.bevelTop.Destroy()
            self.bevelTop = None
            self.bevelBottom.Destroy()
        InspectorEditorControl.destroyControl(self)

    def createControl(self, parent, idx, sizeX):
        self.bevelTop = wx.Panel(parent, -1,
            (0, idx*oiLineHeight -1), (sizeX, 1))
        self.bevelTop.SetBackgroundColour(wx.BLACK)
        self.bevelBottom = wx.Panel(parent, -1,
            (0, (idx + 1)*oiLineHeight -1), (sizeX, 1))
        self.bevelBottom.SetBackgroundColour(wx.WHITE)

    def setWidth(self, width):
        if self.bevelTop:
            self.bevelTop.SetSize(wx.Size(width, 1))
            self.bevelBottom.SetSize(wx.Size(width, 1))

    def setIdx(self, idx):
        if self.bevelTop:
            self.bevelTop.SetPosition(wx.Point(-2, idx*oiLineHeight -1))
            self.bevelBottom.SetPosition(wx.Point(-2, (idx +1)*oiLineHeight -1))
#        InspectorEditorControl.setIdx(self, idx)

class BeveledLabelIEC(BevelIEC):
    def createControl(self, parent, idx, sizeX):
        BevelIEC.createControl(self, parent, idx, sizeX)
        self.editorCtrl = wx.StaticText(parent, -1, self.value,
            (2, idx*oiLineHeight+2),
            (sizeX, oiLineHeight-3))
        self.editorCtrl.SetForegroundColour(wx.Colour(24, 24, 24)) #Preferences.propValueColour)

class TextCtrlIEC(InspectorEditorControl):
    ugly_hack = True

    def createControl(self, parent, value, idx, sizeX, style=wx.TE_PROCESS_ENTER):
        value = self.propEditor.valueToIECValue()
        self.editorCtrl = wx.TextCtrl(parent, self.wID, value,
              (-2, idx*oiLineHeight -2),
              (sizeX, oiLineHeight+3), style = style)
        wx.EVT_TEXT_ENTER(parent, self.wID, self.OnSelect)
        InspectorEditorControl.createControl(self)

        if value:
            self.editorCtrl.SetSelection(0, len(value))

        # Ugly hack. It doesn't get focus the very first time
        if TextCtrlIEC.ugly_hack:
            if wx.Platform != "__WXMSW__": wx.GetApp().frame.pb.Freeze()
            wx.GetApp().frame.pb.nb.SetSelection(1)
            wx.GetApp().frame.pb.nb.SetSelection(0)
            if wx.Platform != "__WXMSW__": wx.GetApp().frame.pb.Thaw()
            TextCtrlIEC.ugly_hack = False

    def getValue(self):
        if self.editorCtrl:
            self.value = self.editorCtrl.GetValue()
        return self.value

    def setValue(self, value):
        self.value = value
        if self.editorCtrl:
            self.editorCtrl.SetValue(value)

class ChoiceIEC(InspectorEditorControl):
    def createControl(self, parent, values, idx, sizeX, iec = None):
        self.editorCtrl = wx.Choice(parent, self.wID,
         wx.Point(-2, idx*oiLineHeight -1), wx.Size(sizeX, oiLineHeight+3),
         values)
        wx.EVT_CHOICE(self.editorCtrl, self.wID, self.OnSelect)
        InspectorEditorControl.createControl(self)
        self.iec = None

    def getValue(self):
        if self.editorCtrl:
            sel = self.editorCtrl.CurrentSelection
            #if sel != -1: return self.editorCtrl.GetClientData(sel)
            return self.editorCtrl.GetStringSelection()

    def setValue(self, value):
        if self.editorCtrl:
            self.editorCtrl.SetStringSelection(value)

    def repopulate(self):
        self.editorCtrl.Clear()
        for val in self.propEditor.getValues():
            self.editorCtrl.Append(val)

    def OnSelect(self, evt):
        InspectorEditorControl.OnSelect(self, evt)

class ComboIEC(InspectorEditorControl):
    def createControl(self, parent, idx, sizeX):
        self.editorCtrl = wxComboBox(parent, self.wID, self.value,
         wxPoint(-2, idx*Preferences.oiLineHeight -1), wxSize(sizeX, Preferences.oiLineHeight+3),
         self.propEditor.getValues())
        InspectorEditorControl.createControl(self);
    def getValue(self):
        if self.editorCtrl:
            return self.editorCtrl.GetStringSelection()
    def setValue(self, value):
        if self.editorCtrl:
            self.editorCtrl.SetSelection(self.editorCtrl.FindString(value))

class ButtonIEC(BevelIEC):
    btnSize = 18
    def createControl(self, parent, idx, sizeX, editMeth):
        bmp = wx.Image(get_image_path('ellipsis.xpm'), wx.BITMAP_TYPE_XPM).ConvertToBitmap()
        self.editorCtrl = wx.BitmapButton(parent, self.wID, bmp,
          wx.Point(sizeX - self.btnSize - 3, idx*oiLineHeight +1),
          wx.Size(self.btnSize, oiLineHeight-2))
        self.propValLabel = wx.StaticText(parent, -1, str(self.getValue()),
          wx.Point(2, idx*oiLineHeight+2),
          wx.Size(sizeX - self.btnSize - 6, oiLineHeight-3),
          style=wx.ST_NO_AUTORESIZE)
        wx.EVT_BUTTON(self.editorCtrl, self.wID, editMeth)
        BevelIEC.createControl(self, parent, idx, sizeX)

    def setWidth(self, width):
        if self.editorCtrl:
            self.editorCtrl.SetDimensions(width - self.btnSize - 3,
              self.editorCtrl.GetPosition().y, self.btnSize,
              oiLineHeight-2)
            self.propValLabel.SetDimensions(2,
              self.propValLabel.GetPosition().y, width - self.btnSize - 6,
              oiLineHeight-3)

        BevelIEC.setWidth(self, width)

    def setIdx(self, idx):
        if self.editorCtrl:
            self.editorCtrl.SetDimensions(self.editorCtrl.GetPosition().x,
              idx*oiLineHeight +2, self.btnSize,
              oiLineHeight-2)
            self.propValLabel.SetDimensions(
              self.propValLabel.GetPosition().x,
              idx*oiLineHeight +1, self.propValueLabel.GetSize().x,
              oiLineHeight-3)

        BevelIEC.setIdx(self, idx)

    def setValue(self, value):
        BevelIEC.setValue(self, value)
        self.propValLabel.SetLabel(str(value))

    def destroyControl(self):
        if self.editorCtrl:
            self.propValLabel.Destroy()
            self.propValLabel = None
        BevelIEC.destroyControl(self)

class TextCtrlButtonIEC(BevelIEC):
    def createControl(self, parent, value, idx, sizeX, editMeth = None):
        bmp = wx.Bitmap('images/ellipsis.xpm')
        value = self.propEditor.valueToIECValue()
        self.wID2 = wx.NewId()
        self.editorCtrl = [
              wx.TextCtrl(parent, self.wID, value,
               (-2, idx*oiLineHeight -2),
               (sizeX - 18, oiLineHeight+3)),#, style = style),
              wx.BitmapButton(parent, self.wID2, bmp,
               (sizeX - 18 -3, idx*oiLineHeight +1),
               (18, oiLineHeight-2))]
        EVT_BUTTON(self.editorCtrl[1], self.wID2, editMeth)

        if value:
            self.editorCtrl[0].SetSelection(0, len(value))

        BevelIEC.createControl(self, parent, idx, sizeX)

    def destroyControl(self):
        """ Close an open editor control """
        if self.editorCtrl:
            #self.editorCtrl.GetParent().SetFocus()
            for ec in self.editorCtrl:
                ec.Destroy()
            self.editorCtrl = None
        if self.bevelTop:
            self.bevelTop.Destroy()
            self.bevelTop = None
            self.bevelBottom.Destroy()
            self.bevelBottom = None

    # default sizing for controls that span the entire value width
    def setWidth(self, width):
        if self.editorCtrl:
            self.editorCtrl[0].SetSize(wxSize(width -18,
                  self.editorCtrl[0].GetSize().y))
            self.editorCtrl[1].SetDimensions(width - 18 -3,
                  self.editorCtrl[1].GetPosition().y, 18,
                  Preferences.oiLineHeight-2)

        BevelIEC.setWidth(self, width)

    def setIdx(self, idx):
        """ Move the to the given index """
        if self.editorCtrl:
            for ec in self.editorCtrl:
                ec.SetPosition(ec.GetPosition().x, idx*Preferences.oiLineHeight -2)
        BevelIEC.setIdx(self, idx)

    def getValue(self):
        if self.editorCtrl:
            self.value = self.editorCtrl[0].GetValue()
        return self.value

    def setValue(self, value):
        self.value = value
        if self.editorCtrl:
            self.editorCtrl[0].SetValue(value)

class CheckBoxIEC2(InspectorEditorControl):
    def createControl(self, parent, idx, sizeX):
        self.editorCtrl = wxWindow(parent, wxNewId(),
         style = wxTAB_TRAVERSAL | wxSUNKEN_BORDER)
        self.editorCtrl.SetDimensions(-2, idx*Preferences.oiLineHeight-2,
         sizeX, Preferences.oiLineHeight+3)

        self.checkBox = wxCheckBox(self.editorCtrl, self.wID, 'False', (2, 1))
        EVT_CHECKBOX(self.editorCtrl, self.wID, self.OnSelect)
        def OnWinSize(evt, win=self.checkBox):
            win.SetSize(evt.GetSize())
        EVT_SIZE(self.editorCtrl, OnWinSize)

        InspectorEditorControl.createControl(self)

    truefalseMap = {True: 'True', False: 'False'}
    def getValue(self):
        if self.editorCtrl:
            return self.truefalseMap[self.editorCtrl.GetValue()]
    def setValue(self, value):
        if self.editorCtrl:
            self.editorCtrl.SetLabel(value)
            self.editorCtrl.SetValue(self.truefalseMap[True] == value)

    def OnSelect(self, event):
        if event.IsChecked():
            self.setValue(self.truefalseMap[event.IsChecked()])

        InspectorEditorControl.OnSelect(self, event)

class CheckBoxIEC(BevelIEC):
    def createControl(self, parent, idx, sizeX):
        self.editorCtrl = wx.CheckBox(parent, self.wID, 'False',
            (2, idx*oiLineHeight+1),
            (sizeX, oiLineHeight-2) )
        wx.EVT_CHECKBOX(self.editorCtrl, self.wID, self.OnSelect)

        BevelIEC.createControl(self, parent, idx, sizeX)

    truefalseMap = {True: 'True', False: 'False'}
    def getValue(self):
        if self.editorCtrl:
            return self.truefalseMap[self.editorCtrl.GetValue()]
        else:
            return self.value
    def setValue(self, value):
        if self.editorCtrl:
            self.editorCtrl.SetLabel(value)
            self.editorCtrl.SetValue(
                string.lower(self.truefalseMap[True]) == string.lower(value))

    def setIdx(self, idx):
        if self.editorCtrl:
            self.editorCtrl.SetDimensions(2, idx*Preferences.oiLineHeight +1,
            self.editorCtrl.GetSize().x, Preferences.oiLineHeight-2)
        BevelIEC.setIdx(self, idx)
        # InspectorEditorControl.setIdx(self, idx)

    def OnSelect(self, event):
        self.setValue(self.truefalseMap[event.IsChecked()])

        BevelIEC.OnSelect(self, event)

import  wx.lib.popupctl as  pop
import  wx.calendar     as  cal

thumbnail_size = 64

class AnimationPanel(wx.Panel):
    class ThumbnailCache:
        def __init__(self):
            self.cache = {}

        def get_thumbnail(self, filename):
            filename = join(wx.GetApp().frame.project.project_path, filename)
            if not exists(filename):
                return None
            if self.cache.has_key(filename):
                return self.cache[filename]
            img = wx.Image(filename)
            w, h = img.GetWidth(), img.GetHeight()
            if w > h:
                h *= thumbnail_size / float(w)
                w = thumbnail_size
            else:
                w *= thumbnail_size / float(h)
                h = thumbnail_size

            img.Rescale(w, h)
            bmp = wx.BitmapFromImage(img)
            self.cache[filename] = bmp
            return bmp

    cache = ThumbnailCache()

    def __init__(self, parent, iec, poz):
        wx.Panel.__init__(self, parent, pos = poz)
        self.parent = parent
        self.iec = iec

        n = len(pypoujol.animation.anim_classes.values()) + 2
        self.SetSize((thumbnail_size * 4 + 40, (n / 4) * thumbnail_size + 104))
        parent.SetSize((thumbnail_size * 4 + 40, (n / 4) * thumbnail_size + 104))

    def create_buttons(self):
        def is_derived_from(c, b):
            try:
                for i in c.__bases__:
                    if b == i: return True
                    if is_derived_from(i, b): return True
                return None
            except: pass # Not a class ?

        if sys.platform == "darwin": pos = (20, 20)
        else: pos = (20, 20)
        cancel = wx.Button(self, 30, "Cancel\n", pos = pos, size = (thumbnail_size, thumbnail_size))
        cancel.Bind(wx.EVT_BUTTON, self.on_cancel_button)
        n = 1
        pos = ((n % 4) * thumbnail_size + 20, (n / 4) * thumbnail_size + 20)
        nonebutton = wx.Button(self, -1, "None\n", pos = pos, size = (thumbnail_size, thumbnail_size))
        nonebutton.Bind(wx.EVT_BUTTON, self.on_none_button)
        n += 1
        newanimbutton = wx.Button(self, -1, "From file\n",
            pos = ((n % 4) * thumbnail_size + 20,
                   (n / 4) * thumbnail_size + 20),
            size = (thumbnail_size, thumbnail_size))
        newanimbutton.Bind(wx.EVT_BUTTON, self.on_new_anim_button)
        n += 1
        self.buttons = []

        for i, filename, j in pypoujol.animation.anim_classes.values():
            if (not filename) or (not filename[0]): continue
            filename = filename[0]
            bmp = self.cache.get_thumbnail(filename)
            if not bmp: continue
            b = wx.BitmapButton(self, 30 + n, bmp, ((n % 4) * thumbnail_size + 20, (n / 4) * thumbnail_size + 20), (thumbnail_size, thumbnail_size))
            self.buttons.append(b)
            try:
                parent_class = get_parent_class(j)
                b.image = parent_class
                b.Bind(wx.EVT_BUTTON, self.on_button)
                n = n + 1
            except: pass

    def on_cancel_button(self, evt):
        self.iec.editorCtrl.PopDown()

    def on_none_button(self, evt):
        self.iec.editorCtrl.SetValue("None")
        self.iec.editorCtrl.PopDown()

    def on_new_anim_button(self, evt):
        self.iec.editorCtrl.PopDown()

        newanimdlg = NewAnimationDialog(wx.GetApp().frame, -1, size = (500, 400),
                                        style = wx.DEFAULT_DIALOG_STYLE)
        newanimdlg.CenterOnScreen()
        newanimdlg.name.SetValue(self.iec.resource.__class__.__name__ + "_anim")
        val = newanimdlg.ShowModal()

        if val == wx.ID_OK:
            project = wx.GetApp().frame.project
            res = wx.GetApp().frame.pb.active_resource
            res.sync()
            name = newanimdlg.name.GetValue()
            filename = project.ask_to_import(newanimdlg.filename.GetValue())

            if filename.endswith('.psd'):
                import PIL.Image
                img = PIL.Image.open(str(filename))
                base = str(filename[:-4]) + '.png'
                import tempfile
                import exceptions
                import shutil

                tempdir = tempfile.gettempdir()
                tempdir = tempdir + '/psd_outputs'
                shutil.rmtree(tempdir, ignore_errors=True)

                try: os.mkdir(tempdir)
                except: pass

                for index, layer in enumerate(img.layers):
	                img.seek(index)
	                img.save(join(tempdir, os.path.basename(base) + "_" + layer[0].decode('utf-8') + ".png"))

                dlg = ib.ImageDialog(self, tempdir)
                dlg.Centre()

                if dlg.ShowModal() == wx.ID_OK:
                    filename = wx.GetApp().frame.project.project_path + '/' + os.path.basename(dlg.GetFile())
                    shutil.copyfile(dlg.GetFile(), filename)

                else:
                    self.iec.editorCtrl.PopDown()
                    newanimdlg.Destroy()
                    return

                shutil.rmtree(tempdir, ignore_errors=True)

            nom = get_parent_class(self.iec.resource.__class__)
            classe = res.get_class(nom)
            c = classe.add_class(name, ["Animation"], [
                           "filenames = [" + repr(filename) + "]\n"])
            res.ast_has_changed = True
            res.topy()
            wx.GetApp().gns.run(res.listing)
            wx.GetApp().artub_frame.update_treeitem(res)
            self.iec.editorCtrl.SetValue(nom + '.' + name)
            if os.name != "posix": # Hack
                self.iec.editorCtrl.PopDown()
            self.iec.propEditor.inspectorPost(False)

        newanimdlg.Destroy()

    def on_button(self, evt):
        self.iec.editorCtrl.SetValue(evt.GetEventObject().image)
        self.iec.editorCtrl.PopDown()
        self.iec.propEditor.inspectorPost(False)
        evt.Skip()

from    wx.lib import masked
import  wx.lib.colourselect as  csel

class FloatCtrlIEC(InspectorEditorControl):
    def createControl(self, parent, value, idx, sizeX, constraints):
        self.editorCtrl = masked.NumCtrl(
                                parent, pos=(-2, idx*oiLineHeight-2),
                                size=(sizeX, oiLineHeight+3), value=value,
                                integerWidth=constraints[0],
                                fractionWidth=constraints[1], allowNegative=constraints[2],
                                min=constraints[3], max=constraints[4],
                                limited=constraints[5], autoSize=constraints[6])

    def getValue(self):
        self.value = self.editorCtrl.GetValue()
        return self.value

class ColorIEC(InspectorEditorControl):
    def createControl(self, parent, value, idx, sizeX, style=wx.TE_PROCESS_ENTER):
        # r = value[0] * 255
        # g = value[1] * 255
        # b = value[2] * 255
        r = value[0]
        g = value[1]
        b = value[2]
        self.parent = parent
        self.sizeX = sizeX
        self.idx = idx
        s = "(%d, %d, %d)" % (r, g, b)
        self.editorCtrl = csel.ColourSelect(parent, 2000, s, wx.Color(r, g, b),
                                            size=(sizeX, oiLineHeight+3),
                                            pos=(-2, idx*oiLineHeight-2))
        self.editorCtrl.Bind(csel.EVT_COLOURSELECT, self.on_select_color, id=2000)

    def getValue(self):
        self.value = self.editorCtrl.GetValue()
        self.value = (self.value.Red(), self.value.Green(), self.value.Blue())
        # self.value = (self.value.Red() / 255., self.value.Green() / 255., self.value.Blue() / 255.)
        return self.value

    def on_select_color(self, evt):
        value = evt.GetValue()
        self.editorCtrl.Destroy()
        s = "(%d, %d, %d)" % (value[0], value[1], value[2])
        self.editorCtrl = csel.ColourSelect(self.parent, 2000, s, wx.Color(value[0], value[1], value[2]),
                                            size=(self.sizeX, oiLineHeight+3),
                                            pos=(-2, self.idx*oiLineHeight-2))
        self.editorCtrl.Refresh()
        self.propEditor.inspectorPost(False)

class AnimationIEC(InspectorEditorControl):
    def createControl(self, parent, value, idx, sizeX, style=wx.TE_PROCESS_ENTER):
        self.editorCtrl = pop.PopupControl(parent, -1, pos = (0,0), size = (sizeX,oiLineHeight))
        self.win = wx.Window(self.editorCtrl, wx.NewId(),
         style = wx.TAB_TRAVERSAL | wx.SUNKEN_BORDER, size = (-1, -1)) # (200,200))
        self.win.Show(False)
        self.editorCtrl.SetDimensions(-2, idx*oiLineHeight-2,
         sizeX, oiLineHeight+3)

        self.cal = AnimationPanel(self.win, self, (0,0))
        self.cal.resource = self.resource

        self.editorCtrl.SetValue(value)
        self.editorCtrl.SetPopupContent(self.win)
        InspectorEditorControl.createControl(self)
        self.cal.create_buttons()

        wx.EVT_KILL_FOCUS(self.cal, self.on_left_button_down)

    def on_left_button_down(self, evt):
        # self.editorCtrl.PopDown()
        evt.Skip()

    def getValue(self):
        if self.editorCtrl:
            return self.editorCtrl.GetValue()

    def setValue(self, value):
        if self.editorCtrl:
            self.editorCtrl.SetLabel(value)
            self.editorCtrl.SetValue(self.truefalseMap[True] == value)

    def OnSelect(self, event):
        if event.IsChecked():
            self.setValue(self.truefalseMap[event.IsChecked()])

        InspectorEditorControl.OnSelect(self, event)

{'PIL.Image': 'PIL.Image', 'tempfile': 'tempfile', 'exceptions': 'exceptions', 'shutil': 'shutil'}


import copy
from pathlib import Path
from loguru import logger

import numpy as np
import torch
import wandb
from einops import rearrange
from pytorch_lightning import Callback
from pytorch_lightning.callbacks import Callback
from dataset.ds_utils.dataset_common_utils import need_to_upsample256
from diffusion_utils.taokit.wandb_utils import wandb_scatter_fig
from eval.test_exps.common_stuff import sampling_cond_str
from diffusion_utils.taokit.vis_utils import upsample_pt

def update_cfg_dict(_kwargs, update_info_dict, cfg_name):
    _kwargs_new = copy.deepcopy(_kwargs)
    _kwargs_new.update(update_info_dict)
    logger.warning("{}: {}".format(cfg_name, _kwargs_new))
    return _kwargs_new

def _get_wandbimg(samples):
    assert isinstance(
        samples, torch.Tensor
    )  # if it is tensor, split along first dimension
    # n_log_step, n_row, C, H, W
    denoise_grid = rearrange(samples, "n b c h w -> b n c h w")
    denoise_grid = rearrange(denoise_grid, "b n c h w -> (b n) c h w")
    wandb_list = [wandb.Image(_i.float()) for _i in denoise_grid]
    return wandb_list

class ImageLogger(Callback):
    def __init__(
        self,
        batch_frequency,
        max_images,
        disabled=False,
        log_on_batch_idx=False,
        log_first_step=False,
    ):
        super().__init__()

        self.batch_frequency = batch_frequency
        self.max_images = max_images

        self.disabled = disabled
        self.log_on_batch_idx = log_on_batch_idx
        self.log_first_step = log_first_step

    def log_img(self, pl_module, batch, batch_idx, split="train"):
        check_idx = batch_idx if self.log_on_batch_idx else pl_module.global_step
        is_train = pl_module.training#important issue!!!

        if self.check_frequency(check_idx) and self.max_images > 0:
            if is_train:
                pl_module.eval()

            with torch.no_grad():
                if hasattr(pl_module, "log_images"):
                    batch = pl_module.log_images(batch)
                    self.log_wandb(pl_module, batch=batch)
                else:
                    logger.warning(
                        "log_images function is missing in pl_module, skip the vis.."
                    )

            if is_train:
                pl_module.train()

    def log_sample_and_prog(
        self, log, samples, inter_dict, _key, prog_vis_num, need_to_upsample256=False
    ):
        log[f"{_key}_max"] = samples.max().item()
        log[f"{_key}_min"] = samples.min().item()
        if need_to_upsample256:
            samples = [wandb.Image(upsample_pt(_i).float()) for _i in samples]
        else:
            samples = [wandb.Image(_i.float()) for _i in samples]
        log[_key] = samples

        # pred_x0_unclipped
        if "pred_x0_unclipped_max" in inter_dict:
            prog_pred_x0_unclipped_max = inter_dict["pred_x0_unclipped_max"]
            prog_pred_x0_unclipped_min = inter_dict["pred_x0_unclipped_min"]
            timestep = len(prog_pred_x0_unclipped_max)
            max_list = [prog_pred_x0_unclipped_max[i] for i in range(timestep)]
            min_list = [prog_pred_x0_unclipped_min[i] for i in range(timestep)]
            x_list = list(range(timestep))
            log.update(
                wandb_scatter_fig(
                    x_list=x_list, y_list=max_list, dict_key=f"{_key}_unclipped_max"
                )
            )
            log.update(
                wandb_scatter_fig(
                    x_list=x_list, y_list=min_list, dict_key=f"{_key}_unclipped_min"
                )
            )

        # progressive
        prog = inter_dict["pred_x0"]  # [timestep_vis, b, 3 ,w, h]
        prog = prog[
            :, :prog_vis_num
        ]  # [timestep_vis, b, 3 ,w, h], truncate to progressive_vis_num
        prog_wandblist = _get_wandbimg(prog)
        log[f"{_key}_prog"] = prog_wandblist
        return log

    def log_wandb(self, pl_module, batch, vis_num=16, prog_vis_num=9):
        logdict_wandb = dict()
        vis_num = min(len(batch["image"]), vis_num)
        logdict_wandb["inputs"] = batch["image"][:vis_num].to(
            pl_module.device)  # [B,3,W,H]
        _dataset = pl_module.trainer.datamodule.train_dataloader().dataset
        try:
            captions = [f"{_dataset.label_list[_dataset.targets[_id]]}:{_dataset.targets[_id]}:{_id}" for _id in batch['id'][:vis_num]]
        except:
            captions = ['dump caption'] * vis_num
        logdict_wandb["inputs"] = [wandb.Image(_img.float(), caption=_caption) for _img,_caption in zip(logdict_wandb["inputs"],captions)]
        pl_module.logger.experiment.log({"imagelogger/inputs_vis": logdict_wandb["inputs"]})

        batch = {k:v[:vis_num].to(pl_module.device) for k,v in batch.items()}

        condition_kwargs, sampling_kwargs, fid_kwargs = pl_module.get_default_config()
        ######         update some common params #################################
        fid_kwargs.update(
            save_dir=str(Path(pl_module.trainer.logdir).expanduser().resolve())
        )
        sampling_kwargs.update(
            dict(
                num_timesteps=pl_module.hparams.model.num_timesteps_imagelogger,
                sampling_method=pl_module.hparams.model.sampling_imagelogger,
            )
        )

        with pl_module.ema_scope("Plotting Native Sampling"):
            cond_scale_list = list(set([0, pl_module.hparams.cond_scale]))
            for cond_scale in cond_scale_list:
                condition_cfg_now = update_cfg_dict(
                    condition_kwargs,
                    update_info_dict=dict(cond_scale=cond_scale),
                    cfg_name="condition_kwargs",
                )
                sampling_cfg_now = update_cfg_dict(
                    sampling_kwargs,
                    update_info_dict=dict(return_inter_dict=True),
                    cfg_name="sampling_kwargs",
                )
                samples, inter_dict = pl_module.sampling_progressive(
                    batch_size=len(batch["image"]),
                    batch_data=batch,
                    condition_kwargs=condition_cfg_now,
                    sampling_kwargs=sampling_cfg_now,
                )

                logdict_wandb = self.log_sample_and_prog(
                    log=logdict_wandb,
                    _key=f"native_{sampling_cond_str(sampling_cfg_now,condition_cfg_now)}",
                    samples=samples,
                    inter_dict=inter_dict,
                    prog_vis_num=prog_vis_num,
                    need_to_upsample256=need_to_upsample256(
                        pl_module.hparams.data.name
                    ),
                )

        #########################
        wandb_dict = dict()
        for k, v in logdict_wandb.items():
            if isinstance(
                v, list
            ):  # already constructed a list of wandb.Image, so directly use it here.
                wandb_dict[k] = v
            elif isinstance(v, wandb.Image):
                wandb_dict[k] = v
            else:
                wandb_dict[k] = v

        wandb_dict = {f"imagelogger/{k}": v for k, v in wandb_dict.items()}
        pl_module.logger.experiment.log(wandb_dict)

    def check_frequency(self, check_idx):
        if ((check_idx % self.batch_frequency) == 0) and (
            check_idx > 0 or self.log_first_step
        ):
            return True
        else:
            return False

    def on_train_batch_end(self, trainer, pl_module, outputs, batch, batch_idx):
        if not self.disabled and (pl_module.global_step > 0 or self.log_first_step):
            self.log_img(pl_module, batch, batch_idx, split="train")


from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
import time
import os
import glob

def waitElement(driver, element, by=By.ID):
    return WebDriverWait(driver, 30).until(
        EC.element_to_be_clickable((by, element))
    )

def waitElementDisable(driver, element, by=By.ID):
    return WebDriverWait(driver, 30).until(
        EC.invisibility_of_element_located((by, element))
    )

def waitDownload(path):
    if not os.path.exists(path):
        os.makedirs(path)

    tempfiles = 0
    while tempfiles == 0:
        time.sleep(1)
        for fname in os.listdir(path):
            if "crdownload" in fname or "tmp" in fname:
                tempfiles = 0
                break
            else:
                tempfiles = 1

def deleteTemporals(path):
    if not os.path.exists(path):
        return
    for fname in os.listdir(path):
        os.remove(os.path.join(path, fname))

def getMostRecentFile(path, _filter=None):
    list_of_files = glob.glob(fr'{path}/*')
    list_of_files = _filter(
        list_of_files) if _filter is not None else list_of_files
    return max(list_of_files, key=os.path.getctime)


import sys
sys.path.append('../..')
from benchmark.rtl import *
from benchmark.simulator import State
from misc.benchmark_utils import *
from typing import List

name = "GrilledCheese"

prompts = [
    "Compile a list of ingredients for grilled cheese. Go to Zarko's office, and ask him which of them he has. Come back and tell me what he does not have.",
    "Create a list of ingredients needed to make grilled cheese. Visit Zarko's office and inquire which of them he possesses. Return and inform me of the items he does not have.",
    "Put together a compilation of grilled cheese ingredients. Head to Zarko's office, inquire about his available ingredients, and then update me on what he does not have.",
    "Compile the necessary ingredients for grilled cheese. Go to Zarko's office, ask him about his available items, and then report back to me which items he does not have.",
    "Put together a list of grilled cheese ingredients. Head to Zarko's office and inquire about his available ingredients. Return and inform me of what he is missing."
]

# construct state
state0 = State().addLocation("lobby").addLocation("zarko's office").addLocation("start_loc") \
        .addAgent(".*", "zarko's office", [r".*"]) \
        .addRobotLocation("start_loc")

# helper checks
def generic_con(trace: Trace, VERBOSE: bool = False) -> bool:
    contain_cheese = contain_words(["cheese"])
    contain_bread = contain_words(["bread"])

    go_to_check = not trace.Exists(GoTo(r"lobby")) and trace.Exists(GoTo(r"zarko's office"))
    ask_at_zarko_cheese = trace.ActAtFirst("zarko's office", Ask(r".*", contain_cheese)) or \
                          trace.ActAtFirst("zarko's office", Ask(contain_cheese, r".*"))
    ask_at_zarko_bread = trace.ActAtFirst("zarko's office", Ask(r".*", contain_bread)) or \
                         trace.ActAtFirst("zarko's office", Ask(contain_bread, r".*"))
    say_at_start = trace.AfterFirst(GoTo(r"zarko's office")).ActAtFirst("start_loc", Say(r".*"))

    if VERBOSE:
        print("==== generic_con ====")
        print_debug("go_to_check", go_to_check, [label_L])
        print_debug("ask_at_zarko_cheese", ask_at_zarko_cheese, [label_OWK, label_A, label_AL])
        print_debug("ask_at_zarko_bread", ask_at_zarko_bread, [label_OWK, label_A, label_AL])
        print_debug("say_at_start", say_at_start, [label_IT, label_S, label_AL])

    return go_to_check and ask_at_zarko_cheese and ask_at_zarko_bread and say_at_start

def test0(trace_elements: List[TraceElement], VERBOSE: bool = False) -> bool:
    trace = Trace(trace_elements)
    generic = generic_con(trace, VERBOSE=VERBOSE)

    return generic

tests = [
    {
        "state" : state0,
        "test" : test0,
        "timeout": False
    }
]


# coding: utf-8
from django.db import models
from django.utils.translation import ugettext_lazy as _

class Article(models.Model):
    title = models.CharField(max_length=128)
    content = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        verbose_name = _('Article')
        verbose_name_plural = _('Articles')

    def __str__(self):
        return self.title


import csv
import sys
from collections import defaultdict
from os.path import basename

if __name__ == "__main__":

    with open(sys.argv[1]) as file:
        rdr = csv.DictReader(file)
        ctrs = {attr: defaultdict(int) for attr in rdr.fieldnames}
        miss = {attr: 0 for attr in rdr.fieldnames}
        cnt = 0
        for record in rdr:
            cnt += 1
            for attr in rdr.fieldnames:
                if record[attr] == "":
                    miss[attr] += 1
                else:
                    ctrs[attr][record[attr]] += 1

    pct = lambda n: f"{100*n:.3g}%"
    fmt = lambda values: ", ".join(f"`{v[0]}` ({pct(v[1]/cnt)})" for v in values)

    print(f"### `{basename(sys.argv[1])}` ({cnt} records)\n")
    print("| Field | Description | Values | Missing |\n| --- | --- | --- | --- |")
    for attr, ctr in ctrs.items():
        values = sorted(ctr.items(), key=lambda x: x[1], reverse=True)
        values = values[:10]
        print(f"| `{attr}` | | {fmt(values)} | {pct(miss[attr]/cnt)} |")


"""
    @Difficulty : M 2
    @Status     : AC
    @Time       : 2021/1/24 10:29
    @Author     : Chen Runwen
"""
from typing import List

class Solution:
    def kthLargestValue(self, matrix: List[List[int]], k: int) -> int:
        nums = []
        m, n = len(matrix), len(matrix[0])
        for j in range(1, n):
            for i in range(m):
                matrix[i][j] ^= matrix[i][j-1]
        nums += matrix[0]
        for i in range(1, m):
            for j in range(n):
                matrix[i][j] ^= matrix[i-1][j]
            nums += matrix[i]
        nums.sort(reverse=True)
        return nums[k-1]

if __name__ == '__main__':
    matrix = [[5, 2], [1, 6]]
    k = 4
    print(Solution())

contests/20210124/5663. 找出第 K 大的异或坐标值.py

5663. 找出第 K 大的异或坐标值.py


from model import *
import MCTS
import env
import chess
import chess.pgn
import chess.uci
import os
from tqdm import tqdm
from time import time

agent = load_model("long_ResNet64.json", "long_ResNet64.h5")
#Init Stockfish
stockfish = chess.uci.popen_engine("stockfish")
stockfish.uci()

games = 25 #Number of games to play
simulations = 1600 #Node count per move

white = True
start = time()
for it in tqdm(range(games)):
	board = chess.Board()
	turn = white
	while not board.is_game_over():
		if turn:
			stockfish.position(board)
			stock_move = stockfish.go(movetime=100) #Set stockfish to 10 moves per second
			board.push(stock_move[0])
		else:
			policy, values, _, _ = MCTS.muMCTS(board, board.turn, agent, simulations=simulations)
			index = np.random.choice(1968, p=policy)
			uci_move = env.MOVES[index]
			board.push_uci(uci_move)

		turn = not turn

	#Save a pgn of the game for analysis later
	game = chess.pgn.Game().from_board(board)
	game.headers["Event"] = "Game " + str(it+1)
	game.headers["Site"] = "RTX Titan & AMD ThreadRipper"
	game.headers["Round"] = "Testing (1600 Nodes vs 100ms)"

	if white:
		game.headers["White"] = "Stockfish 12"
		game.headers["Black"] = "Basilisk"
	else:
		game.headers["White"] = "Basilisk"
		game.headers["Black"] = "Stockfish 12"

	name = "games/testing/1600N_c=10.0/" + str(it+1) + ".pgn"
	directory = "games/testing/1600N_c=10.0/"

	try:
		os.mkdir(directory)
	except FileExistsError:
		#directory already exists
		pass

	new_pgn = open(name, "w", encoding="utf-8")
	exporter = chess.pgn.FileExporter(new_pgn)
	game.accept(exporter)

	white = not white #Alternate who plays white

print("Time (Minutes)", (time() - start)/60)


import pandas as pd
import matplotlib.pyplot as plt
import datetime
# A. Load data for a specific time period
def load_data(start_date, end_date):
    # Read the CSV file
    df = pd.read_csv('data.csv')

    # Convert date_created column to datetime format
    df['date_created'] = pd.to_datetime(df['date_created'], utc=True)

    # Filter the data for the specified time period
    mask = (df['date_created'].dt.date >= start_date) & (df['date_created'].dt.date <= end_date)
    filtered_df = df[mask]

    return filtered_df

# B. Calculate statistics for the selected dates
def calculate_statistics(df):
    # Calculate minimum, maximum, mean, standard deviation, and variance
    min_poso = df['poso'].min()
    max_poso = df['poso'].max()
    mean_poso = df['poso'].mean()
    std_poso = df['poso'].std()
    var_poso = std_poso ** 2

    return min_poso, max_poso, mean_poso, std_poso, var_poso

# C. Calculate total outstanding bills per apartment building
def calculate_debts_per_building(df):
    # Group the data by buildingID and sum the outstanding amounts
    debts_per_building = df.groupby('buildingID')['poso'].sum()

    return debts_per_building

# D. Generate required graphs
def generate_graphs(data):
    # Total debts per apartment building
    debts_per_building = data.groupby('buildingID')['poso'].sum()
    plt.figure(figsize=(10, 5))
    plt.bar(debts_per_building.index, debts_per_building.values)
    plt.xlabel('buildingID')
    plt.ylabel('Total Debts')
    plt.title('Total Debts per Apartment Building')
    plt.show()

    # Debts per month/year
    data['date_created'] = pd.to_datetime(data['date_created'])
    debts_per_month_year = data.groupby(data['date_created'].dt.to_period('M'))['poso'].sum()
    plt.figure(figsize=(10, 5))
    plt.plot(debts_per_month_year.index.astype(str), debts_per_month_year.values)
    plt.xlabel('Month/Year')
    plt.ylabel('Total Debts')
    plt.title('Debts per Month/Year')
    plt.xticks(rotation=45)
    plt.show()

    # Number of expenses per apartment building
    expenses_per_building = data.groupby('buildingID').size()
    plt.figure(figsize=(10, 5))
    plt.bar(expenses_per_building.index, expenses_per_building.values)
    plt.xlabel('buildingID')
    plt.ylabel('Number of Expenses')
    plt.title('Number of Expenses per Apartment Building')
    plt.show()

    # Total debts per apartment building per month
    debts_per_building_month = data.groupby(['buildingID', data['date_created'].dt.to_period('M')])['poso'].sum()
    debts_per_building_month.unstack(level=0).plot(kind='bar', figsize=(10, 5))
    plt.xlabel('Month')
    plt.ylabel('Total Debts')
    plt.title('Total Debts per Apartment Building per Month')
    plt.legend(title='buildingID')
    plt.show()

def get_time_period():
    while True:
        try:
            start_date_str = input("Enter the start date (YYYY-MM-DD): ")
            start_date = datetime.datetime.strptime(start_date_str, "%Y-%m-%d").date()

            end_date_str = input("Enter the end date (YYYY-MM-DD): ")
            end_date = datetime.datetime.strptime(end_date_str, "%Y-%m-%d").date()

            if start_date > end_date:
                print("Start date cannot be greater than end date. Please try again.")
                continue

            return start_date, end_date

        except ValueError:
            print("Invalid date format. Please enter the date in the format YYYY-MM-DD.")

def print_stats(data):
    min_poso, max_poso, mean_poso, std_poso, var_poso = calculate_statistics(data)
    print('Statistics for the selected dates:')
    print('Minimum Poso:', min_poso)
    print('Maximum Poso:', max_poso)
    print('Mean Poso:', mean_poso)
    print('Standard Deviation:', std_poso)
    print('Variance:', var_poso)
# Usage example:
start_date, end_date = get_time_period()

data = load_data(start_date, end_date)
print_stats(data)
# Generate the required graphs
generate_graphs(data)


from flask import render_template, Flask

from selenium import webdriver
from selenium.webdriver.common.keys import Keys
import time

app = Flask(__name__)

@app.route("/")
def output():
    driver = webdriver.Chrome("/Users/Florian/Downloads/chromedriver")
    driver.get("https://themilzandgryllzshow.podbean.com/")
    time.sleep(1)
    elem = driver.find_element_by_id("home_visitor_stat")
    print(elem.text)
    txt = elem.text
    driver.close()
    return render_template('homepage.html', console_gave = [txt])

if __name__ == "__main__":
	app.run()

#python script4.py
#python ~/Documents/6_Webdesign/09_Dreamweaver/themilzandgryllzshow/script4.py
# cd Documents/6_Webdesign/09_Dreamweaver/themilzandgryllzshow