//
//  MeasurementChartView.swift
//  USB Meter
//
//  Created by Bogdan Timofte on 06/05/2020.
//  Copyright © 2020 Bogdan Timofte. All rights reserved.
//

import SwiftUI

struct MeasurementChartView: View {
    
    @EnvironmentObject private var measurements: Measurements
    
    @State var displayVoltage: Bool = false
    @State var displayCurrent: Bool = false
    @State var displayPower: Bool = true
    let xLabels: Int = 4
    let yLabels: Int = 4

    var body: some View {
        Group {
            //if measurements.power.points.count > 0 {
            VStack {
                HStack {
                    Button( action: {
                        self.displayVoltage.toggle()
                        if self.displayVoltage {
                            self.displayPower = false
                        }
                    } ) { Text("Voltage") }
                        .asEnableFeatureButton(state: displayVoltage)
                    Button( action: {
                        self.displayCurrent.toggle()
                        if self.displayCurrent {
                            self.displayPower = false
                        }
                    } ) { Text("Current") }
                        .asEnableFeatureButton(state: displayCurrent)
                    Button( action: {
                        self.displayPower.toggle()
                        if self.displayPower {
                            self.displayCurrent = false
                            self.displayVoltage = false
                        }
                    } ) { Text("Power") }
                        .asEnableFeatureButton(state: displayPower)
                }
                .padding(.bottom, 5)
                if measurements.current.context.isValid {
                    VStack {
                        GeometryReader { geometry in
                            HStack {
                                Group { // MARK: Left Legend
                                    if self.displayPower {
                                        self.yAxisLabelsView(geometry: geometry, context: self.measurements.power.context, measurementUnit: "W")
                                            .withRoundedRectangleBackground(cornerRadius: 0, foregroundColor: .red, opacity: 0.5)
                                    } else if self.displayVoltage {
                                        self.yAxisLabelsView(geometry: geometry, context: self.measurements.voltage.context, measurementUnit: "V")
                                            .withRoundedRectangleBackground(cornerRadius: 0, foregroundColor: .green, opacity: 0.5)
                                    }
                                    else if self.displayCurrent {
                                        self.yAxisLabelsView(geometry: geometry, context: self.measurements.current.context, measurementUnit: "A")
                                            .withRoundedRectangleBackground(cornerRadius: 0, foregroundColor: .blue, opacity: 0.5)
                                    }
                                }
                                ZStack { // MARK: Graph
                                    if self.displayPower {
                                        Chart(strokeColor: .red)
                                            .environmentObject(self.measurements.power)
                                            .opacity(0.5)
                                    } else {
                                        if self.displayVoltage{
                                            Chart(strokeColor: .green)
                                                .environmentObject(self.measurements.voltage)
                                                .opacity(0.5)
                                        }
                                        if self.displayCurrent{
                                            Chart(strokeColor: .blue)
                                                .environmentObject(self.measurements.current)
                                                .opacity(0.5)
                                        }
                                    }
                                    
                                    // MARK: Grid
                                    self.horizontalGuides()
                                    self.verticalGuides()
                                }
                                .withRoundedRectangleBackground( cornerRadius: 0, foregroundColor: .primary, opacity: 0.06 )
                                Group { // MARK: Right Legend
                                    self.yAxisLabelsView(geometry: geometry, context: self.measurements.current.context, measurementUnit: "A")
                                        .foregroundColor(self.displayVoltage && self.displayCurrent ? .primary : .clear)
                                        .withRoundedRectangleBackground(cornerRadius: 0, foregroundColor: .blue, opacity: 0.5)
                                }
                            }
                        }
                        xAxisLabelsView(context: self.measurements.current.context)
                            .padding(.horizontal, 10)
                        
                    }
                }
                else {
                    Text("Nothing to show!")
                }
                
            }
            .padding(10)
            .font(.footnote)
            .frame(maxWidth: .greatestFiniteMagnitude)
            .withRoundedRectangleBackground( cornerRadius: 15, foregroundColor: .primary, opacity: 0.03 )
            .padding()
        }
    }
    
    // MARK: Cu iteratie nu functioneaza deoarece view-ul din bucla nu este reimprospatat la modificare obiectului observat
    fileprivate func xAxisLabelsView(context: ChartContext) -> some View {
        var timeFormat: String?
        switch context.size.width {
        case 0..<3600: timeFormat = "HH:mm:ss"
        case 3600...86400: timeFormat = "HH:mm"
        default: timeFormat = "E:HH:MM"
        }
        return HStack {
            ForEach (1...xLabels, id: \.self) { i in
                Group {
                    Text( "\(Date(timeIntervalSince1970: context.xAxisLabel(for: i, of: self.yLabels)).format(as: timeFormat!))" )
                        .fontWeight(.semibold)
                    if i < self.xLabels {
                        Spacer()
                    }
                }
            }
        }
    }
    
