# Licensed under a 3-clause BSD style license - see LICENSE.rst
"""
=============
CadcTAP plus
=============

"""
import time
import requests
import astroquery.cadc.cadctap.jobSaxParser
from astroquery.utils.tap.model.job import Job
from astroquery.cadc.cadctap import utils

__all__ = ['Job']


class JobCadc(Job):
    """JobCadc class
    Reason for change
    -----------------
    Job class did not redirect
    """

    def __init__(self, async_job, query=None, connhandler=None):
        """Constructor

        Parameters
        ----------
        async_job : bool, mandatory
            'True' if the job is asynchronous
        query : str, optional, default None
            Query
        connhandler : TapConn, optional, default None
            Connection handler
        Reason for change
        -----------------
        Add errmessage for when a job errors
        """
        super(JobCadc, self).__init__(async_job=async_job,
                                      query=query,
                                      connhandler=connhandler)
        self.errmessage = None

    def get_results(self, verbose=False):
        """Returns the job results
        This method will block if the job is asynchronous and the job has not
        finished yet.

        Parameters
        ----------
        verbose : bool, optional, default 'False'
            flag to display information about the process

        Returns
        -------
        The job results (astropy.table).
        Reason for change
        -----------------
        Get rid of read from file because if file is wrong it errors badly
        """
        if self.results is not None:
            return self.results
        # Try to load from server: only async
        if not self.async_:
            # sync: result is in a file
            return None
        else:
            # async: result is in the server once the job is finished
            self.__load_async_job_results()
            return self.results

    def save_results(self, filename, verbose=False):
        """Saves job results
        If the job is asynchronous, this method will block until the results
        are available.

        Parameters
        ----------
        filename : str, mandatory
            name of the file to save the output to
        verbose : bool, optional, default 'False'
            flag to display information about the process
        Reason for change
        -----------------
        Add redirects if resutls are in a different place and get right
        output format for write()
        """
        output = filename
        output_format = self.parameters['format']
        if self._Job__resultInMemory:
            if output_format == 'csv':
                output_format = 'ascii.csv'
            elif "tsv" == output_format:
                output_format = "ascii.fast_tab"

            self.results.write(output, format=output_format)
        else:
            if not self.async_:
                # sync: cannot access server again
                if verbose:
                    print("No results to save")
            else:
                # Async
                self.wait_for_job_end(verbose)
                context = 'async/' + str(self.jobid) + "/results/result"
                response = self.connHandler.execute_tapget(context)
                if verbose:
                    print(response.status, response.reason)
                    print(response.getheaders())

                response = self.redirectJob(response, verbose)
                self.connHandler.dump_to_file(output, response)

    def wait_for_job_end(self, verbose=False):
        """Waits until a job is finished

        Parameters
        ----------
        verbose : bool, optional, default 'False'
            flag to display information about the process
        Reason for change
        -----------------
        Wait less for first few tries then wait longer and
        send abort if there is a keyboard interrupt
        """
        currentResponse = None
        responseData = None
        loops = 0
        while True:
            try:
                responseData = self.get_phase(update=True)
                currentResponse = self._Job__last_phase_response_status

                lphase = responseData.lower().strip()
                if verbose:
                    print("Job " + self.jobid + " status: " + lphase)
                if "pending" != lphase and "queued" != lphase and \
                        "executing" != lphase:
                    break
                # PENDING, QUEUED, EXECUTING, COMPLETED, ERROR, ABORTED,
                # UNKNOWN, HELD, SUSPENDED, ARCHIVED:
                if loops < 10:
                    seconds = 1
                else:
                    seconds = 30
                time.sleep(seconds)
            except KeyboardInterrupt:
                self.abortJob(verbose)
                raise
            loops = loops + 1
        return currentResponse, responseData

    def abortJob(self, verbose):
        """
        Reason for addding
        ------------------
        Abort the job if keyboard interrupt
        """
        if verbose:
            print('abbortting')
            print(self.jobid)
        args = {
            "PHASE": "ABORT"}
        data = self.connHandler.url_encode(args)
        response = self.connHandler.execute_tappost(
            'async/'+str(self.jobid)+'/phase',
            data)
        if verbose:
            print(response.status, response.reason)
            print(response.getheaders())
        return

    def __load_async_job_results(self, debug=False):
        """
        Reason for change
        -----------------
        Add getting the error reason and redirect for getting results
        """
        wjResponse, wjData = self.wait_for_job_end()
        if wjData != 'COMPLETED':
            if wjData == 'ERROR':
                subcontext = 'async/' + self.jobid
                errresponse = self.connHandler.execute_tapget(
                    subcontext)
                # parse job
                jsp = astroquery.cadc.cadctap.jobSaxParser. \
                    JobSaxParserCadc(async_job=False)
                errjob = jsp.parseData(errresponse)[0]
                errjob.connHandler = self.connHandler
                raise requests.exceptions.HTTPError(
                    errjob.errmessage)
            else:
                raise requests.exceptions.HTTPError(
                    'Error running query, PHASE: '+wjData)
        subContext = "async/" + str(self.jobid) + "/results/result"
        resultsResponse = self.connHandler.execute_tapget(subContext)
        if debug:
            print(resultsResponse.status, resultsResponse.reason)
            print(resultsResponse.getheaders())
        resultsResponse = self.redirectJob(resultsResponse, debug)
        outputFormat = self.parameters['format']
        results = utils.read_http_response(resultsResponse, outputFormat)
        self.set_results(results)
        self._Job__phase = wjData

    def redirectJob(self, resultsResponse, verbose=False):
        numberOfRedirects = 0
        while (resultsResponse.status == 303 or resultsResponse.status == 302)\
                and numberOfRedirects < 20:
            joblocation = self.connHandler.find_header(
                resultsResponse.getheaders(),
                "location")
            resultsResponse = self.connHandler.execute_get_other(joblocation)
            numberOfRedirects += 1
            if verbose:
                print(resultsResponse.status, resultsResponse.reason)
                print(resultsResponse.getheaders())

        isError = self.connHandler.check_launch_response_status(
            resultsResponse,
            verbose,
            200)
        if isError:
            if verbose:
                print(resultsResponse.reason)
            raise Exception(resultsResponse.reason)
        else:
            return resultsResponse
