//
//  Chrono.swift
//  SwiftyChrono
//
//  Created by Jerry Chen on 1/18/17.
//  Copyright © 2017 Potix. All rights reserved.
//

import Foundation

public enum OptionType: String { case
    morning = "morning",
    afternoon = "afternoon",
    evening = "evening",
    noon = "noon",
    night = "night",
    forwardDate = "forwardDate"
}

public enum WeekdayParseType { case
    closest,
    future
}

@objc public class Chrono: NSObject {
    /// iOS's Calender.Component to date that has 6 minutes less if the date is before 1900 (compared to JavaScript or Java)
    /// If your use case will include both be ealier than 1900 and its minutes, seconds, nanoseconds, (milliseconds)
    /// you should turn on this fix.
    public static var sixMinutesFixBefore1900 = false

    /// In some cases, a keyword for a langugage A means week, for language B could be a non-date related word.
    /// To prevent from getting conflict with other languages, we can simply set the preferred language.
    /// The idead is that if there are at least a reasult generated by the preferred language parser, the parsing will end without
    /// iterating via other languages parser. Otherwise, all parsers will be used in the parsing execution.
    public static var preferredLanguage: Language?

    /// you can set default imply hour
    public static var defaultImpliedHour: Int = 12
    /// you can set default imply minute
    public static var defaultImpliedMinute: Int = 0
    /// you can set default imply second
    public static var defaultImpliedSecond: Int = 0
    /// you can set default imply millisecond
    public static var defaultImpliedMillisecond: Int = 0
    /// Find weekday as closest or future only
    public static var weekdayParseType: WeekdayParseType = .future

    let modeOption: ModeOptio
    var parsers: [Parser] { return modeOption.parsers }
    var refiners: [Refiner] { return modeOption.refiners }

    public init(modeOption: ModeOptio = casualModeOption()) {
        self.modeOption = modeOption
    }

    public func parse(text: String, refDate: Date = Date(), opt: [OptionType: Int] = [:]) -> [ParsedResult] {
        var allResults = [ParsedResult]()

        if text.isEmpty {
            return allResults
        }

        if let lang = Chrono.preferredLanguage {
            // first phase: preferredLanguage parsers
            for parser in parsers {
                if parser.language == .english || parser.language == lang {
                    allResults += parser.execute(text: text, ref: refDate, opt: opt)
                }
            }

            // second phase: other language parsers
            if allResults.isEmpty {
                for parser in parsers {
                    if parser.language != .english && parser.language != lang {
                        allResults += parser.execute(text: text, ref: refDate, opt: opt)
                    }
                }
            }
        } else {
            for parser in parsers {
                allResults += parser.execute(text: text, ref: refDate, opt: opt)
            }
        }

        allResults.sort { $0.index < $1.index }

        for refiner in refiners {
            allResults = refiner.refine(text: text, results: allResults, opt: opt)
        }

        return allResults
    }

    @objc public func parseDate(text: String, refDate: Date = Date()) -> Date? {
        let results = self.parse(text: text, refDate: refDate, opt: [.forwardDate: 1])
        return results.first?.start.date
    }
    
    @objc public func parseSimple(text: String, refDate: Date = Date()) -> simpleResult? {
        let results = self.parse(text: text, refDate: refDate, opt: [.forwardDate: 1])
        guard let first = results.first else {
            return nil
        }
        return simpleResult(start: first.start.date, end: first.end?.date)
    }

    @objc public static let strict = Chrono(modeOption: strictModeOption())
    @objc public static let casual = Chrono(modeOption: casualModeOption())
}