    fileprivate func yAxisLabelsView(geometry: GeometryProxy, context: ChartContext, measurementUnit: String) -> some View {
        return ZStack {
            VStack {
                Text("\(context.yAxisLabel(for: 4, of: 4).format(fractionDigits: 2))")
                    .fontWeight(.semibold)
                    .padding(.top, geometry.size.height*Constants.chartUnderscan/2 )
                Spacer()
                ForEach (1..<yLabels-1, id: \.self) { i in
                    Group {
                        Text("\(context.yAxisLabel(for: self.yLabels-i, of: self.yLabels).format(fractionDigits: 2))")
                            .fontWeight(.semibold)
                        Spacer()
                    }
                }
                Text("\(context.yAxisLabel(for: 1, of: yLabels).format(fractionDigits: 2))")
                    .fontWeight(.semibold)
                    .padding(.bottom, geometry.size.height*Constants.chartUnderscan/2 )
            }
            VStack {
                Text(measurementUnit)
                    .fontWeight(.bold)
                    .padding(.top, 5)
                Spacer()
            }
        }
    }
    
    fileprivate func horizontalGuides() -> some View {
        GeometryReader { geometry in
            Path { path in
                let pading = geometry.size.height*Constants.chartUnderscan
                let height = geometry.size.height - pading
                let border = pading/2
                for i: CGFloat in stride(from: 0, through: CGFloat(self.yLabels-1), by: 1) {
                    path.addLine(from: CGPoint(x: 0, y: border + height*i/CGFloat(self.yLabels-1 )), to: CGPoint(x: geometry.size.width, y: border + height*i/CGFloat(self.yLabels-1)))
                }
            }.stroke(lineWidth: 0.25)
        }
    }
    
    fileprivate func verticalGuides() -> some View {
        GeometryReader { geometry in
            Path { path in
                
                for i: CGFloat in stride(from: 1, through: CGFloat(self.xLabels-1), by: 1) {
                    path.move(to: CGPoint(x: geometry.size.width*i/CGFloat(self.xLabels-1), y: 0) )
                    path.addLine(to: CGPoint(x: path.currentPoint!.x, y: geometry.size.height) )
                }
            }.stroke(lineWidth: 0.25)
        }
    }
    
}

struct Chart : View {
    
    @EnvironmentObject private var measurement: Measurements.Measurement
    var areaChart: Bool = false
    var strokeColor: Color = .black
    
    var body : some View {
        GeometryReader { geometry in
            if self.areaChart {
                self.path( geometry: geometry )
                    .fill(LinearGradient( gradient: .init(colors: [Color.red, Color.green]), startPoint: .init(x: 0.5, y: 0.1), endPoint: .init(x: 0.5, y: 0.9)))
            } else {
                self.path( geometry: geometry )
                    .stroke(self.strokeColor, style: StrokeStyle(lineWidth: 2, lineCap: .round, lineJoin: .round))
            }
        }
    }
    
    fileprivate func path(geometry: GeometryProxy) -> Path {
        return Path { path in
            let firstPoint = measurement.context.placeInRect(point: measurement.points.first!.point())
            path.move(to: CGPoint(x: firstPoint.x * geometry.size.width, y: firstPoint.y * geometry.size.height ) )
            for item in measurement.points.map({ measurement.context.placeInRect(point: $0.point()) }) {
                path.addLine(to: CGPoint(x: item.x * geometry.size.width, y: item.y * geometry.size.height ) )
            }
            if self.areaChart {
                let lastPointX = measurement.context.placeInRect(point: CGPoint(x: measurement.points.last!.point().x, y: measurement.context.origin.y ))
                let firstPointX = measurement.context.placeInRect(point: CGPoint(x: measurement.points.first!.point().x, y: measurement.context.origin.y ))
                path.addLine(to: CGPoint(x: lastPointX.x * geometry.size.width, y: lastPointX.y * geometry.size.height ) )
                path.addLine(to: CGPoint(x: firstPointX.x * geometry.size.width, y: firstPointX.y * geometry.size.height ) )
                // MARK: Nu e nevoie. Fill inchide automat calea
                // path.closeSubpath()
            }
        }
    }
    
}
