import Foundation import RealmSwift import DifferenceKit class VehicleName: Object, Decodable, Cloneable { @objc dynamic var original: String? @objc dynamic var normalized: String? required init(copy: VehicleName) { self.original = copy.original self.normalized = copy.normalized } required init() { } } class VehicleBrand: Object, Decodable, Cloneable { @objc dynamic var name: VehicleName? @objc dynamic var logo: String? required init(copy: VehicleBrand) { self.name = copy.name?.clone() self.logo = copy.logo } required init() { super.init() } } class VehicleModel: Object, Decodable, Cloneable { @objc dynamic var name: VehicleName? required init(copy: VehicleModel) { self.name = copy.name?.clone() } required init() { super.init() } } class VehicleEngine: Object, Decodable, Cloneable { @objc dynamic var number: String? var volume: RealmOptional = RealmOptional(0) @objc dynamic var powerHp: Float = 0 var powerKw: RealmOptional = RealmOptional(0) @objc dynamic var fuelType: String? enum CodingKeys: String, CodingKey { case number, volume, powerHp, powerKw, fuelType } required init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.number = try container.decodeIfPresent(String.self, forKey: .number) self.volume = RealmOptional(try container.decodeIfPresent(Int.self, forKey: .volume)) self.powerHp = try container.decode(Float.self, forKey: .powerHp) self.powerKw = RealmOptional(try container.decodeIfPresent(Float.self, forKey: .powerKw)) self.fuelType = try container.decodeIfPresent(String.self, forKey: .fuelType) } required init() { super.init() } required init(copy: VehicleEngine) { self.number = copy.number self.volume = copy.volume self.powerHp = copy.powerHp self.powerKw = copy.powerKw self.fuelType = copy.fuelType } } class VehiclePhoto: Object, Decodable, Cloneable { @objc dynamic var brand: String? @objc dynamic var model: String? @objc dynamic var date: TimeInterval = 0 @objc dynamic var url: String = "" override var description: String { let formatter = DateFormatter() formatter.timeZone = TimeZone(identifier:"GMT") formatter.dateStyle = .medium formatter.timeStyle = .none let date = Date(timeIntervalSince1970: self.date/1000) let dateStr = formatter.string(from: date) return "\(self.brand ?? "") \(self.model ?? "") (\(dateStr))" } required init(copy: VehiclePhoto) { self.brand = copy.brand self.model = copy.model self.date = copy.date self.url = copy.url } required init() { super.init() } } enum OwnerType: String, CustomStringConvertible { case legal case individual var description: String { switch self { case .legal: return NSLocalizedString("legal", comment: "Owner type") case .individual: return NSLocalizedString("individual", comment: "Owner type") } } } enum SteeringWheelPosition: CustomStringConvertible { case left case right case unknown var description: String { switch self { case .left: return "Left" case .right: return "Right" case .unknown: return "Unknown" } } } class VehicleOwnershipPeriod: Object, Decodable, Cloneable { @objc dynamic var lastOperation: String = "" @objc dynamic var ownerType: String = "" @objc dynamic var from: Int64 = 0 @objc dynamic var to: Int64 = 0 @objc dynamic var region: String? @objc dynamic var registrationRegion: String? @objc dynamic var locality: String? @objc dynamic var code: String? @objc dynamic var street: String? @objc dynamic var building: String? @objc dynamic var inn: String? required init(copy: VehicleOwnershipPeriod) { self.lastOperation = copy.lastOperation self.ownerType = copy.ownerType self.from = copy.from self.to = copy.to self.region = copy.region self.registrationRegion = copy.registrationRegion self.locality = copy.locality self.code = copy.code self.street = copy.street self.building = copy.building self.inn = copy.inn } required init() { super.init() } } class Vehicle: Object, Decodable, Identifiable, Differentiable, Cloneable { @objc dynamic var brand: VehicleBrand? @objc dynamic var model: VehicleModel? @objc dynamic var color: String? @objc dynamic var year: Int = 0 @objc dynamic var category: String? @objc dynamic var engine: VehicleEngine? @objc private dynamic var number: String = "" @objc dynamic var currentNumber: String? @objc dynamic var vin1: String? @objc dynamic var vin2: String? @objc dynamic var sts: String? @objc dynamic var pts: String? var isRightWheel = RealmOptional() var isJapanese: RealmOptional = RealmOptional() @objc dynamic var addedDate: TimeInterval = 0 @objc dynamic var updatedDate: TimeInterval = 0 @objc dynamic var addedBy: String = "" var photos = List() var ownershipPeriods = List() var events = List() var osagoContracts = List() var ads = List() var differenceIdentifier: String { self.number } func isContentEqual(to source: Vehicle) -> Bool { return self == source } enum CodingKeys: String, CodingKey { case brand case model case color case year case category case engine case number case currentNumber case vin1 case vin2 case sts case pts case isRightWheel case isJapanese case addedDate case addedBy case photos case ownershipPeriods case events case osagoContracts case ads } required init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.brand = try container.decodeIfPresent(VehicleBrand.self, forKey: .brand) self.model = try container.decodeIfPresent(VehicleModel.self, forKey: .model) self.color = try container.decodeIfPresent(String.self, forKey: .color) self.year = try container.decodeIfPresent(Int.self, forKey: .year) ?? 0 self.category = try container.decodeIfPresent(String.self, forKey: .category) self.number = try container.decode(String.self, forKey: .number) self.engine = try container.decodeIfPresent(VehicleEngine.self, forKey: .engine) self.currentNumber = try container.decodeIfPresent(String.self, forKey: .currentNumber) self.vin1 = try container.decodeIfPresent(String.self, forKey: .vin1) self.vin2 = try container.decodeIfPresent(String.self, forKey: .vin2) self.sts = try container.decodeIfPresent(String.self, forKey: .sts) self.pts = try container.decodeIfPresent(String.self, forKey: .pts) self.isRightWheel = RealmOptional(try container.decodeIfPresent(Bool.self, forKey: .isRightWheel)) self.isJapanese = RealmOptional(try container.decodeIfPresent(Bool.self, forKey: .isJapanese)) self.addedDate = (try container.decode(TimeInterval.self, forKey: .addedDate))/1000 self.addedBy = try container.decode(String.self, forKey: .addedBy) if let photosArray = try container.decodeIfPresent([VehiclePhoto].self, forKey: .photos) { self.photos.append(objectsIn: photosArray) } if let ownersipsArray = try container.decodeIfPresent([VehicleOwnershipPeriod].self, forKey: .ownershipPeriods) { self.ownershipPeriods.append(objectsIn: ownersipsArray) } if let eventsArray = try container.decodeIfPresent([VehicleEvent].self, forKey: .events) { self.events.append(objectsIn: eventsArray) } if let lastEventDate = self.events.max(by: { $0.date < $1.date })?.date { self.updatedDate = max(self.addedDate, lastEventDate) } else { self.updatedDate = self.addedDate } if let osago = try container.decodeIfPresent([Osago].self, forKey: .osagoContracts) { self.osagoContracts.append(objectsIn: osago) } if let ads = try container.decodeIfPresent([VehicleAd].self, forKey: .ads) { self.ads.append(objectsIn: ads) } } required init() { super.init() } init(_ number: String) { self.number = number self.addedDate = Date().timeIntervalSince1970 self.updatedDate = self.addedDate } func getNumber() -> String { return self.number } override static func primaryKey() -> String? { return "number" } override class func ignoredProperties() -> [String] { return ["id", "identifier", "differenceIdentifier"] } var unrecognized: Bool { return self.brand == nil } var outdated: Bool { if let current = self.currentNumber { return current != self.number } else { return false } } // MARK: - Cloneable required init(copy: Vehicle) { self.brand = copy.brand self.model = copy.model self.color = copy.color self.year = copy.year self.category = copy.category self.engine = copy.engine self.number = copy.number self.currentNumber = copy.currentNumber self.vin1 = copy.vin1 self.vin2 = copy.vin2 self.sts = copy.sts self.pts = copy.pts self.isRightWheel = copy.isRightWheel self.isJapanese = copy.isJapanese self.addedDate = copy.addedDate self.addedBy = copy.addedBy self.updatedDate = copy.updatedDate let photos = List() photos.append(objectsIn: copy.photos.map { $0.clone() }) self.photos = photos let ownerships = List() ownerships.append(objectsIn: copy.ownershipPeriods.map { $0.clone() }) self.ownershipPeriods = ownerships let events = List() events.append(objectsIn: copy.events.map { $0.clone() }) self.events = events let osago = List() osago.append(objectsIn: copy.osagoContracts.map { $0.clone() }) self.osagoContracts = osago let ads = List() ads.append(objectsIn: copy.ads.map { $0.clone() }) self.ads = ads } }