import asyncio
import random

from traderion2.bot import TraderionBot, BID, ASK


# Set this to True for logging
DEBUG = False


def debug(debug_msg):
    if DEBUG:
        print(debug_msg)


class RandomBot(TraderionBot):
    """
    A bot that behaves (more or less) randomly. Its purpose is to showcase the API.
    """

    def __init__(self, username, password, room_id, loop_sleep=1):
        super().__init__(username, password, room_id, loop_sleep)

        self.counter = 0

    def on_room_status_change(self, old_status, new_status):
        debug('Room status changed: {} -> {}\n'.format(old_status, new_status))

    async def on_room_param_change(self, category, name, old_value, new_value):
        debug('Room param changed ({}, {}): {} -> {}\n'.format(category, name, old_value, new_value))

    async def on_new_client_call(self, client_call):
        debug('New client call: {}\n'.format(client_call))

        if client_call['client_price'] is None:
            market_price = self.api.get_market_prices(client_call['swift_id'])
            price = market_price['price']

            msg = await self.api.quote_client_call(client_call['id'], price)
            debug('quote_client_call returned {}\n'.format(msg))
        else:
            if random.randint(0, 1) == 0:
                msg = await self.api.accept_client_call(client_call['id'])
                debug('accept_client_call returned {}\n'.format(msg))
            else:
                msg = await self.api.decline_client_call(client_call['id'])
                debug('decline_client_call returned {}\n'.format(msg))

    async def on_client_call_accepted(self, client_call):
        debug('Client call accepted: {}\n'.format(client_call))

    async def on_client_call_declined(self, client_call):
        debug('Client call declined: {}\n'.format(client_call))

    async def on_client_call_expired(self, client_call):
        debug('Client call expired: {}\n'.format(client_call))

    async def on_new_ib_call(self, ib_call):
        debug('New ib call: {}\n'.format(ib_call))

        market_price = self.api.get_market_prices(ib_call['swift_id'])
        bid = market_price['bid']
        ask = market_price['ask']

        if random.randint(0, 1) == 0:
            bid = bid - 0.0010
            ask = bid + 0.0030
        else:
            ask = ask + 0.0010
            bid = ask - 0.0030

        msg = await self.api.quote_ib_call(ib_call['id'], bid, ask)
        debug('quote_ib_call returned {}\n'.format(msg))

    async def on_ib_call_answered(self, ib_call):
        debug('IB call answered: {}\n'.format(ib_call))
        r = random.randint(0, 2)
        if r == 0:
            msg = await self.api.ntg_ib_call(ib_call['id'])
        elif r == 1:
            msg = await self.api.hit_ib_call(ib_call['id'], BID)
        else:
            msg = await self.api.hit_ib_call(ib_call['id'], ASK)

        debug('{} returned {}\n'.format('ntg_ib_call' if r == 0 else 'hit_ib_call', msg))

    async def on_ib_call_hit(self, ib_call):
        debug('IB call hit: {}\n'.format(ib_call))

    async def on_ib_call_ntg(self, ib_call):
        debug('IB call NTG: {}\n'.format(ib_call))

    async def on_ib_call_expired(self, ib_call):
        debug('IB call expired: {}\n'.format(ib_call))

    async def on_dp_order_hit(self, other_party, hit_amount, price, order):
        debug('DP order hit | other_party: {}, hit_amount: {}, price: {}, order: {}\n'.format(other_party, hit_amount,
                                                                                              price, order))

    async def on_eb_order_hit(self, market_taker, hit_amount, price, eb_order):
        debug('EB order hit | market_taker: {}, hit_amount: {}, price: {}, order: {}\n'.format(market_taker, hit_amount,
                                                                                               price, eb_order))

    async def on_eb_depth_change(self, swift_id, direction, old_depth, new_depth):
        debug('EB depth changed: swift_id: {}, direction: {}'.format(swift_id, direction))
        debug('old_depth: {}\nnew_depth: {}\n'.format(old_depth, new_depth))

    async def on_market_price_change(self, old_price, new_price):
        debug('Market price changed\nold_price: {}\nnew_price: {}\n'.format(old_price, new_price))

    async def on_position_change(self, old_position, new_position):
        debug('Position changed\nold_position: {}\nnew_position: {}\n'.format(old_position, new_position))

    async def on_macro_event(self, macro_event):
        debug('Macro event published: {}\n'.format(macro_event))

    async def main_loop(self):
        swift_id = self.api.get_swifts()[0]['swift_id']

        # API commands
        if self.counter % 10 == 0:
            asyncio.ensure_future(self.hit_price(swift_id))
            asyncio.ensure_future(self.eb_add_order(swift_id))
        if self.counter % 30 == 0:
            asyncio.ensure_future(self.make_ib_call(swift_id))
        if self.counter % 75 == 0:
            asyncio.ensure_future(self.eb_cancel_order(swift_id))
        if self.counter % 150 == 0:
            asyncio.ensure_future(self.eb_cancel_all_orders(swift_id))

        # API getters
        if self.counter % 10 == 0:
            debug('CURRENT STATE:\n--------------')
            debug('EB orders: {}\n'.format(self.api.get_eb_orders(swift_id)))
            debug('EB depth: {}\n'.format(self.api.get_eb_depth(swift_id)))
            debug('Market prices: {}\n'.format(self.api.get_market_prices(swift_id)))
            debug('Price curve: {}\n---------------------\n\n\n'.format(self.api.get_price_curves(swift_id)))

        self.counter += 1

    async def make_ib_call(self, swift_id):
        msg = await self.api.make_ib_call(swift_id, 5)
        debug('make_ib_call returned {}\n'.format(msg))

    async def dp_add_order(self, swift_id):
        direction = random.choice([BID, ASK])
        price = None
        if random.choice([False, True]):
            market_price = self.api.get_market_prices(swift_id)
            price = {BID: market_price['bid'], ASK: market_price['ask']}[direction]

        msg = await self.api.dp_add_order(swift_id, direction, 50, price)
        debug('dp_add_order with price={} returned {}\n'.format(price, msg))

    async def dp_cancel_order(self, swift_id):
        direction = random.choice([BID, ASK])
        orders = self.api.get_dp_orders(swift_id, direction)

        if len(orders) > 0:
            order_id = orders[0]['id']
            msg = await self.api.dp_cancel_order(order_id)
            debug('dp_cancel_order {} returned {}\n'.format(order_id, msg))

    async def dp_cancel_all_orders(self, swift_id):
        msg = await self.api.dp_cancel_all_orders(swift_id)
        debug('dp_cancel_all_orders returned {}\n'.format(msg))

    async def hit_price(self, swift_id):
        direction = random.choice([BID, ASK])
        fok = random.choice([False, True])

        msg = await self.api.hit_price(swift_id, direction, 10, fill_or_kill=fok)
        debug('hit_price with fill_or_kill={} returned {}\n'.format(fok, msg))

    async def eb_add_order(self, swift_id):
        market_price = self.api.get_market_prices(swift_id)

        direction = random.choice([BID, ASK])
        price = {BID: market_price['bid'], ASK: market_price['ask']}[direction]
        iceberg = random.choice([False, True])
        chunk = 1 if iceberg else None
        plus = random.choice([False, True])

        msg = await self.api.eb_add_order(swift_id, direction, 10, price, chunk, plus)
        debug('eb_add_order with chunk={}, plus={} returned {}\n'.format(chunk, plus, msg))

    async def eb_cancel_order(self, swift_id):
        direction = random.choice([BID, ASK])
        orders = self.api.get_eb_orders(swift_id, direction)

        if len(orders) > 0:
            order_id = orders[0]['id']
            msg = await self.api.eb_cancel_order(order_id)
            debug('eb_cancel_order {} returned {}\n'.format(order_id, msg))

    async def eb_cancel_all_orders(self, swift_id):
        msg = await self.api.eb_cancel_all_orders(swift_id)
        debug('eb_cancel_all_orders returned {}\n'.format(msg))


def main():
    username = 'YOUR_USERNAME'
    password = 'YOUR_PASSWORD'
    room_id = 'ROOM_ID'

    bot = RandomBot(username, password, room_id)
    loop = asyncio.get_event_loop()
    loop.run_until_complete(bot.run())


if __name__ == '__main__':
    main()
