"use strict";

import React from "react";
import {
    StyleSheet,
    ScrollView,
    Animated,
    View,
    StyleProp,
    ViewStyle,
    TextStyle,
    Easing,
    processColor,
    LayoutRectangle,
} from "react-native";
import TabItem, {TabItemProps, RouteProps} from "./TabItem";
import Indicator from "./Indicator";

interface EndResult {
    finished: boolean;
}

type EndCallback = (result: EndResult) => void;

interface CompositeAnimation {
    start: (callback?: EndCallback) => void;
    stop: () => void;
}

const styles = StyleSheet.create({
    tabBar: {
        minHeight: 45,
        flexDirection: "row",
        justifyContent: "center",
        alignItems: "center",
    },
    contentContainerStyle: {
        height: "100%",
        paddingVertical: 8,
    },
});

// function getInterpolate(
//   tabs: Array<{ key: string; title: string }>,
//   index: number,
//   isActive: boolean,
//   category: 'color' | 'scale',
// ) {
//   const inputRange: Array<number | string> = [];
//   const outputRange: Array<number | string> = [];
//   tabs.forEach((item, i) => {
//     inputRange.push(i);
//     if (category === 'color') {
//       outputRange.push(isActive && i === index ? '#000000' : '#333333');
//     }
//     if (category === 'scale') {
//       outputRange.push(isActive && i === index ? 1.5 : 1);
//     }
//   });
//   return { inputRange, outputRange };
// }

interface TabBarProps
    extends Omit<TabItemProps,
        "style" | "onTabLayout" | "onLabelLayout" | "onPress" | "route"> {
    style?: StyleProp<ViewStyle>;
    contentContainerStyle?: StyleProp<ViewStyle>;
    tabStyle?: StyleProp<ViewStyle>;
    labelStyle?: StyleProp<TextStyle>;
    labelSelectStyle?: StyleProp<TextStyle>;
    indicatorStyle?: StyleProp<ViewStyle>;
    activeColor?: string;
    inactiveColor?: string;
    renderLabel?: ({
                       route,
                       isActive,
                   }: {
        route: RouteProps;
        isActive?: boolean;
    }) => React.ReactNode;
    renderIcon?: ({
                      route,
                      isActive,
                  }: {
        route: RouteProps;
        isActive?: boolean;
    }) => React.ReactNode;
    renderBadge?: ({
                       route,
                       isActive,
                   }: {
        route: RouteProps;
        isActive?: boolean;
    }) => React.ReactNode;
    renderIndicator?: ({
                           indexValue,
                           leftValue,
                           widthValue,
                       }: {
        indexValue: Animated.Value;
        leftValue: Animated.Value;
        widthValue: Animated.Value;
    }) => React.ReactNode;
    renderLeftSection?: () => React.ReactNode;
    renderRightSection?: () => React.ReactNode;
    routes: Array<RouteProps>;
    bounces?: boolean;
    scrollEnabled?: boolean;
    initialIndex?: number;
    onTabPress?: (index: number) => void;
    onTabChange?: (index: number) => void;
    indicatorMode?: "tab" | "label";
    indicatorWidthRatio?: number;
    tabMode?: "scrollable" | "fixed";
}

interface TabBarState {
    selectedIndex: number;
}


class TabBar extends React.PureComponent<TabBarProps, TabBarState> {
    static defaultProps = {
        initialIndex: 0,
        bounces: true,
        scrollEnabled: true,
        indicatorMode: "tab",
        indicatorWidthRatio: 1,
        tabMode: "fixed",
    };

    indexValue: Animated.Value;
    indicatorWidthValue: Animated.Value;
    indicatorLeftValue: Animated.Value;
    labelColorValue: Animated.Value;
    labelScaleValue: Animated.Value;
    scrollView: React.RefObject<ScrollView>;
    scrollViewWidth: number;
    scrollViewHeight: number;
    tabItemLayout: { [key: number]: LayoutRectangle } = {};
    tabLabelLayout: { [key: number]: LayoutRectangle } = {};
    // @ts-ignore
    timer: NodeJS.Timeout | null;
    selectedIndex: number;

