#!/usr/bin/env python

import datetime

from suds.wsse import Security
from suds.wsse import UsernameToken
from suds.client import Client

from dm.common.exceptions.objectNotFound import ObjectNotFound
from dm.common.objects.runInfo import RunInfo
from dm.common.objects.beamlineInfo import BeamlineInfo
from dm.common.objects.proposalInfo import ProposalInfo
from dm.common.objects.apsUserInfo import ApsUserInfo
from dm.common.exceptions.dmException import DmException
from dm.common.utility.loggingManager import LoggingManager

class BssClient:

    WSDL_URL = 'https://schedule.aps.anl.gov/beamschedds/springws'
    CACHE_DURATION_IN_SECONDS = 10

    def __init__(self, username, password):
        self.logger = LoggingManager.getInstance().getLogger(self.__class__.__name__)
        self.__configure(username, password)

    def __configure(self, username, password):
        beamlineScheduleServiceUrl = self.WSDL_URL + '/beamlineScheduleService/beamlineScheduleWebService.wsdl'
        runScheduleServiceUrl = self.WSDL_URL + '/runScheduleService/runScheduleWebService.wsdl'

        try:
            self.runScheduleServiceClient = Client(runScheduleServiceUrl)
            self.runScheduleServiceClient.options.cache.setduration(seconds=self.CACHE_DURATION_IN_SECONDS)
            self.setSoapHeader(self.runScheduleServiceClient, username, password)
        
            self.beamlineScheduleServiceClient = Client(beamlineScheduleServiceUrl)
            self.beamlineScheduleServiceClient.options.cache.setduration(seconds=self.CACHE_DURATION_IN_SECONDS)
            self.setSoapHeader(self.beamlineScheduleServiceClient, username, password)
        except Exception, ex:
            self.logger.error('Cannot open BSS connection: %s' % str(ex))
            raise DmException(exception=ex)
         
    @classmethod
    def setSoapHeader(cls, client, username, password):
        security = Security()
        token = UsernameToken(username, password)
        token.setcreated()
        security.tokens.append(token)
        client.set_options(wsse=security)

    def listRuns(self):
        ''' Return list of all runs. '''
        result = self.runScheduleServiceClient.service.findAllRuns()
        runArray = result.run
        runs = []
        for run in runArray:
            runs.append(RunInfo({'name' : run.runName, 'startTime' : run.startTime, 'endTime' : run.endTime}))
        return runs

    def listRunsBetweenDates(self, startDate, endDate):
        ''' Find list of runs between given startDate and endDate. '''
        result = self.runScheduleServiceClient.service.findAllRuns()
        runArray = result.run
        runs = []
        for run in runArray:
            if run.startTime >= startDate and run.endTime <= endDate:
                runs.append(RunInfo({'name' : run.runName, 'startTime' : run.startTime, 'endTime' : run.endTime}))
        return runs

    def getRunForDates(self, startDate, endDate):
        ''' Find run that spans given startDate and endDate. '''
        result = self.runScheduleServiceClient.service.findAllRuns()
        runArray = result.run
        for run in runArray:
            if startDate >= run.startTime and endDate <= run.endTime:
                return RunInfo({'name' : run.runName, 'startTime' : run.startTime, 'endTime' : run.endTime})
        raise ObjectNotFound('No run found.')

    def getCurrentRun(self):
        ''' Find current run. '''
        now = datetime.datetime.now()
        return self.getRunForDates(now, now)

    def listBeamlines(self):
        ''' Find list of all beamlines. '''
        result = self.beamlineScheduleServiceClient.service.findAllBeamlines()
        beamlineArray = result.beamline
        beamlines = []
        for beamline in beamlineArray:
            beamlines.append(BeamlineInfo({'name' : beamline.beamlineName, 'id' : beamline.id}))
        return beamlines

    def createProposalInfo(self, proposal):
        experimenterArray = proposal.experimenters.experimenter
        experimenters = []
        for experimenter in experimenterArray:
            user = ApsUserInfo({
                'id' : experimenter.id,
                'badge' : experimenter.badge,
                'email' : experimenter.email,
                'firstName' : experimenter.firstName,
                'instId' : experimenter.instId,
                'institution' : experimenter.institution,
                'lastName' : experimenter.lastName
            })
            if hasattr(experimenter, 'piFlag'):
                user['piFlag'] = experimenter.piFlag

            experimenters.append(user)
        proposalInfo = ProposalInfo({
            'title' : proposal.proposalTitle,
            'id' : proposal.id,
            'experimenters' : experimenters
        })
        return proposalInfo

    def listBeamlineProposals(self, beamlineName, runName):
        ''' Find beamline schedule for a given beamlineName and runName. '''
        schedule = self.beamlineScheduleServiceClient.service.findBeamlineSchedule(beamlineName, runName)
        activitiesArray = schedule.activities.activity
        proposals = []
        for activity in activitiesArray:
            if hasattr(activity, 'beamtimeRequest'):
                proposal = activity.beamtimeRequest.proposal
                proposals.append(self.createProposalInfo(proposal))
        return proposals

    def getBeamlineProposal(self, proposalId, beamlineName, runName):
        ''' Find proposal with a given id, beamlineName and runName. '''
        schedule = self.beamlineScheduleServiceClient.service.findBeamlineSchedule(beamlineName, runName)
        activitiesArray = schedule.activities.activity
        proposals = []
        for activity in activitiesArray:
            if hasattr(activity, 'beamtimeRequest'):
                proposal = activity.beamtimeRequest.proposal
                if proposal.id == proposalId:
                    return self.createProposalInfo(proposal)
        raise ObjectNotFound('Proposal with id %s does not exist (beamline: %s; run: %s).' % (proposalId, beamlineName, runName))

if __name__ == '__main__':
    bss = BssClient('DMADMIN', 'A2Fxew@11:76am')
    now = datetime.datetime.now()
    days = datetime.timedelta(days=180)
    before = now - days
    print before, now
    runNames = bss.getRunsBetweenDates(before, now)
    print 'RUNS: ', runNames 
    print 'CURRENT RUN: ', bss.getCurrentRun()
    print bss.listBeamlines()
    print bss.listBeamlineProposals('1-ID-B,C,E', '2017-1')
    print
    print bss.getBeamlineProposal(51190, '1-ID-B,C,E', '2017-1')
    print
    print bss.getBeamlineProposal(48258, '1-ID-B,C,E', '2017-1')