//
//  LKFFileAnalyser.swift
//  LKF
//
//  Created by Alexander Holzer on 11.09.24.
//  Copyright © 2024 Alexander Holzer. All rights reserved.
//

import Foundation


public class LKFFileAnalyserCategoryOption {
    public let value : String
    public let label: String
    public var lines = [String]()
    
    init (value: String, label: String? = nil) {
        self.value = value
        self.label = label ?? value
    }
    
    public func add (line: String) {
        self.lines.append(line)
    }
}

public class LKFFileAnalyserCategory {
    public let key : String
    public let label: String
    public var options : [LKFFileAnalyserCategoryOption]
    public var optionDictionary: [String:LKFFileAnalyserCategoryOption]
    public var otherLines: [String]?
    public let converter: ((_:[String]) -> String?)
    public let dynamic : Bool
    
    public var activeIndices : Int = 0
    
    public init (key: String, label: String? = nil, options: [LKFFileAnalyserCategoryOption]? = nil, dynamic: Bool? = false, converter: @escaping ((_:[String]) -> String?)) {
        self.key = key
        self.label = label ?? key
        self.options = options ?? []
        self.dynamic = dynamic ?? false
        self.converter = converter
        var dict = [String:LKFFileAnalyserCategoryOption]()
        if let options {
            for o in options {
                dict[o.value] = o
            }
            optionDictionary = dict
        }
        else {
            optionDictionary = [:]
        }
    }
    
    public func getOptionDescription (skipEmpty: Bool = true, skipOther: Bool = true, separator: String = ", ") -> String {
        var s = [String]()
        for o in options {
            if o.lines.count == 0, skipEmpty {
                continue
            }
            s.append("\(o.label):\(o.lines.count)")
        }
        
        let r = s.joined(separator: separator)
        if !skipOther, let otherLines {
            return r + " \(otherLines.count)"
        }
        return r
    }
    
    public func add(line: String, lineParts:[String]? = nil) {
        let lineParts = lineParts ?? line.components(separatedBy: "|")
        
        if let v = converter (lineParts), v.count > 0 {
            if let o = optionDictionary[v] {
                o.add(line: line)
                return
            }
            if dynamic {
                let option = LKFFileAnalyserCategoryOption(value: v)
                options.append(option)
                optionDictionary[v] = option
                option.add(line: line)
                return
            }
        }
        if otherLines != nil {
            otherLines!.append(line)
        }
        otherLines = [line]
    }
}

public class LKFFileAnalyser {

    public let categories: [LKFFileAnalyserCategory]
    public private(set) var count: Int = 0
    
    public init(categories: [LKFFileAnalyserCategory]) {
        self.categories = categories
    }
    
    public convenience init() {
        
        let ambulant = LKFFileAnalyserCategory(key: "AMBSTAT", label: "ambulant/stationär", options: [LKFFileAnalyserCategoryOption(value: "I", label: "ambulant"), LKFFileAnalyserCategoryOption(value: "S", label: "stationär")]) { parts in
                return parts.count > 6 ? parts[6] : nil
            }
        
        let aufdat = LKFFileAnalyserCategory(key: "AUFDAT", label: "Aufnahme-Monat", options: (0..<12).map { LKFFileAnalyserCategoryOption(value: String(format:"%02d", $0)) } ) { parts in
            guard parts.count >= 4 else {
                return ""
            }
            let s = parts[4]
            return s.substr(from: 4, length: 2)
        }

        let aufart = LKFFileAnalyserCategory(key: "AUFART", label: "Aufnahmeart", options: [
            LKFFileAnalyserCategoryOption(value: "A"), LKFFileAnalyserCategoryOption(value: "T")
        ], dynamic: true) { parts in
            guard parts.count > 15 else {
                return ""
            }
            return parts[15]
        }

        let entart = LKFFileAnalyserCategory(key: "ENTART", label: "Entlassungsart", options: [
            LKFFileAnalyserCategoryOption(value: "E"), LKFFileAnalyserCategoryOption(value: "T"), LKFFileAnalyserCategoryOption(value: "S")
        ], dynamic: true) { parts in
            guard parts.count > 19 else {
                return ""
            }
            return parts[19]
        }
        
        let geschl = LKFFileAnalyserCategory(key: "GESCHLECHT", label: "Geschlecht", options: [
            LKFFileAnalyserCategoryOption(value: "W"), LKFFileAnalyserCategoryOption(value: "M")
        ], dynamic: true) { parts in
            guard parts.count > 9 else {
                return ""
            }
            return parts[9]
        }

        
        self.init(categories: [ambulant, aufdat, geschl, aufart, entart])
    }
    
    
    func add (line: String) {
        
        guard line.starts(with: "X01") else {
            return
        }
        
        let parts = line.components(separatedBy: "|")
        count += 1
        
        for c in categories {
            c.add(line: line, lineParts: parts)
        }
    }
    
    public func from (fileUrl: URL, encoding: String.Encoding) throws {
        let fileContents = try String(contentsOf: fileUrl, encoding: encoding)
        for line in fileContents.components(separatedBy: .newlines) {
            add(line: line)
        }
    }
    
    public func from (data: Data) throws {
        guard let buf = String(data: data, encoding: .utf8) else {
            guard let bufascii = String(data: data, encoding: .ascii) else {
                throw LKFError("invalid format")
            }
            from (lines: bufascii.components(separatedBy: .newlines))
            return
        }
        from (lines: buf.components(separatedBy: .newlines))
    }
    
    public func from (lines: [String]) {
        for line in lines {
            add(line: line)
        }
    }
    
}

