import zXcat
import bXcat
from utils import *
from waiting import *
from time import sleep
import json
import os, sys
from pprint import pprint
from trades import Contract, Trade
import userInput

def check_p2sh(currency, address):
    if currency == 'bitcoin':
        print("Checking funds in Bitcoin p2sh")
        return bXcat.check_funds(address)
    else:
        print("Checking funds in Zcash p2sh")
        return zXcat.check_funds(address)

def create_htlc(currency, funder, redeemer, secret, locktime):
    if currency == 'bitcoin':
        sell_p2sh = bXcat.hashtimelockcontract(funder, redeemer, secret, locktime)
    else:
        sell_p2sh = zXcat.hashtimelockcontract(funder, redeemer, secret, locktime)
    return sell_p2sh

def fund_htlc(currency, p2sh, amount):
    if currency == 'bitcoin':
        txid = bXcat.fund_htlc(p2sh, amount)
    else:
        txid = zXcat.fund_htlc(p2sh, amount)
    return txid

def fund_buy_contract(trade):
    buy = trade.buyContract
    txid = fund_htlc(buy.currency, buy.p2sh, buy.amount)
    setattr(trade.buyContract, 'fund_tx', txid)
    save(trade)
    return txid

def fund_sell_contract(trade):
    sell = trade.sellContract
    txid = fund_htlc(sell.currency, sell.p2sh, sell.amount)
    setattr(trade.sellContract, 'fund_tx', txid)
    save(trade)
    return txid

def create_sell_p2sh(trade, secret, locktime):
    # CREATE SELL CONTRACT
    sell = trade.sellContract
    contract = create_htlc(sell.currency, sell.initiator, sell.fulfiller, secret, locktime)
    print("sell contract", contract)
    setattr(trade.sellContract, 'p2sh', contract['p2sh'])
    setattr(trade.sellContract, 'redeemScript', contract['redeemScript'])
    setattr(trade.sellContract, 'redeemblocknum', contract['redeemblocknum'])
    save(trade)

def create_buy_p2sh(trade, secret, locktime):
    ## CREATE BUY CONTRACT
    buy = trade.buyContract
    print("Now creating buy contract on the {0} blockchain where you will wait for the buyer to send funds...".format(buy.currency))
    print("HTLC DETAILS", buy.currency, buy.fulfiller, buy.initiator, secret, locktime)
    buy_contract = create_htlc(buy.currency, buy.fulfiller, buy.initiator, secret, locktime)
    print("Buy contract", buy_contract)

    setattr(trade.buyContract, 'p2sh', buy_contract['p2sh'])
    setattr(trade.buyContract, 'redeemScript', buy_contract['redeemScript'])
    setattr(trade.buyContract, 'redeemblocknum', buy_contract['redeemblocknum'])
    print("Now contact the buyer and tell them to send funds to this p2sh: ", trade.buyContract.p2sh)

    save(trade)

# we try to redeem contract with secret
# we try to redeem revertcontract with time lock
# returns True if at least one redeem succeeded
def redeem_p2sh(contract, secret, revertcontract):

    currency = contract.currency
    res = False 
    revert_currency = revertcontract.currency

    if (currency == 'bitcoin' and bXcat.still_locked(contract)):
        print("trying to redeem btc with secret:")
        try:
            res = bXcat.redeem_with_secret(contract, secret)
        except Exception:
            print("Failed - you might not have the correct secret - perhaps because the seller has not redeemed the buy contract correctly")
        if(res): print("You have redeemed {0} {1}!".format(contract.amount, contract.currency))
    
    if (currency == 'zcash' and zXcat.still_locked(contract)):
        print("trying to redeeming zec with secret:")
        try:
            res = zXcat.redeem_with_secret(contract, secret)
        except Exception:
            print("Failed - you might not have the correct secret - perhaps because the seller has not redeemed the buy contract correctly")
        if(res): print("You have redeemed {0} {1}!".format(contract.amount, contract.currency))
    if (revert_currency == 'bitcoin'):
        if(bXcat.still_locked(revertcontract)):
            print('too early for redeeminng with time lock on btc chain')
        else:        
            print("trying to redeeming btc with timelock:")
            try:
                res = bXcat.redeem_after_timelock(revertcontract)
            except Exception:
                print("Failed - the other party might have redeemed the fund tx on the btc chain with the secret by now") 
            if(res): print("You have redeemed {0} {1}!".format(revertcontract.amount, revertcontract.currency))   
    if (revert_currency == 'zcash'):
        if(zXcat.still_locked(revertcontract)):
            print('too early for redeeminng with time lock on zcash chain')
        else:        
            print("trying to redeeming zec with timelock:")
            try:
                res = zXcat.redeem_after_timelock(revertcontract)
            except Exception:
                print("Failed - the other party might have redeemed the fund tx on the zcash chain with the secret by now") 
            if(res): print("You have redeemed {0} {1}!".format(revertcontract.amount, revertcontract.currency))   

    return res

def print_trade(role):
    print("\nTrade status for {0}:".format(role))
    trade = get_trade()
    pprint(trade)

# ####  Main functions determining user flow from command line
# def buyer_redeem(trade):
#     userInput.authorize_buyer_redeem(trade)
#     if trade.sellContract.get_status() == 'redeemed':
#         print("You already redeemed the funds and acquired {0} {1}".format(trade.sellContract.amount, trade.sellContract.currency))
#         exit()
#     else:
#         # Buyer redeems seller's funded tx
#         p2sh = trade.sellContract.p2sh
#         currency = trade.sellContract.currency
#         # Buy contract is where seller disclosed secret in redeeming
#         if trade.buyContract.currency == 'bitcoin':
#             secret = bXcat.parse_secret(trade.buyContract.redeem_tx)
#         else:
#             secret = zXcat.parse_secret(trade.buyContract.redeem_tx)
#         print("Found secret in seller's redeem tx", secret)
#         redeem_tx = redeem_p2sh(trade.sellContract, secret)
#         setattr(trade.sellContract, 'redeem_tx', redeem_tx)
#         save(trade)
#     exit()

