#!/usr/bin/env python3
# Copyright (c) 2017-present, Facebook, Inc.
# All rights reserved.
# This source code is licensed under the BSD-style license found in the
# LICENSE file in the root directory of this source tree. An additional grant
# of patent rights can be found in the PATENTS file in the same directory.
from .base import BaseGameConfig
from ast import literal_eval

class PersonachatGC(BaseGameConfig):
    TASK_NAME = 'personachat:both'
    TASK_IDENTIFIER = 'persona_vanilla'
    EXAMPLE_MODE = 'dialog'

    # Flip this to change to teacher forcing or collecting all new conversations
    # If this is true, you probably should not use --use_truth_label
    CONTINUE_CONVERSATION = False

    P1_OFFSET = len('partner\'s persona: ')
    P2_OFFSET = len('your persona: ')
    P1_MSG_TEMPLATE = "<span style='font-size: 18px'><b>[Them]:</b> {}</span>"
    P2_MSG_TEMPLATE = "<span style='font-size: 18px'><b>[You]:</b> {}</span>"
    EX_TEMPLATE = \
    '''
    <span style='font-size: 18px'><b>Your Persona:</b></span>
    <br>
    <ul style='font-size: 16px'>
        {}
    </ul>
    {}
    '''
    LI_TEMPLATE = '<li>{}</li>'

    HIT_TITLE = 'Continue a conversation with friends! (Average $15/hr with Bonuses)'

    HIT_DESCRIPTION = 'In a group of people, you will help determine the best response to a given conversation given a persona.'

    HIT_KEYWORDS = 'response,conversation, persona'

    START_LABEL_MSG = '\nRound {}! Please look at the message and your \
        persona, then write an engaging response.'

    ONBOARDING = \
    '''
    <h1><b>Player, prepare for Persona Chat...</b></h1>
    <br>
    Your goal is to write an engaging response to part of a conversation.
    <br>
    In order to emerge victorious from this battle, your responses need to be engaging and interesting.
    <br>
    <br>
    {}
    '''

    TASK_DESCRIPTION = \
    '''
    <h2><b>Description</b></h2>
    In this task, you will attempt to come up with the best responses to a conversation with a persona by either writing a response or voting on the best one.
    <br>
    <h4><span style='color:blue'><b>Responding</b></span></h4>
    You will see a conversation, and a persona. <b>Using the persona</b>, write an engaging response to the conversation.
    <br>
    <b>Do not simply copy the persona.</b> Sometimes it doesn't make sense to use the persona, and that's okay.
    <br>
    Remember, you're competing against other Turkers to write the best one, so stay relevant and be creative!
    <br>
    <h4><span style='color:blue'><b>Voting</b></span></h4>
    All the different responses written will appear under the example. Choose the response you believe is the <b>most engaging</b> response to the example.
    Remember, an engaging response makes you want to continue the conversation and reply.
    <br>
    Try to choose engaging ones. Note that you cannot vote for your own response.
    <br>
    <h4><span style='color:blue'><b>Scoring and Bonuses</b></span></h4>
    After everyone has voted, the votes are tallied. The response with the most votes is the winner! If there is a tie for top response, then no bonuses are distributed - so make sure you vote for the right one!
    The player that wrote the winning response receives a bonus. All the players that voted for the winning response also receive a bonus.
    <br>
    <h4><span style='color:blue'><b>Progression of the Game</b></span></h3>
    Each HIT consists of 5 rounds. Every new round, a new conversation is generated by our bot. Our bot isn't very good at acting human, so please be forgiving of any mistakes it makes!
    <h4><span style='color:blue'><b>Example</b></span></h4>
    <span><b>[Them]:</b> Hey, how's your day been going so far?</span>
    <span><b>[You]:</b> Not bad, what about you?</span>
    <span><b>[Them]:</b> I guess okay, but man, this rainy weather has been terrible lately.</span>
    <br>
    <span><b>Your Persona</b></span>
    <br>
    <ul>
        <li>My name is David.</li>
        <li>I enjoy reading fantasy novels.</li>
        <li>My favorite drink is orange juice.</li>
        <li>I have a deep fear of bicycles.</li>
    </ul>
    <span><b>Sample responses:</b></span>
    <br>
    <ul>
        <li>Really? It's my favorite weather to curl up and read a book in.</li>
        <li>Maybe, but at least it's keeping bicyclists off the streets! Man, I hate those things.</li>
        <li>Dang it, I forgot to pack my umbrella since I was running late and wanted to make sure I got a glass of orange juice.</li>
    </ul>
    Note that these sample responses are engaging, continue the conversation, and usually make use of the given persona.
    {}
    '''

    def __init__(self):
        super().__init__()
        self.examples = []

    def next_example(self, generator, total_rounds, offset):
        if len(self.examples) == 0:
            for i in range(total_rounds):
                ex0 = generator.get(offset + 2 * i, entry_idx=0)
                ex1 = generator.get(offset + 2 * i, entry_idx=1)
                ex2 = generator.get(offset + 2 * i, entry_idx=2)
                text = str.join('\n', [ex0['text'], ex0['labels'][0], ex1['text'], ex1['labels'][0], ex2['text']])
                ex0['text'] = text
                ex0['offset'] = offset + 2 * i
                self.examples.append(ex0)
        return self.examples.pop(0)

    def process_dialog(self, example, prev_winning_label=None):
        raw_data = example['text']
        split_data = raw_data.split('\n')

        persona_raw = split_data[:-1]
        persona_one_raw = list(filter(
            lambda x: x.startswith('partner\'s persona:'), persona_raw))
        persona_two_raw = list(filter(
            lambda x: x.startswith('your persona:'), persona_raw))
        persona_one = [self.LI_TEMPLATE.format(
            p[self.P1_OFFSET:].capitalize())
            for p in persona_one_raw]
        persona_two = [self.LI_TEMPLATE.format(
            p[self.P2_OFFSET:].capitalize())
            for p in persona_two_raw]
        persona_one = str.join('', persona_one)
        persona_two = str.join('', persona_two)

        conv_history = '<br>' + self.P1_MSG_TEMPLATE.format(split_data[-5].capitalize())
        conv_history += '<br>' + self.P2_MSG_TEMPLATE.format(split_data[-4].capitalize())
        conv_history += '<br>' + self.P1_MSG_TEMPLATE.format(split_data[-3].capitalize())
        conv_history += '<br>' + self.P2_MSG_TEMPLATE.format(split_data[-2].capitalize())
        conv_history += '<br>' + self.P1_MSG_TEMPLATE.format(split_data[-1].capitalize())
        return self.EX_TEMPLATE.format(persona_two, conv_history)


