import csv
import json
import datetime
import functools

# 1.NFT总量 （宝箱+赠送+open宝箱）
# 2.NFT mint数量 （卖出的宝箱+赠送）
# 3.NFT宝箱已开和未开数量（总数即可)
# 4.NFT持有人数量排序 (已购的宝箱+赠送)

mintTable = []
nftTable = []
nftTransferTable = []

contracts = lambda : json.load(open('contract.json', 'r'))

stat = {
    'nft_total': 0,
    'nft_mint': 0,
    'blindBox': {
        'user_hold': 0,
        'open': 0,
        'total': 0,
        'merchant_hold': 0
    },
    'nft_rank': [
    ],
    'nft_hero_box_rank': [
    ]
}

EMPTY_ADDRESS = '0x0000000000000000000000000000000000000000'.lower()
MERCHANT_ADDRESS = '0x14ea40648fc8c1781d19363f5b9cc9a877ac2469'.lower()

merchant_box = {}

def exportMintTable():
    rows = []
    with open('mint.raw.csv', 'w', newline='') as f:
        writer = csv.writer(f)
        writer.writerows(rows)

def exportNftTable():
    rows = []
    with open('nft.raw.csv', 'w', newline='') as f:
        writer = csv.writer(f)
        writer.writerows(rows)

def exportNftTransferTable():
    rows = []
    with open('nft_transfer.raw.csv', 'w', newline='') as f:
        writer = csv.writer(f)
        writer.writerows(rows)

def exportNftSumTable():
    rows = []
    for nft in nftTable:
        rows.push(
            (
                nft['token_id'],
                nft['token_type'],
                nft['creator_address'],
                nft['owner_address'],
                nft['tags'],
                datetime.datetime.fromtimestamp(nft['modifytime'])
            )
        )
    with open('nft.sum.csv', 'w', newline='') as f:
        writer = csv.writer(f)
        writer.writerows(rows)