    constructor(props: TabBarProps) {
        super(props);
        this.indexValue = new Animated.Value(0);
        this.indicatorWidthValue = new Animated.Value(0);
        this.indicatorLeftValue = new Animated.Value(0);
        this.labelColorValue = new Animated.Value(processColor("#333333"));
        this.labelScaleValue = new Animated.Value(1.5);
        this.scrollView = React.createRef<ScrollView>();
        this.scrollViewWidth = 0;
        this.scrollViewHeight = 0;
        this.tabItemLayout = {};
        this.timer = null;
        this.selectedIndex = 0;

        this.state = {
            selectedIndex: 0,
        };
    }

    // componentDidMount() {
    //   this.timer = setTimeout(() => {
    //     if (this.state.selectedIndex === -1 && !!this.scrollViewWidth && !!this.scrollViewHeight) {
    //       this.scrollToIndex(this.props.initialIndex || 0);
    //       console.warn('ds');
    //     }
    //   }, 200);
    // }

    // UNSAFE_componentWillReceiveProps(nextProps: TabBarProps) {
    //   console.warn('UNSAFE_componentWillReceiveProps');
    //   if (this.props.routes !== nextProps.routes) {
    //     this.scrollToIndex(this.state.selectedIndex);
    //   }
    // }

    // componentDidUpdate() {
    //   console.warn('componentDidUpdate');
    // }

    // static getDerivedStateFromProps(props, state) {
    //   console.warn('getDerivedStateFromProps');
    //   return null;
    // }

    componentWillUnmount() {
        this.timer && clearTimeout(this.timer);
    }

    scrollToIndex(index: number, callback?: () => void) {
        if (index === this.selectedIndex && index !== this.props.initialIndex) {
            return;
        }
        if (!this.tabItemLayout[index] || !this.tabLabelLayout[index]) {
            return;
        }

        const animated =
            this.props.initialIndex === index ||
            Math.abs(index - this.selectedIndex) === 1;
        const animations: Array<Animated.CompositeAnimation> = [];
        const {x, width} = this.tabItemLayout[index];
        const {width: labelWidth} = this.tabLabelLayout[index];
        const {indicatorMode, indicatorWidthRatio = 1} = this.props;

        let ratio = indicatorWidthRatio % 1;
        ratio = ratio === 0 && indicatorWidthRatio >= 1 ? 1 : ratio;

        let leftValue = x + (width - width * ratio) / 2;
        let widthValue = width * ratio;

        if (indicatorMode === "label") {
            leftValue = x + (width - labelWidth * ratio) / 2;
            widthValue = labelWidth * ratio;
        }

        if (animated) {
            // animations.push(
            //   Animated.spring(this.indexValue, { toValue: index, friction: 12, tension: 40, useNativeDriver: false }),
            // );
            // animations.push(
            //   Animated.spring(this.indicatorLeftValue, {
            //     toValue: leftValue,
            //     friction: 12,
            //     tension: 40,
            //     useNativeDriver: false,
            //   }),
            // );
            // animations.push(
            //   Animated.spring(this.indicatorWidthValue, {
            //     toValue: widthValue,
            //     friction: 12,
            //     tension: 40,
            //     useNativeDriver: false,
            //   }),
            // );
            animations.push(
                Animated.timing(this.indexValue, {
                    toValue: index,
                    duration: 200,
                    easing: Easing.linear,
                    useNativeDriver: false,
                })
            );
            animations.push(
                Animated.timing(this.indicatorLeftValue, {
                    toValue: leftValue,
                    duration: 200,
                    easing: Easing.linear,
                    useNativeDriver: false,
                })
            );
            animations.push(
                Animated.timing(this.indicatorWidthValue, {
                    toValue: widthValue,
                    duration: 200,
                    easing: Easing.linear,
                    useNativeDriver: false,
                })
            );
        } else {
            animations.push(
                Animated.timing(this.indexValue, {
                    toValue: index,
                    useNativeDriver: false,
                })
            );
            animations.push(
                Animated.timing(this.indicatorLeftValue, {
                    toValue: leftValue,
                    useNativeDriver: false,
                })
            );
            animations.push(
                Animated.timing(this.indicatorWidthValue, {
                    toValue: widthValue,
                    useNativeDriver: false,
                })
            );
        }

        const lastTabItemLayout = this.tabItemLayout[this.props.routes.length - 1];
        if (this.scrollView && lastTabItemLayout) {
            const pageCenterX = (this.scrollViewWidth - width) / 2;
            const firstX = 0 + pageCenterX;
            const lastX = lastTabItemLayout.x - pageCenterX;
            // console.warn(x, pageCenterX, firstX, lastX);

            if (x < firstX) {
                this.scrollView.current &&
                this.scrollView.current.scrollTo({x: 0, y: 0, animated: true});
            } else if (x > lastX) {
                this.scrollView.current &&
                this.scrollView.current.scrollToEnd({animated: true});
            } else {
                const dx = x - pageCenterX;
                this.scrollView.current &&
                this.scrollView.current.scrollTo({x: dx, y: 0, animated: true});
            }
        }

        this.selectedIndex = index;
        this.setState({selectedIndex: index});
        Animated.parallel(animations).start(callback);
    }