class PersonachatRespondingOnlyGC(PersonachatGC):

    TASK_IDENTIFIER = 'persona_response_solo'
    END_INFO = \
    """
    <h3><span style="color:blue"><b>Reward/Bonus</b></span></h3>
    There are two bonuses that can be won that push the average pay to $15/hr.
    <br>
    <ol>
        <li>Round Completion: successfully progress through all rounds without disconnecting or being kicked (all-or-nothing $0.70)</li>
        <li>Participation: do not time out at any point (all-or-nothing $0.40)</li>
    </ol>
    There is a base pay rate of $0.05. So, for a given HIT, if you do not time out and finish all the rounds, you will earn $0.05 + $0.70 + $0.40 = $1.15.
    Please note that bonuses are typically paid 2~4 days after the HIT is completed.
    <br>
    <h3><span style="color:blue"><b>Close Window/Timeout/Return HIT</b></span></h3>
    Once the conversation has started, close window/timeout or return HIT during the chat will result in
    <b>HIT EXPIRED</b> to you. You will still earn the bonuses already won to that point.
    <br>
    Please remember that each round has a time limit. If you timeout even once, you will be kicked out of the HIT.
    <br>
    <br>
    <h3><span style='color:red'><b>IMPORTANT NOTICE</b></span></h4>
    1. <b>Be aware the conversations you have will be made public, so act as you would e.g. on a public social network like Twitter.</b>
    <br>
    2. Please do not send any message that could make others uncomfortable, including any level of discrimination, racism, sexism and offensive religious/politics comments, otherwise the submission will be rejected.
    <br>
    3. This particular HIT can only be completed by an individual worker up to 5 times.
    <br>
    <br>
    """

    HIT_TITLE = 'Continue a conversation! (Average $15/hr with Bonuses)'

    HIT_DESCRIPTION = 'You will help determine the best response to a given conversation given a persona.'

    HIT_KEYWORDS = 'response,conversation, persona'

    TASK_DESCRIPTION = \
    '''
    <h2><b>Description</b></h2>
    In this task, you will attempt to come up with the best responses to a conversation with a persona.
    <br>
    <h4><span style='color:blue'><b>Responding</b></span></h4>
    You will see a conversation, and a persona. <b>Using the persona</b>, write an engaging response to the conversation.
    <br>
    <b>Do not simply copy the persona.</b> Sometimes it doesn't make sense to use the persona, and that's okay. Stay relevant and be creative!
    <br>
    <h4><span style='color:blue'><b>Progression of the Game</b></span></h3>
    Each HIT consists of 5 rounds. Every new round, a new conversation is generated by our bot. Our bot isn't very good at acting human, so please be forgiving of any mistakes it makes!
    <h4><span style='color:blue'><b>Example</b></span></h4>
    <span><b>[Them]:</b> Hey, how's your day been going so far?</span>
    <span><b>[You]:</b> Not bad, what about you?</span>
    <span><b>[Them]:</b> I guess okay, but man, this rainy weather has been terrible lately.</span>
    <br>
    <span><b>Your Persona</b></span>
    <br>
    <ul>
        <li>My name is David.</li>
        <li>I enjoy reading fantasy novels.</li>
        <li>My favorite drink is orange juice.</li>
        <li>I have a deep fear of bicycles.</li>
    </ul>
    <span><b>Sample responses:</b></span>
    <br>
    <ul>
        <li>Really? It's my favorite weather to curl up and read a book in.</li>
        <li>Maybe, but at least it's keeping bicyclists off the streets! Man, I hate those things.</li>
        <li>Dang it, I forgot to pack my umbrella since I was running late and wanted to make sure I got a glass of orange juice.</li>
    </ul>
    Note that these sample responses are engaging, continue the conversation, and usually make use of the given persona.
    {}
    '''

    def __init__(self):
        super().__init__()


