modules/FeedProcessor/BetgeniusProcessor.psm1

using module '..\Enums.psm1'
using module '..\Helper\DateTimeHelper.psm1'
using module '.\AuditLog\Message.psm1'
using module '.\AuditLog\Event.psm1'
using module '.\AuditLog\Market.psm1'
using module '.\AuditLog\Outcome.psm1'
using module '.\AuditLog\Event\SportData.psm1'
using module '.\AuditLog\Event\Score.psm1'
using module '.\AuditLog\Message\Cardinality.psm1'
using module '.\ProcessorBase.psm1'

class BetgeniusProcessor: Processor{

    BetgeniusProcessor([SOURCE_TYPE] $sourceType): base($sourceType){
        $this.source = [SOURCE]::betgenius

        $this.producers.Add('BG', 61)
        $this.mapExternalKeyPrefix.Add('betgenius', '6111')

        $this.shouldReprocess = $false
        $this.isTypeBased = $false
        $this.haveMarketSeparator = $false
        $this.needFeedProducer = $false
    }

    #region Internal Resolving Methods

    # TODO Standardize Actions across feed processors
    hidden [string] ResolveMessageAction([PSCustomObject] $content){
        if ($content.Fixture) { return [CONST]::MSG_FIXTURE }
        if ($content.Coverage) { return [CONST]::MSG_FIXTURE }
        if ($content.MarketSet) { return [CONST]::MSG_ODDS_CHANGE }
        if ($content.ResultSet) { return [CONST]::MSG_SETTLEMENT }

        return 'NA'
    }

    hidden [string] ResolveMarketStatus([string] $tradingStatus){

        return $tradingStatus
    }

    hidden [hashtable] GetSpecifiers([string]$hcp, [string]$seq) {
        $_retValue = @{}

        if ($hcp) { $_retValue.Add('hcp', $hcp) }
        if ($seq) { $_retValue.Add('seq', $seq) }

        return $_retValue
    }

    hidden [long] getUniqueIdentifier(){

        return $this.content.MessageGuid
    }

    hidden [long] getTimestamp(){

        if ($this.content.Header){
            $timeUtc = $this.content.Header.TimeStampUtc
        }
        else {
            $timeUtc = $this.content.MessageTimestampUtc
        }

        $timeUtc = [DatetimeHelper]::getStringToLocalDate($timeUtc, '')

        if ($timeUtc) { $timestamp = [DatetimeHelper]::ConvertToTimestamp($timeUtc) }
        else { $timestamp = -1 }

        return $timestamp
    }

    #endregion

    #region Processing Methods

    [void] ProcessMessage([PSCustomObject] $line) {
        ([Processor]$this).ProcessMessage($line)

        $this.message.uniqueIdentifier = $this.getUniqueIdentifier()
        $this.message.timestamp = $this.getTimestamp()
    }

    [void] ProcessEvent([PSCustomObject] $line, [bool] $includeSportData) {

        $this.ProcessMessage($line)
        $this.message.event = $this.getEvent($line.content, $includeSportData)

        # TODO Move the action to message, not event - or at least keep two actions
        $this.message.event.action = $this.ResolveMessageAction($line.content)
        $this.isEventProcessed = $true
    }

    [void] ProcessCardinality([PSCustomObject] $line) {

        $this.message.cardinality = $this.getCardinality($line.content)
    }

    [void] ProcessEnrichment([PSCustomObject] $line) {
        if (-not $this.message.event) {
            $this.ProcessEvent($line, $false)
        }
        $this.message.event.scoreDetails = $this.getScoreboard($line.content)
    }

    [void] ProcessMarket([PSCustomObject] $line, [Nullable[SEARCH_SCOPE]] $searchScope, [string[]] $marketId, [string[]] $outcomeId) {

        if (-not $searchScope) { $searchScope = [SEARCH_SCOPE]::typeId }
        if (-not $this.message.event) {
            $this.ProcessEvent($line, $false)
        }

        $marketsContent=$null; $action=''
        if ($line.content.MarketSet){
            $marketsContent = $line.content.MarketSet.Markets
            $action = [CONST]::MSG_ODDS_CHANGE
        }
        elseif ($line.content.ResultSet){
            $marketsContent = $line.content.ResultSet.Results
            $action = [CONST]::MSG_SETTLEMENT
        }

        $this.message.event.markets = @()
        foreach ($market in $this.FilterMarkets($marketsContent, $searchScope, $marketId)) {
            $m = $this.getMarket($market, $outcomeId, $action)
            $m.action = $action

            $this.message.event.markets += $m
        }
        $this.isMarketProcessed = $true
    }