    render() {
        const {
            style,
            contentContainerStyle,
            tabStyle,
            labelStyle,
            labelSelectStyle,
            indicatorStyle,
            activeColor,
            inactiveColor,
            renderLabel,
            renderIcon,
            renderBadge,
            renderIndicator,
            renderLeftSection,
            renderRightSection,
            routes,
            bounces,
            scrollEnabled,
            onTabPress,
            onTabChange,
            tabMode,
        } = this.props;
        const {selectedIndex} = this.state;
        return (
            <View style={[styles.tabBar, style]}>
                {renderLeftSection && renderLeftSection()}
                <ScrollView
                    style={{flex: 1, height: "100%"}}
                    ref={this.scrollView}
                    contentContainerStyle={[
                        styles.contentContainerStyle,
                        contentContainerStyle,
                        tabMode === "fixed" ? {width: "100%"} : {},
                    ]}
                    horizontal
                    showsHorizontalScrollIndicator={false}
                    scrollEnabled={scrollEnabled}
                    bounces={bounces}
                    onLayout={({
                                   nativeEvent: {
                                       layout: {width, height},
                                   },
                               }) => {
                        this.scrollViewWidth = width;
                        this.scrollViewHeight = height;
                    }}
                >
                    {routes.map((route: RouteProps, index: number) => {
                        const isActive = index === selectedIndex;
                        return (
                            <TabItem
                                route={route}
                                style={[tabStyle, tabMode === "fixed" ? {flex: 1} : {}]}
                                key={`TabItem${String(index)}`}
                                onTabLayout={({nativeEvent: {layout}}) => {
                                    // console.warn(`tabItem${index}Layout`, layout);
                                    this.tabItemLayout[index] = layout;
                                }}
                                onLabelLayout={({nativeEvent: {layout}}) => {
                                    // console.warn(`tabItem${index}Layout`, layout);
                                    this.tabLabelLayout[index] = layout;
                                }}
                                isActive={isActive}
                                activeColor={activeColor}
                                inactiveColor={inactiveColor}
                                onPress={() => {
                                    onTabPress && onTabPress(index);
                                    this.scrollToIndex(index, () => {
                                        onTabChange && onTabChange(index);
                                    });
                                }}
                                labelStyle={labelStyle}
                                labelSelectStyle={labelSelectStyle}
                                colorValue={this.labelColorValue}
                                scaleValue={this.labelScaleValue}
                                renderLabel={renderLabel}
                                renderIcon={renderIcon}
                                renderBadge={renderBadge}
                            />
                        );
                    })}
                    {renderIndicator ? (
                        renderIndicator({
                            indexValue: this.indexValue,
                            leftValue: this.indicatorLeftValue,
                            widthValue: this.indicatorWidthValue,
                        })
                    ) : (
                        <Indicator
                            style={indicatorStyle}
                            leftValue={this.indicatorLeftValue}
                            widthValue={this.indicatorWidthValue}
                        />
                    )}

                </ScrollView>
                {renderRightSection && renderRightSection()}
            </View>
        );
    }
}

export default TabBar;