class PersonachatVotingOnlyGC(PersonachatGC):
    TASK_IDENTIFIER = 'persona_voting'
    START_LABEL_MSG = '\nRound {}! Please look at the conversation and your \
        persona, then vote for the most engaging response.'

    END_INFO = \
    """
    <h3><span style="color:blue"><b>Reward/Bonus</b></span></h3>
    There are three bonuses that can be won that push the average pay to $15/hr.
    <br>
    <ol>
        <li>Round Completion: successfully progress through all rounds without disconnecting or being kicked (all-or-nothing $0.70)</li>
        <li>Participation: do not time out at any point (all-or-nothing $0.40)</li>
        <li>Voter Winner: vote for a response that is chosen as the winner for a single round ($0.15/occurrence)</li>
    </ol>
    There is a base pay rate of $0.05. So, for a given HIT, if you do not time out and finish all the rounds, you will earn $0.05 + $0.70 + $0.40 = $1.15.
    But, if you vote for all five winning responses, you can earn up to an extra $0.75 per HIT! Please note that bonuses are typically paid 2~4 days after the HIT is completed.
    <br>
    <b>Please note:</b> if another player disconnects in the middle of the game and not enough remain to play, your bonuses <b>will be pro-rated</b>.
    This means if 4/5 rounds are completed, you get 80% of the max Round Completion and Participation bonus if you qualify.
    <br>
    <h3><span style="color:blue"><b>Close Window/Timeout/Return HIT</b></span></h3>
    Once the conversation has started, close window/timeout or return HIT during the chat will result in
    <b>HIT EXPIRED</b> to you. You will still earn the bonuses already won to that point.
    <br>
    <br>
    <h3><span style='color:red'><b>IMPORTANT NOTICE</b></span></h4>
    1. <b>Be aware the conversations you have will be made public, so act as you would e.g. on a public social network like Twitter.</b>
    <br>
    2. Please do not send any message that could make others uncomfortable, including any level of discrimination, racism, sexism and offensive religious/politics comments, otherwise the submission will be rejected.
    <br>
    <br>
    """

    HIT_TITLE = 'Vote on Responses to a conversation! (Average $15/hr with Bonuses)'

    HIT_DESCRIPTION = 'You will help determine the best response to a given conversation given a persona by voting for the best ones.'

    HIT_KEYWORDS = 'response,conversation, persona'

    ONBOARDING = \
    '''
    <h1><b>Player, prepare for Persona chat...</b></h1>
    <br>
    Your goal is to vote for the best responses for various examples.
    <br>
    In order to emerge victorious from this battle, you need to select the most engaging and creative responses.
    <br>
    <br>
    To ensure the game continues to progress, there is a time limit to vote for the best one (30 seconds).
    <br>
    {}
    '''

    TASK_DESCRIPTION = \
    '''
    <h2><b>Description</b></h2>
    In this task, you will attempt to come up with the best responses to a conversation with a persona by voting on the best one.
    <br>
    <h4><span style='color:blue'><b>Responding</b></span></h4>
    You will see a conversation, and a persona. <b>Using the persona</b>, vote for the best response to a conversation.
    <br>
    <b>Do not simply copy the persona.</b> Sometimes it doesn't make sense to use the persona, and that's okay.
    <br>
    <h4><span style='color:blue'><b>Voting</b></span></h4>
    All the different responses written will appear under the example. Choose the response you believe is the <b>most engaging</b> response to the example.
    Remember, an engaging response makes you want to continue the conversation and reply.
    <br>
    Try to choose engaging ones.
    <br>
    <h4><span style='color:blue'><b>Scoring and Bonuses</b></span></h4>
    After everyone has voted, the votes are tallied. The response with the most votes is the winner! If there is a tie for top response, then no bonuses are distributed - so make sure you vote for the right one!
    The player that wrote the winning response receives a bonus. All the players that voted for the winning response also receive a bonus.
    <br>
    <h4><span style='color:blue'><b>Progression of the Game</b></span></h3>
    Each HIT consists of 5 rounds. Every new round, a new conversation is generated by our bot. Our bot isn't very good at acting human, so please be forgiving of any mistakes it makes!
    <h4><span style='color:blue'><b>Example</b></span></h4>
    <span><b>[Them]:</b> Hey, how's your day been going so far?</span>
    <span><b>[You]:</b> Not bad, what about you?</span>
    <span><b>[Them]:</b> I guess okay, but man, this rainy weather has been terrible lately.</span>
    <br>
    <span><b>Your Persona</b></span>
    <br>
    <ul>
        <li>My name is David.</li>
        <li>I enjoy reading fantasy novels.</li>
        <li>My favorite drink is orange juice.</li>
        <li>I have a deep fear of bicycles.</li>
    </ul>
    <span><b>Sample responses:</b></span>
    <br>
    <ul>
        <li>Really? It's my favorite weather to curl up and read a book in.</li>
        <li>Maybe, but at least it's keeping bicyclists off the streets! Man, I hate those things.</li>
        <li>Dang it, I forgot to pack my umbrella since I was running late and wanted to make sure I got a glass of orange juice.</li>
    </ul>
    Note that these sample responses are engaging, continue the conversation, and usually make use of the given persona.
    {}
    '''

    def __init__(self):
        super().__init__()
        self.ex_idcs = None
        self.exs = []

    def load_cands(self, example):
        truth = example['labels'][0]
        options = [ex for ex in example['label_candidates'] if ex != truth]
        to_display = random.sample(options, 4)
        to_display.append(truth)
        random.shuffle(to_display)
        cands = []
        for idx, lbl in enumerate(to_display):
            cands.append((idx, lbl, idx))
        return cands