    [bool] isEventRelated([PSCustomObject] $line){
        return $line.content.Fixture -or $line.content.Coverage
    }

    [bool] isEnrichmentRelated([PSCustomObject] $line){
        return (-not $line.content.Header)
    }

    [bool] isMarketRelated([PSCustomObject] $line, [Nullable[SEARCH_SCOPE]] $searchScope, [string[]] $marketId){
        if (-not $searchScope) { $searchScope = [SEARCH_SCOPE]::typeId }
        $marketsContent = $null

        if ($line.content.MarketSet){
            $marketsContent = $line.content.MarketSet
        }
        elseif ($line.content.ResultSet){
            $marketsContent = $line.content.ResultSet
        }

        return ($null -ne $this.FilterMarkets($marketsContent, $searchScope, $marketId))
    }

    #endregion

    #region Private Methods

    hidden [PSCustomObject] FilterMarkets([PSCustomObject] $marketsContent, [SEARCH_SCOPE] $searchScope, [string[]] $marketId){
        return ($marketsContent | Where-Object { ($searchScope -eq [SEARCH_SCOPE]::typeId -and $_.MarketType.Id -in $marketid) -or `
                ($searchScope -eq [SEARCH_SCOPE]::internalId -and ($_.Id -in $marketid -or $_.MarketId -in $marketid)) -or `
                ('*' -in $marketid) })
    }

    hidden [PSCustomObject] FilterOutcomes([PSCustomObject] $outcomesContent, [string[]] $outcomeId){
        return $outcomesContent | Where-Object { ($_.id -in $outcomeId) -or ('*' -in $outcomeId) }
    }

    hidden [Event] getEvent([PSCustomObject] $messageContent, [bool] $includeSportData){
        [Event] $event = [Event]::new()

        if ($includeSportData) { $event.sportData = $this.getSportData($messageContent) }

        $status = $true
        foreach ($i in $messageContent.Coverage.AvailableFeeds) {
            $event.other.Add($i.Type, $i.IsLicensed)
            $status = $status -and $i.IsLicensed
        }

        if ($messageContent.Fixture.StartTimeUtc){
            $event.startDate = [DateTimeHelper]::getStringToLocalDate($messageContent.Fixture.StartTimeUtc, '')
        }

        $event.liveCoverage = ''
        if ($null -ne $messageContent.Coverage.IsBooked){
            $event.liveCoverage = $messageContent.Coverage.IsBooked
        }

        if ($status) { $event.status = [CONST]::STATUS_ACTIVE }
        else { $event.status = [CONST]::STATUS_SUSPENDED }

        return $event
    }

    hidden [Market] getMarket([PSCustomObject] $marketContent, [string[]] $outcomeId, [string] $action) {

        [Market] $market = [Market]::new()

        $market.action = $action
        if ($action -eq [CONST]::MSG_ODDS_CHANGE){
            $market.id = $marketContent.Id
            $market.type = $marketContent.MarketType.Id
        }
        if ($action -eq [CONST]::MSG_SETTLEMENT){
            $market.id = $marketContent.MarketId
            $market.type = ''
            $market.resulted = 'True'
        }

        $market.status = $this.ResolveMarketStatus($marketContent.TradingStatus)
        $market.name = $marketContent.name
        $market.specifiers = $this.GetSpecifiers($marketContent.Handicap, $marketContent.Sequence)

        foreach ($outcome in $this.FilterOutcomes($marketContent.Selections, $outcomeId)) {
            $market.outcomes += $this.getOutcome($outcome)
        }

        return $market
    }

    hidden [Outcome] getOutcome([PSCustomObject] $outcomeContent) {
        [Outcome] $outcome = [Outcome]::new()

        $outcome.id = $outcomeContent.Id
        $outcome.name = $outcomeContent.name
        #$outcome.action = $outcomeContent.action
        $outcome.status = $outcomeContent.TradingStatus
        $outcome.price = $outcomeContent.Outcome
        if ($outcomeContent.ResultSet.Results) {
            $outcome.resulted = 'True'
        }
        else {
            $outcome.resulted = ''
        }
        $outcome.settlement = $outcomeContent.ResultSet.Results

        return $outcome
    }

