import SwiftUI
import SFSafeSymbols

struct ContentView: View {

    private let deviceInfoUpdateInterval = 3.0

    private let minTempColor = Color(hue: 0.624, saturation: 0.5, brightness: 1.0)
    private let minTemperature = -20.0

    private let maxTempColor = Color(hue: 1.0, saturation: 0.5, brightness: 1.0)
    private let maxTemperature = 40.0

    private let disconnectedColor = Color(white: 0.8)

    @ObservedObject
    var scanner: BluetoothScanner

    @EnvironmentObject
    var storage: PersistentStorage

    @EnvironmentObject
    var transfer: TransferHandler

    @State
    var showDeviceInfo = false
    
    @State
    var showHistory = false

    @State
    var showLog = false

    @State
    var showDataTransferView = false

    @State
    var deviceInfoUpdateTimer: Timer?
    
    var averageTemperature: Double? {
        guard scanner.configuredDevice != nil else {
            return nil
        }
        guard let info = scanner.lastDeviceInfo else {
            return nil
        }
        let t1 = info.sensor1?.optionalValue
        guard let t0 = info.sensor0?.optionalValue else {
            return t1
        }
        guard let t1 else {
            return t0
        }
        return (t0 + t1) / 2
    }
    
    var hasTemperature: Bool {
        averageTemperature != nil
    }
    var temperatureString: String {
        guard let temp = averageTemperature else {
            return "?"
        }
        return String(format: "%.0f°", locale: .current, temp)
    }
        
    var temperatureIcon: SFSymbol {
        guard let temp = averageTemperature else {
            return .thermometerMediumSlash
        }
        guard temp > 0 else {
            return .thermometerSnowflake
        }
        guard temp > 15 else {
            return .thermometerLow
        }
        guard temp > 25 else {
            return .thermometerMedium
        }
        return .thermometerHigh
    }

    var backgroundColor: Color {
        guard let temp = averageTemperature else {
            return disconnectedColor
        }
        guard temp > minTemperature else {
            return minTempColor
        }
        guard temp < maxTemperature else {
            return maxTempColor
        }
        let ratio = (temp - minTemperature) / (maxTemperature - minTemperature)
        return minTempColor.blend(to: maxTempColor, intensity: ratio)
    }

    var backgroundGradient: Gradient {
        let color = backgroundColor
        let lighter = color.opacity(0.5)
        return .init(colors: [lighter, color])
    }

    var connectionSymbol: SFSymbol {
        if scanner.configuredDevice != nil {
            return .iphoneCircle
        }
        if !scanner.bluetoothIsAvailable {
            return .antennaRadiowavesLeftAndRightSlash
        }
        switch scanner.connectionState {
        case .noDeviceFound:
            if scanner.isScanningForDevices {
                return .iphoneRadiowavesLeftAndRightCircle
            }
            return .iphoneSlashCircle
        case .connecting:
            return .arrowRightToLineCircle
        case .discoveringService:
            return .linkCircle
        case .discoveringCharacteristic:
            return .magnifyingglassCircle
        }

    }

    var hasNoDeviceInfo: Bool {
        scanner.lastDeviceInfo == nil
    }

    var isDisconnected: Bool {
        scanner.configuredDevice == nil
    }

    var body: some View {
        VStack {
            Spacer()
            if hasTemperature {
                Text(temperatureString)
                    .font(.system(size: 150, weight: .light))
                    .foregroundColor(.white)
            } else {
                Image(systemSymbol: temperatureIcon)
                    .font(.system(size: 100, weight: .thin))
                    .foregroundColor(.gray)
            }

            Spacer()
            
            Button {
                self.showHistory = true
            } label: {
                ZStack {
                    TemperatureHistoryChart(points: $storage.recentMeasurements)
                        .frame(height: 300)
                        .background(Color.white.opacity(0.1))
                        .cornerRadius(8)
                    if storage.recentMeasurements.isEmpty {
                        Text("No recent measurements")
                            .foregroundColor(.white)
                    }
                }
            }
            HStack(alignment: .center) {
                Button {
                    self.showLog = true
                } label: {
                    Image(systemSymbol: .paperclipCircle)
                        .foregroundColor(.white)
                }
                Spacer()
                Button {
                    if scanner.isScanningForDevices {
                        scanner.isScanningForDevices = false
                    } else if scanner.isConnectingOrConnected {
                        scanner.disconnect()
                    } else {
                        scanner.isScanningForDevices = true
                    }
                } label: {
                    Image(systemSymbol: connectionSymbol)
                        .foregroundColor(.white)
                }
                .foregroundColor(.white)
                Spacer()
                if scanner.lastDeviceInfo != nil {
                    Button {
                        self.showDeviceInfo = true
                    } label: {
                        Image(systemSymbol: .infoCircle)
                            .foregroundColor(.white)
                    }
                    Spacer()
                    Button {
                        showDataTransferView = true
                    } label: {
                        Image(systemSymbol: .arrowUpArrowDownCircle)
                            .foregroundColor(.white)
                    }
                } else {
                    Image(systemSymbol: .infoCircle)
                        .foregroundColor(.gray)
                    Spacer()
                    Image(systemSymbol: .arrowUpArrowDownCircle)
                        .foregroundColor(.gray)
                }
            }
            .padding()
            .font(.system(size: 30, weight: .light))

        }
        .padding()
        .sheet(isPresented: $showDeviceInfo)  {
            if let info = scanner.lastDeviceInfo {
                DeviceInfoView(info: info, isPresented: $showDeviceInfo)
            } else {
                EmptyView()
            }
        }
        .sheet(isPresented: $showHistory) {
            HistoryList()
                .environmentObject(storage)
        }
        .sheet(isPresented: $showLog) {
            LogView()
                .environmentObject(log)
                .environmentObject(storage)
        }
        .sheet(isPresented: $showDataTransferView) {
            if let client = scanner.configuredDevice {
                TransferView(
                    bluetoothClient: client, info: $scanner.lastDeviceInfo)
                    .environmentObject(storage)
                    .environmentObject(transfer)
            } else {
                EmptyView()
            }
        }
        .background(backgroundGradient)
        .onAppear(perform: startDeviceInfoUpdates)
        .onDisappear(perform: endDeviceInfoUpdates)
    }

    // MARK: Device info updates

    private func startDeviceInfoUpdates() {
        deviceInfoUpdateTimer?.invalidate()

        log.info("Starting device info updates")
        deviceInfoUpdateTimer = Timer.scheduledTimer(withTimeInterval: deviceInfoUpdateInterval, repeats: true) { timer in
            self.updateDeviceInfo()
        }

        deviceInfoUpdateTimer?.fire()
    }

    private func updateDeviceInfo() {
        guard let bluetoothDevice = scanner.configuredDevice else {
            return
        }
        Task {
            await bluetoothDevice.updateInfo()
        }
    }

    private func endDeviceInfoUpdates() {
        deviceInfoUpdateTimer?.invalidate()
        deviceInfoUpdateTimer = nil
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        let storage = PersistentStorage(lastMeasurements: TemperatureMeasurement.mockData)
        ContentView(scanner: .init())
            .environmentObject(storage)
    }
}

extension HorizontalAlignment {
    
    private struct InfoTextAlignment: AlignmentID {
        static func defaultValue(in context: ViewDimensions) -> CGFloat {
            context[HorizontalAlignment.leading]
        }
    }

    static let infoTextAlignmentGuide = HorizontalAlignment(
        InfoTextAlignment.self
    )
}