# def seller_redeem(trade):
#     buy = trade.buyContract
#     userInput.authorize_seller_redeem(buy)

#     if trade.sellContract.get_status() == 'redeemed':
#         print("You already redeemed the funds and acquired {0} {1}".format(buy.amount, buy.currency))
#         exit()
#     else:
#         # Seller redeems buyer's funded tx (contract in p2sh)
#         secret = userInput.retrieve_password()
#         tx_type, txid = redeem_p2sh(trade.buyContract, secret)
#         setattr(trade.buyContract, tx_type, txid)
#         save(trade)
#         print("You have redeemed {0} {1}!".format(buy.amount, buy.currency))
#         print_trade('seller')

# def buyer_fulfill(trade):
#     buy = trade.buyContract
#     sell = trade.sellContract
#     buy_p2sh_balance = check_p2sh(buy.currency, buy.p2sh)
#     sell_p2sh_balance = check_p2sh(sell.currency, sell.p2sh)

#     if buy_p2sh_balance == 0:
#         userInput.authorize_buyer_fulfill(sell_p2sh_balance, sell.currency, buy_p2sh_balance, buy.currency)
#         print("Buy amt:", buy.amount)
#         txid = fund_buy_contract(trade)
#         print("Fund tx txid:", txid)
#     else:
#         print("It looks like you've already funded the contract to buy {1}, the amount in escrow in the p2sh is {0}.".format(buy_p2sh_balance, buy.currency))
#         print("Please wait for the seller to remove your funds from escrow to complete the trade.")
#     print_trade('buyer')

def seller_initiate(trade):
    # Get amounts
    amounts = userInput.get_trade_amounts()
    sell = amounts['sell']
    buy = amounts['buy']
    sell_currency = sell['currency']
    buy_currency = buy['currency']
    # Get addresses
    init_addrs = userInput.get_initiator_addresses()
    sell['initiator'] = init_addrs[sell_currency]
    buy['initiator'] = init_addrs[buy_currency]
    fulfill_addrs = userInput.get_fulfiller_addresses()
    sell['fulfiller'] = fulfill_addrs[sell_currency]
    buy['fulfiller'] = fulfill_addrs[buy_currency]
    # initializing contract classes with addresses, currencies, and amounts
    trade.sellContract = Contract(sell)
    trade.buyContract = Contract(buy)
    print(trade.sellContract.__dict__)
    print(trade.buyContract.__dict__)

    secret = userInput.create_password()
    # TODO: Implement locktimes and mock block passage of time
    sell_locktime = 5
    buy_locktime = 10 # Must be more than first tx

    create_sell_p2sh(trade, secret, sell_locktime)

    userInput.authorize_fund_sell(trade)

    txid = fund_sell_contract(trade)
    print("Sent")

    create_buy_p2sh(trade, secret, buy_locktime)
    print_trade('seller')

if __name__ == '__main__':
    print("ZEC <-> BTC XCAT (Cross-Chain Atomic Transactions)")
    print("=" * 50)

    trade = get_trade()

    if trade == None:
        htlcTrade = Trade()
        print("New empty trade")
    else:
        buyContract = Contract(trade['buy'])
        sellContract = Contract(trade['sell'])
        htlcTrade = Trade(buyContract=buyContract, sellContract=sellContract)

    try:
        if sys.argv[1] == 'new':
            erase_trade()
            role = 'seller'
            htlcTrade = Trade()
            print("Creating new XCAT transaction...")
        else:
            role = sys.argv[1]
            print("Your role in demo:", role)
    except:
        if trade == None:
            print("No active trades available.")
            res = input("Would you like to initiate a trade? (y/n) ")
            if res == 'y':
                role = 'seller'
            else:
                exit()
        else:
            print("Trade exists, run script as buyer or seller to complete trade.")
            exit()

    if htlcTrade.buyContract is not None and htlcTrade.sellContract is not None:
        if htlcTrade.sellContract.get_status() == 'redeemed' and htlcTrade.buyContract.get_status() == 'redeemed':
            print("This trade is already complete! Trade details:")
            pprint(trade)
            exit()

    if role == "seller":
        if htlcTrade.sellContract == None:
            seller_initiate(htlcTrade)
        elif htlcTrade.buyContract.get_status() == 'funded':
            seller_redeem(htlcTrade)
        elif htlcTrade.buyContract.get_status() == 'empty':
            print("Buyer has not yet funded the contract where you offered to buy {0}, please wait for them to complete their part.".format(htlcTrade.buyContract.currency))
    else:
        # Need better way of preventing buyer from having secret
        # if 'status' not in trade['buy'] and trade['sell']['status'] == 'funded':
        if htlcTrade.sellContract.get_status() == 'funded' and htlcTrade.buyContract.get_status() != 'redeemed':
            print("One active trade available, fulfilling buyer contract...")
            buyer_fulfill(htlcTrade)
            # How to monitor if txs are included in blocks -- should use blocknotify and a monitor daemon?
            # p2sh = trade['buy']['p2sh']
            # check_blocks(p2sh)
        elif htlcTrade.buyContract.get_status() == 'redeemed':
            # Seller has redeemed buyer's tx, buyer can now redeem.
            buyer_redeem(htlcTrade)
            print("XCAT trade complete!")

        # Note: there is some little endian weirdness in the bXcat and zXcat files, need to handle the endianness of txids better & more consistently