    hidden [SportData] getSportData([PSCustomObject] $messageContent){
        $_sportId = $messageContent.Fixture.Sport.Id
        $_sport = $messageContent.Fixture.Sport.Name
        $_categoryId = $messageContent.Fixture.Competition.Id
        $_category = $messageContent.Fixture.Competition.Name
        $_leagueId = $messageContent.Fixture.Season.Id
        $_league = $messageContent.Fixture.Season.Name
        $_matchId = $messageContent.Fixture.Id
        $_match = $messageContent.Fixture.Name

        $_homeId = ''; $_home = ''; $_awayId = ''; $_away = '';
        foreach ($i in $messageContent.Fixture.Competitors) {
            switch ($i.HomeAway) {
                'Home' {
                    $_homeId = $i.Id
                    $_home = $i.Name
                    break
                }
                'Away' {
                    $_awayId = $i.Id
                    $_away = $i.Name
                    break
                }
            }
        }
        $_references = ''

        return [SportData]::new($_sportId, $_sport, $_categoryId, $_category, $_leagueId, $_league, $_matchId, $_match, $_homeId, $_home, $_awayId, $_away, $_references)
    }

    hidden [Score] getScoreboard([PSCustomObject] $messageContent){
        $_periodId=''; $_home=''; $_away=''; $_scores=''; $_currentServer=''; $_playingMinute='';

        # Soccer
        if ($messageContent.Goals){
            $_periodId = $messageContent.CurrentPhase
            $_home = $messageContent.Goals.Score.Home
            $_away = $messageContent.Goals.Score.Away
            $_scores = @()
            $_currentServer = ''
        }
        #Tennis
        elseif ($messageContent.Match){
            $_periodId = $messageContent.StateOfPlay
            $_home = $messageContent.Match.MatchScore.TeamA
            $_away = $messageContent.Match.MatchScore.TeamB
            $_scores = @()
            foreach ($i in $messageContent.MatchScore.Sets) {
                $_scores += $i.TeamA + ':' + $i.TeamB
            }
            $_currentServer = $messageContent.Server
        }
        #Basketball
        elseif ($messageContent.PossessionArrowDirection){
            $_periodId = $messageContent.CurrentPhase
            $_home = $messageContent.Scores.CurrentScore.TeamA
            $_away = $messageContent.Scores.CurrentScore.TeamB
            $_scores = @()
            if ($messageContent.Scores.FirstQuarterScore) {
                $_scores += $messageContent.Scores.FirstQuarterScore.TeamA + ':' + $messageContent.Scores.FirstQuarterScore.TeamB

                if ($messageContent.Scores.SecondQuarterScore) {
                    $_scores += $messageContent.Scores.SecondQuarterScore.TeamA + ':' + $messageContent.Scores.SecondQuarterScore.TeamB

                    if ($messageContent.Scores.ThirdQuarterScore) {
                        $_scores += $messageContent.Scores.ThirdQuarterScore.TeamA + ':' + $messageContent.Scores.ThirdQuarterScore.TeamB

                        if ($messageContent.Scores.FourthQuarterScore) {
                            $_scores += $messageContent.Scores.FourthQuarterScore.TeamA + ':' + $messageContent.Scores.FourthQuarterScore.TeamB
                        }
                    }
                }
            }

            $_currentServer = $messageContent.CurrentServer
        }
        #Volleyball
        elseif ($messageContent.MatchScore){
            $_periodId = $messageContent.CurrentPhase
            $_home = $messageContent.MatchScore.ScoreInPoints.TeamA
            $_away = $messageContent.MatchScore.ScoreInPoints.TeamB
            $_scores = @()
            foreach ($i in $messageContent.MatchScore.ScoreInSets) {
                $_scores += $i.TeamA + ':' + $i.TeamB
            }
            $_currentServer = $messageContent.CurrentServer
        }
        #Multisport v2
        elseif ($messageContent.MatchScore){
            $_periodId = $messageContent.MatchStatus
            $_home = $messageContent.HomeScore
            $_away = $messageContent.AwayScore
            $_scores = @()
            $_currentServer = ''
        }

        $_data = $messageContent

        return [Score]::new($_periodId, $_home, $_away, $_scores, $_currentServer, $_playingMinute, $_data)
    }

    hidden [Cardinality] getCardinality([PSCustomObject] $messageContent){
        $cardinality = [Cardinality]::new()

        if ($messageContent.MarketSet) {
            $cardinality.markets = $messageContent.MarketSet.Markets.count
            $cardinality.outcomes = $messageContent.MarketSet.Markets.Selections.count
        }
        if ($messageContent.ResultSet) {
            $cardinality.markets = $messageContent.ResultSet.Results.count
            $cardinality.outcomes = $messageContent.ResultSet.Results.Selections.count
        }

        if ($cardinality.markets -eq 0){
            if ($this.isEventRelated($messageContent)) {
                $cardinality.fixtures = 1
            }
            else {
                $cardinality.other = 1
            }
        }

        return $cardinality
    }

    #endregion
}