import Foundation
import SwiftUI

public struct ScrollViewWithScrollBar<Content: View>: View {
    let INDICATOR_HEIGHT: CGFloat = 4
    @State var position = CGPoint(x: 0, y: 2)  //2 is INDICATOR_HEIGHT/2
    @State var contentSize: CGFloat = 0
    @State var scrollViewWidth: CGFloat = 0
    var indicatorWidth: CGFloat
    var indicatorContainerWidth: CGFloat
    var indicatorColor: Color
    var indicatorContainerColor: Color
    var spacing: CGFloat = 4
    @ViewBuilder var content: () -> Content

    public init(
        indicatorWidth: CGFloat = 12, indicatorContainerWidth: CGFloat = 36,
        indicatorColor: Color = Colors.primary, indicatorContainerColor: Color = Colors.black06,
        spacing: CGFloat = 4,
        @ViewBuilder content: @escaping () -> Content
    ) {
        self.indicatorWidth = indicatorWidth
        self.indicatorContainerWidth = indicatorContainerWidth
        self.indicatorColor = indicatorColor
        self.indicatorContainerColor = indicatorContainerColor
        self.content = content
        self.spacing = spacing
    }

  // MARK: Public

    public var body: some View {
        return VStack(spacing: spacing) {
        ScrollView(.horizontal, showsIndicators: false) {
            HStack {
            content()
            }
            .fixedSize(horizontal: true, vertical: false)
            .overlay(
            GeometryReader { proxy in
                Color.clear
                .preference(
                    key: ViewOffsetKey.self,
                    value: -proxy.frame(in: .named("scroll")).origin.x
                )
                .preference(
                    key: ContentSizeKey.self,
                    value: proxy.size.width)
            },
            alignment: .topLeading
            )
            .onPreferenceChange(ContentSizeKey.self) { newContentSize in
            if newContentSize > 0 {
                self.contentSize = newContentSize
                if self.position == CGPoint(x: 0, y: 2) {
                self.position = CGPoint(x: indicatorWidth / 2, y: INDICATOR_HEIGHT / 2)
                }
            }
            }
            .onPreferenceChange(ViewOffsetKey.self) { offset in
            let newPosition =
                (offset * ((indicatorContainerWidth - indicatorWidth) / (contentSize - scrollViewWidth)))
                + indicatorWidth / 2

            if !newPosition.isNaN {
                self.position = CGPoint(x: newPosition, y: INDICATOR_HEIGHT / 2)
            }
            }
        }.background(
            GeometryReader { proxy in
            Color.clear.onAppear {
                self.scrollViewWidth = proxy.size.width
            }
            }
        ).coordinateSpace(name: "scroll")

        HStack(spacing: 0) {
            HStack(spacing: 0) {}.frame(width: indicatorWidth, height: INDICATOR_HEIGHT)
            .background(indicatorColor)
            .cornerRadius(8)
            .position(self.position)
        }.frame(width: indicatorContainerWidth, height: INDICATOR_HEIGHT, alignment: .leading)
            .background(indicatorContainerColor).cornerRadius(8)
        }
    }
}

// MARK: - ViewOffsetKey

struct ViewOffsetKey: PreferenceKey {
    typealias Value = CGFloat

    static var defaultValue = CGFloat.zero

    static func reduce(value: inout Value, nextValue: () -> Value) {
        value += nextValue()
    }
}

// MARK: - ContentSizeKey

struct ContentSizeKey: PreferenceKey {
    typealias Value = CGFloat

    static var defaultValue = CGFloat.zero

    static func reduce(value: inout Value, nextValue: () -> Value) {
        value = nextValue()
    }
}
