import json
import logging
from lxml.html.clean import Cleaner, autolink_html
import re

import open_ended_image_submission
from xmodule.progress import Progress
import capa.xqueue_interface as xqueue_interface
from capa.util import *
from .peer_grading_service import PeerGradingService, MockPeerGradingService
import controller_query_service

from datetime import datetime
from pytz import UTC
import requests
from boto.s3.connection import S3Connection
from boto.s3.key import Key
log = logging.getLogger("mitx.courseware")

# Set the default number of max attempts.  Should be 1 for production
# Set higher for debugging/testing
# attempts specified in xml definition overrides this.
MAX_ATTEMPTS = 1

# Set maximum available number of points.
# Overriden by max_score specified in xml.
MAX_SCORE = 1

FILE_NOT_FOUND_IN_RESPONSE_MESSAGE = "We could not find a file in your submission. Please try choosing a file or pasting a link to your file into the answer box."
ERROR_SAVING_FILE_MESSAGE = "We are having trouble saving your file. Please try another file or paste a link to your file into the answer box."
#@begin:Uploads/delete a file to S3
#@date:2013-11-02   
def upload_to_s3(file_to_upload, keyname, s3_interface):
    '''
    Upload file to S3 using provided keyname.

    Returns:
    public_url: URL to access uploaded file
    '''

    conn = S3Connection(s3_interface['access_key'], s3_interface['secret_access_key'])
    bucketname = str(s3_interface['storage_bucket_name'])
    bucket = conn.create_bucket(bucketname.lower())

    k = Key(bucket)
    k.key = keyname
    k.set_metadata('filename', file_to_upload.name)
    k.set_contents_from_file(file_to_upload)

    k.set_acl("public-read")
    public_url = k.generate_url(60 * 60 * 24 * 365) # URL timeout in seconds.

    return public_url

def delete_s3(keyname, s3_interface):
    '''
    del file
    '''
    conn = S3Connection(s3_interface['access_key'], s3_interface['secret_access_key'])
    bucketname = str(s3_interface['storage_bucket_name'])
    bucket = conn.create_bucket(bucketname.lower())
    k = Key(bucket)
    k.key = keyname
    bucket.delete_key(keyname)
#@end
class OpenEndedChild(object):
    """
    States:

    initial (prompt, textbox shown)
         |
    assessing (read-only textbox, rubric + assessment input shown for self assessment, response queued for open ended)
         |
    post_assessment (read-only textbox, read-only rubric and assessment, hint input box shown)
         |
    done (submitted msg, green checkmark, everything else read-only.  If attempts < max, shows
         a reset button that goes back to initial state.  Saves previous
         submissions too.)
    """

    DEFAULT_QUEUE = 'open-ended'
    DEFAULT_MESSAGE_QUEUE = 'open-ended-message'
    max_inputfields = 1

    STATE_VERSION = 1

    # states
    #@begin:Add initial_submit state
    #@date:2013-11-02   
    INITIAL = 'initial'
    INITIAL_SUBMIT = 'initial_submit'
    ASSESSING = 'assessing'
    POST_ASSESSMENT = 'post_assessment'
    DONE = 'done'
    #@end
    # This is used to tell students where they are at in the module
    HUMAN_NAMES = {
        'initial': 'Not started',
        'assessing': 'In progress',
        'post_assessment': 'Done',
        'done': 'Done',
        }

    def __init__(self, system, location, definition, descriptor, static_data,
                 instance_state=None, shared_state=None, **kwargs):
        # Load instance state

        if instance_state is not None:
            try:
                instance_state = json.loads(instance_state)
            except:
                log.error(
                    "Could not load instance state for open ended.  Setting it to nothing.: {0}".format(instance_state))
        else:
            instance_state = {}

        # History is a list of tuples of (answer, score, hint), where hint may be
        # None for any element, and score and hint can be None for the last (current)
        # element.
        # Scores are on scale from 0 to max_score

        self.child_history = instance_state.get('child_history', [])
        self.child_state = instance_state.get('child_state', self.INITIAL)
        self.child_created = instance_state.get('child_created', False)
        self.child_attempts = instance_state.get('child_attempts', 0)

        self.max_attempts = static_data['max_attempts']
        self.child_prompt = static_data['prompt']
        self.child_rubric = static_data['rubric']
        self.display_name = static_data['display_name']
        self.accept_file_upload = static_data['accept_file_upload']
        self.close_date = static_data['close_date']
        self.s3_interface = static_data['s3_interface']
        self.skip_basic_checks = static_data['skip_basic_checks']
        self._max_score = static_data['max_score']
        self.control = static_data['control']

        # Used for progress / grading.  Currently get credit just for
        # completion (doesn't matter if you self-assessed correct/incorrect).
        if system.open_ended_grading_interface:
            self.peer_gs = PeerGradingService(system.open_ended_grading_interface, system)
            self.controller_qs = controller_query_service.ControllerQueryService(
                system.open_ended_grading_interface, system
            )
        else:
            self.peer_gs = MockPeerGradingService()
            self.controller_qs = None

        self.system = system

        self.location_string = location
        try:
            self.location_string = self.location_string.url()
        except:
            pass

        self.setup_response(system, location, definition, descriptor)

    def setup_response(self, system, location, definition, descriptor):
        """
        Needs to be implemented by the inheritors of this module.  Sets up additional fields used by the child modules.
        @param system: Modulesystem
        @param location: Module location
        @param definition: XML definition
        @param descriptor: Descriptor of the module
        @return: None
        """
        pass

    def closed(self):
        if self.close_date is not None and datetime.now(UTC) > self.close_date:
            return True
        return False

    def check_if_closed(self):
        if self.closed():
            return True, {
                'success': False,
                # This is a student_facing_error
                'error': 'The problem close date has passed, and this problem is now closed.'
            }
        elif self.child_attempts > self.max_attempts:
            return True, {
                'success': False,
                # This is a student_facing_error
                'error': 'You have attempted this problem {0} times.  You are allowed {1} attempts.'.format(
                    self.child_attempts, self.max_attempts
                )
            }
        else:
            return False, {}

    def latest_answer(self):
        """Empty string if not available"""
        if not self.child_history:
            return ""
        return self.child_history[-1].get('answer', "")

    def latest_score(self):
        """None if not available"""
        if not self.child_hist
ory:
            return None
        return self.child_history[-1].get('score')

    def all_scores(se
lf):
        """None if not available"""
        if not self.child_history:
            return None
        return [self.child_history[i].get('score') for i in xrange(0, len(self.child_history))]

    def latest_post_assessment(self, system):
        """Empty string if not available"""
        if not self.child_history:
            return ""
        return self.child_history[-1].get('post_assessment', "")
    #@begin:Update edx code
    #@date:2013-11-02    
    '''
    def sanitize_html(answer):
        try:
            answer = autolink_html(answer)
            cleaner = Cleaner(style=True, links=True, add_nofollow=False, page_structure=True, safe_attrs_only=True,
                              host_whitelist=open_ended_image_submission.TRUSTED_IMAGE_DOMAINS,
                              whitelist_tags=set(['embed', 'iframe', 'a', 'img', 'br']))
            cle