class PersonachatVotingOnlySoloGC(PersonachatVotingOnlyGC):
    TASK_IDENTIFIER = 'persona_voting_solo'

    END_INFO = \
    """
    <h3><span style="color:blue"><b>Reward/Bonus</b></span></h3>
    There is a bonus that can be won that pushes the average pay to $15/hr.
    <br>
    <ol>
        <li>Round Completion: successfully progress through all rounds without timing out (all-or-nothing $1.45)</li>
    </ol>
    There is a base pay rate of $0.05. So, for a given HIT, if you do not time out and finish all the rounds, you will earn $0.05 + $1.45 = $1.50.
    Please note that bonuses are typically paid 2~4 days after the HIT is completed.
    <br>
    <h3><span style="color:blue"><b>Close Window/Timeout/Return HIT</b></span></h3>
    Once the conversation has started, close window/timeout or return HIT during the chat will result in
    <b>HIT EXPIRED</b> to you. You will still earn the bonuses already won to that point.
    <br>
    Please remember that each conversation has a time limit. If you timeout even once, you will be kicked out of the HIT.
    <br>
    <br>
    <h3><span style='color:red'><b>IMPORTANT NOTICE</b></span></h4>
    1. <b>Be aware the conversations you have will be made public, so act as you would e.g. on a public social network like Twitter.</b>
    <br>
    2. Please do not send any message that could make others uncomfortable, including any level of discrimination, racism, sexism and offensive religious/politics comments, otherwise the submission will be rejected.
    <br>
    3. This particular HIT can only be completed by an individual worker up to 5 times.
    <br>
    <br>
    """

    HIT_DESCRIPTION = 'You will help determine the best response for each example by voting on the most engaging ones.'

    TASK_DESCRIPTION = \
    '''
    <h2><b>Description</b></h2>
    In this task, you will attempt to determine the best responses for conversations by voting for the best one.
    <br>
    <h4><span style='color:blue'><b>Responding</b></span></h4>
    You will see a conversation, and a persona. <b>Using the persona</b>, vote for the best response to a conversation.
    <br>
    Sometimes it doesn't make sense to use the persona, and that's okay.
    <br>
    <h4><span style='color:blue'><b>Voting</b></span></h4>
    Several different responses written will appear under the example. Choose the response you believe is the <b>most engaging</b> response to the conversation that <b>uses the persona</b>!
    Remember, an engaging response makes you want to continue the conversation and reply.
    <br>
    <h4><span style='color:blue'><b>Progression of the Game</b></span></h3>
    Each HIT consists of 10 rounds. Every new round, a new conversation is generated by our bot. Our bot isn't very good at acting human, so please be forgiving of any mistakes it makes!
    <h4><span style='color:blue'><b>Example</b></span></h4>
    <span><b>[Them]:</b> Hey, how's your day been going so far?</span>
    <span><b>[You]:</b> Not bad, what about you?</span>
    <span><b>[Them]:</b> I guess okay, but man, this rainy weather has been terrible lately.</span>
    <br>
    <span><b>Your Persona</b></span>
    <br>
    <ul>
        <li>My name is David.</li>
        <li>I enjoy reading fantasy novels.</li>
        <li>My favorite drink is orange juice.</li>
        <li>I have a deep fear of bicycles.</li>
    </ul>
    <span><b>Sample responses:</b></span>
    <br>
    <ul>
        <li>Really? It's my favorite weather to curl up and read a book in.</li>
        <li>Maybe, but at least it's keeping bicyclists off the streets! Man, I hate those things.</li>
        <li>Dang it, I forgot to pack my umbrella since I was running late and wanted to make sure I got a glass of orange juice.</li>
    </ul>
    Note that these sample responses are engaging, continue the conversation, and usually make use of the given persona.
    {}
    '''

    def __init__(self):
        super().__init__()

    def next_example(self, generator, total_rounds, offset):
        if len(self.examples) == 0:
            for i in range(total_rounds):
                ex0 = generator.get(offset + i, entry_idx=0)
                ex1 = generator.get(offset + i, entry_idx=1)
                ex2 = generator.get(offset + i, entry_idx=2)
                text = str.join('\n', [ex0['text'], ex0['labels'][0], ex1['text'], ex1['labels'][0], ex2['text']])
                ex0['text'] = text
                ex0['offset'] = offset + i
                ex0['label_candidates'] = [literal_eval(lbl) for lbl in ex2['label_candidates'][1:]]
                self.examples.append(ex0)
        return self.examples.pop(0)

    def load_cands(self, example):
        return [(idx, lbl[0], lbl[1]) for idx, lbl in enumerate(example['label_candidates'])]
