<commit_before>
//
//  RGAgendaLogic.swift
//  RioGo
//
//  Created by <NAME> on 25/02/2016.
//  Copyright © 2016 SkedGo. All rights reserved.
//

import Foundation

import RxSwift

/**
 A data source provides classes or structs conforming to the `TKAgendaInputType` protocol for
 specified date components. The date components will typically be for a specific date, but
 without timezone information. Treat this as "What has the user planned for this calendar
 date", regardless of the time zone the event is in.
 */
public protocol TKAgendaDataSource {
  func items(dateComponents: NSDateComponents) -> Observable<[TKAgendaInputItem]>
}

public protocol TKAgendaBuilderType {
  func buildTrack(forItems items: [TKAgendaInputItem], startDate: NSDate, endDate: NSDate, privateVehicles: [STKVehicular], tripPatterns: [ [String: AnyObject] ]) -> Observable<[TKAgendaOutputItem]>
}

extension TKAgendaBuilderType {
  func buildTrack(forItems items: [TKAgendaInputItem], dateComponents: NSDateComponents) -> Observable<[TKAgendaOutputItem]> {
    let startDate = dateComponents.earliestDate()
    let endDate = dateComponents.latestDate()
    return buildTrack(forItems: items, startDate: startDate, endDate: endDate, privateVehicles: [], tripPatterns: [])
  }
}

/**
 Main singleton class which acts as the gateway to getting an agenda for a day, updating it, calculating it using server calls, and everything else.
 
 - Note: Make sure to initialise the singleton with the data sources it
            should use before you use it.
 */
public class TKAgendaManager {
  private static var _singleton: TKAgendaManager?
  public class var singleton: TKAgendaManager {
    if let singleton = _singleton {
      return singleton
    } else {
      fatalError("Call `initialize` first!")
    }
  }
  
  public class func initialize(builder: TKAgendaBuilderType, dataSources: [TKAgendaDataSource]) {
    guard nil == _singleton else {
      print("Ignore subsequent call to `initialize`.")
      return
    }
    _singleton = TKAgendaManager(builder: builder, dataSources: dataSources)
  }

  private init(builder: TKAgendaBuilderType, dataSources: [TKAgendaDataSource]) {
    self.dataSources = dataSources
    self.builder = builder
  }
  
  private let dataSources: [TKAgendaDataSource]
  private let builder: TKAgendaBuilderType
  
  private var agendas = [String : TKAgendaType]()
  
  public func agenda(dateComponents: NSDateComponents) -> TKAgendaType {
    let key = "\(dateComponents.year)-\(dateComponents.month)-\(dateComponents.day)"
    if let agenda = agendas[key] {
      return agenda
    }
    
    // FIXME: For now, we just use the items of the first data source
    guard let rawItems = dataSources.first?.items(dateComponents) else {
      fatalError("Data sources shalt not be empty")
    }
    
    let lastError = Variable<ErrorType?>(nil)
    let trackItems = rawItems.flatMap { data in
      // If that throws an error, we shouldn't propagate that up!<commit_msg>Should be an assert<commit_after>
//
//  RGAgendaLogic.swift
//  RioGo
//
//  Created by <NAME> on 25/02/2016.
//  Copyright © 2016 SkedGo. All rights reserved.
//

import Foundation

import RxSwift

/**
 A data source provides classes or structs conforming to the `TKAgendaInputType` protocol for
 specified date components. The date components will typically be for a specific date, but
 without timezone information. Treat this as "What has the user planned for this calendar
 date", regardless of the time zone the event is in.
 */
public protocol TKAgendaDataSource {
  func items(dateComponents: NSDateComponents) -> Observable<[TKAgendaInputItem]>
}

public protocol TKAgendaBuilderType {
  func buildTrack(forItems items: [TKAgendaInputItem], startDate: NSDate, endDate: NSDate, privateVehicles: [STKVehicular], tripPatterns: [ [String: AnyObject] ]) -> Observable<[TKAgendaOutputItem]>
}

extension TKAgendaBuilderType {
  func buildTrack(forItems items: [TKAgendaInputItem], dateComponents: NSDateComponents) -> Observable<[TKAgendaOutputItem]> {
    let startDate = dateComponents.earliestDate()
    let endDate = dateComponents.latestDate()
    return buildTrack(forItems: items, startDate: startDate, endDate: endDate, privateVehicles: [], tripPatterns: [])
  }
}

/**
 Main singleton class which acts as the gateway to getting an agenda for a day, updating it, calculating it using server calls, and everything else.
 
 - Note: Make sure to initialise the singleton with the data sources it
            should use before you use it.
 */
public class TKAgendaManager {
  private static var _singleton: TKAgendaManager?
  public class var singleton: TKAgendaManager {
    if let singleton = _singleton {
      return singleton
    } else {
      fatalError("Call `initialize` first!")
    }
  }
  
  public class func initialize(builder: TKAgendaBuilderType, dataSources: [TKAgendaDataSource]) {
    guard nil == _singleton else {
      assertionFailure("Ignore subsequent call to `initialize`.")
      return
    }
    _singleton = TKAgendaManager(builder: builder, dataSources: dataSources)
  }

  private init(builder: TKAgendaBuilderType, dataSources: [TKAgendaDataSource]) {
    self.dataSources = dataSources
    self.builder = builder
  }
  
  private let dataSources: [TKAgendaDataSource]
  private let builder: TKAgendaBuilderType
  
  private var agendas = [String : TKAgendaType]()
  
  public func agenda(dateComponents: NSDateComponents) -> TKAgendaType {
    let key = "\(dateComponents.year)-\(dateComponents.month)-\(dateComponents.day)"
    if let agenda = agendas[key] {
      return agenda
    }
    
    // FIXME: For now, we just use the items of the first data source
    guard let rawItems = dataSources.first?.items(dateComponents) else {
      fatalError("Data sources shalt not be empty")
    }
    
    let lastError = Variable<ErrorType?>(nil)
    let trackItems = rawItems.flatMap { data in
      // If that throws an error, we shouldn't propagate that up!
{'fraction_non_alphanumeric': 0.08566170319558307, 'fraction_numerical': 0.004015392337292956, 'mean_word_length': 3.940495867768595, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5306036', 'n_tokens_mistral': 1752, 'n_tokens_neox': 1661, 'n_words': 570}