def statData():
    userNftNums = {}
    boxUser = {}
    for row in nftTransferTable:
        rawData = json.loads(row['raw_data'])
        if rawData['address'].lower() == '0x8444404bD78089A5a6d5Cc57f7Df8924f2DdACB4'.lower() and \
           rawData['event'] == 'Transfer':
            fromAddress = rawData['returnValues']['from'].lower()
            toAddress = rawData['returnValues']['to'].lower()
            tokenId = rawData['returnValues']['tokenId'].lower()
            if fromAddress == EMPTY_ADDRESS:
                stat['nft_total'] += 1
                stat['blindBox']['total'] += 1
                boxUser[tokenId] = toAddress
            if toAddress == EMPTY_ADDRESS:
                stat['blindBox']['open'] += 1
                del boxUser[tokenId]
            else:
                boxUser[tokenId] = toAddress
            if toAddress == MERCHANT_ADDRESS:
                merchant_box[rawData['returnValues']['tokenId']] = 1
            if fromAddress == MERCHANT_ADDRESS:
                del merchant_box[rawData['returnValues']['tokenId']]

    #end for
    assert(len(boxUser) == stat['blindBox']['total'] - stat['blindBox']['open'])
    stat['blindBox']['merchant_hold'] = len(merchant_box)
    stat['blindBox']['user_hold'] = stat['blindBox']['total'] - stat['blindBox']['open'] - stat['blindBox']['merchant_hold']
    for key, value in boxUser.copy().items():
        if value == MERCHANT_ADDRESS:
            del boxUser[key]
    assert(len(boxUser) == stat['blindBox']['user_hold'])
    userHoldNums = {}
    userHoldHeroBoxNums = {}
    for key, value in boxUser.items():
        userHoldNums[value] = 1
    for row in nftTable:
        ownerAddress = row['owner_address'].lower()
        if ownerAddress != EMPTY_ADDRESS and ownerAddress != '':
            if row['token_type'] != '4':
                stat['nft_total'] += 1
                stat['nft_mint'] += 1
                if ownerAddress not in userHoldNums:
                    userHoldNums[ownerAddress] = 1
                else:
                    userHoldNums[ownerAddress] += 1
            if row['token_type'] == '1':
                if ownerAddress not in userHoldHeroBoxNums:
                    userHoldHeroBoxNums[ownerAddress] = {
                        'hero': 1,
                        'box': 0,
                        'nftNum': 1
                    }
                else:
                    userHoldHeroBoxNums[ownerAddress]['hero'] += 1
                    userHoldHeroBoxNums[ownerAddress]['nftNum'] += 1
            if row['token_type'] == '4':
                if ownerAddress not in userHoldHeroBoxNums:
                    userHoldHeroBoxNums[ownerAddress] = {
                        'hero': 0,
                        'box': 1,
                        'nftNum': 1
                    }
                else:
                    userHoldHeroBoxNums[ownerAddress]['box'] += 1
                    userHoldHeroBoxNums[ownerAddress]['nftNum'] += 1
    #end for
    userHoldList = []
    for key, value in userHoldNums.items():
        if value > 1:
            userHoldList.append(
                {
                    'address': key,
                    'nftNum': value
                }
            )
    #end for
    def cmpFunc(a, b):
        if a['nftNum'] > b['nftNum']:
            return -1
        elif a['nftNum'] < b['nftNum']:
            return 1
        else:
            return 0
    stat['nft_rank'] = sorted(userHoldList, key=functools.cmp_to_key(cmpFunc))
    for i in range(len(stat['nft_rank'])):
        stat['nft_rank'][i]['rank'] = i + 1
    stat['nft_mint'] += stat['blindBox']['merchant_hold']
    rankList = [
        (
            'address',
            'nftNum',
            'rank'
        )
    ]
    for item in stat['nft_rank']:
        rankList.append(
            (
                item['address'],
                item['nftNum'],
                item['rank']
            )
        )
    with open('nft_rank.csv', 'w', newline='') as f:
        writer = csv.writer(f)
        writer.writerows(rankList)

    #
    userHoldHeroBoxList = []
    for key, value in userHoldHeroBoxNums.items():
        if value['nftNum'] > 1:
            userHoldHeroBoxList.append(
                {
                    'address': key,
                    'hero': value['hero'],
                    'box': value['box'],
                    'nftNum': value['nftNum']
                }
            )
    #end for
    def cmpFunc(a, b):
        if a['nftNum'] > b['nftNum']:
            return -1
        elif a['nftNum'] < b['nftNum']:
            return 1
        else:
            return 0
    stat['nft_hero_box_rank'] = sorted(userHoldHeroBoxList, key=functools.cmp_to_key(cmpFunc))
    for i in range(len(stat['nft_hero_box_rank'])):
        stat['nft_hero_box_rank'][i]['rank'] = i + 1
    rankList = [
        (
            'address',
            'hero',
            'box',
            'nftNum',
            'rank'
        )
    ]
    for item in stat['nft_hero_box_rank']:
        rankList.append(
            (
                item['address'],
                item['hero'],
                item['box'],
                item['nftNum'],
                item['rank']
            )
        )
    with open('nft_hero_box_rank.csv', 'w', newline='') as f:
        writer = csv.writer(f)
        writer.writerows(rankList)
    print(json.dumps(stat, indent=4))

def loadData():
    def rowToObj(row, fields):
        #print(row)
        #print(len(row), len(fields))
        assert(len(row) == len(fields))
        obj = {}
        for idx in range(len(fields)):
            obj[fields[idx]] = row[idx]
        return obj
    def loadCsv(data, fileName, fields):
        print(fileName)
        with open(fileName, "r", encoding="utf-8") as f:
            reader = csv.reader(f, delimiter='#')
            for row in reader:
                data.append(rowToObj(row, fields))
    loadCsv(mintTable, 't_mint.txt',  [
        'idx',
        'unikey',
        'account',
        'game_id',
        'ignore',
        'bc_minted',
        'bc_mint_txhash',
        'bc_mint_itemid',
        'bc_mint_tokenid',
        'bc_mint_token_type',
        'bc_mint_tags',
        'bc_mint_count',
        'bc_mint_time',
        'bc_mint_prepare_block_number',
        'bc_mint_success_block_number',
        'bc_mint_confirm_time',
        'suspend',
        'suspend_reason',
        'done',
        'createtime',
        'modifytime'
    ])
    loadCsv(nftTable, 't_nft.txt',  [
        'idx',
        'owner_address',
        'creator_address',
        'token_id',
        'token_type',
        'token_state',
        'game_id',
        'item_id',
        'deleted',
        'opened',
        'confirm_count',
        'confirm_block_number',
        'rand_attr',
        'tags',
        'createtime',
        'modifytime'
    ])
    loadCsv(nftTransferTable, 't_nft_transfer.txt',  [
        'idx',
        'token_id',
        'txhash',
        'block_number',
        'log_index',
        '_from',
        '_to',
        'raw_data',
        'owner_confirmed',
        'createtime',
        'modifytime'
    ])

loadData()
statData()
