/***
 * DONE: Reduce layout processing on data insert
 * DONE: Add notify data set changed and notify data insert option in data source
 * DONE: Add on end reached callback
 * DONE: Make another class for render stack generator
 * DONE: Simplify rendering a loading footer
 * DONE: Anchor first visible index on any insert/delete data wise
 * DONE: Build Scroll to index
 * DONE: Give viewability callbacks
 * DONE: Add full render logic in cases like change of dimensions
 * DONE: Fix all proptypes
 * DONE: Add Initial render Index support
 * TODO: Destroy less frequently used items in recycle pool, this will help in case of too many types.
 * TODO: Add animated scroll to web scrollviewer
 * TODO: Animate list view transition, including add/remove
 * TODO: Implement sticky headers
 * TODO: Make viewability callbacks configurable
 * TODO: Observe size changes on web to optimize for reflowability
 * TODO: Solve //TSI
 */
//@ts-ignore
import {debounce} from "lodash-es";
import * as PropTypes from "prop-types";
import * as React from "react";
//@ts-ignore
import {ObjectUtil, Default} from "ts-object-utils";
import ContextProvider from "./dependencies/ContextProvider";
import DataProvider from "./dependencies/DataProvider";
import LayoutProvider, {Dimension} from "./dependencies/LayoutProvider";
import CustomError from "./exceptions/CustomError";
import RecyclerListViewExceptions from "./exceptions/RecyclerListViewExceptions";
import LayoutManager, {Point, Rect} from "./layoutmanager/LayoutManager";
import Messages from "./messages/Messages";
import BaseScrollComponent from "./scrollcomponent/BaseScrollComponent";
import BaseScrollView, {ScrollEvent, ScrollViewDefaultProps} from "./scrollcomponent/BaseScrollView";
import {TOnItemStatusChanged} from "./ViewabilityTracker";
import VirtualRenderer, {RenderStack, RenderStackItem, RenderStackParams} from "./VirtualRenderer";
import ItemAnimator, {BaseItemAnimator} from "./ItemAnimator";
import {ScrollView} from 'react-native';

//#if [REACT-NATIVE]
import ViewRenderer from "../platform/reactnative/viewrenderer/ViewRenderer";
import {DefaultJSItemAnimator as DefaultItemAnimator} from "../platform/reactnative/itemanimators/defaultjsanimator/DefaultJSItemAnimator";
import {Platform} from "react-native";
import PullRefreshScrollView from "../platform/reactnative/scrollcomponent/PullRefreshScrollView";
import {Dimensions, View, Text, StyleSheet} from "react-native";
import TSCast from "react-native-refresh-loadmore-recyclerlistview/utils/TSCast";

const IS_WEB = Platform.OS === "web",
//#endif

    /***
     * To use on web, start importing from recyclerlistview/web. To make it even easier specify an alias in you builder of choice.
     */

//#if [WEB]
//import ScrollComponent from "../platform/web/scrollcomponent/ScrollComponent";
//import ViewRenderer from "../platform/web/viewrenderer/ViewRenderer";
//import { DefaultWebItemAnimator as DefaultItemAnimator} from "../platform/web/itemanimators/DefaultWebItemAnimator";
//const IS_WEB = true;
//#endif

    refreshRequestDebouncer = debounce((executable: () => void) => {
        executable();
    });

/***
 * This is the main component, please refer to samples to understand how to use.
 * For advanced usage check out prop descriptions below.
 * You also get common methods such as: scrollToIndex, scrollToItem, scrollToTop, scrollToEnd, scrollToOffset, getCurrentScrollOffset,
 * findApproxFirstVisibleIndex.
 * You'll need a ref to Recycler in order to call these
 * Needs to have bounded size in all cases other than window scrolling (web).
 *
 * NOTE: React Native implementation uses ScrollView internally which means you get all ScrollView features as well such as Pull To Refresh, paging enabled
 *       You can easily create a recycling image flip view using one paging enabled flag. Read about ScrollView features in official
 *       react native documentation.
 * NOTE: If you see blank space look at the renderAheadOffset prop and make sure your data provider has a good enough rowHasChanged method.
 *       Blanks are totally avoidable with this listview.
 * NOTE: Also works on web (experimental)
 * NOTE: For reflowability set canChangeSize to true (experimental)
 */

export interface RecyclerListViewProps {
    layoutProvider: LayoutProvider;
    dataProvider: DataProvider;
    rowRenderer: (type: string | number, data: any, index: number) => JSX.Element | JSX.Element[] | null;
    contextProvider?: ContextProvider;
    renderAheadOffset?: number;
    isHorizontal?: boolean;
    onScroll?: (rawEvent: ScrollEvent, offsetX: number, offsetY: number) => void;
    onEndReached?: () => void;
    onEndReachedThreshold?: number;
    onVisibleIndexesChanged?: TOnItemStatusChanged;
    renderFooter?: () => JSX.Element | JSX.Element[] | null;
    externalScrollView?: { new(props: ScrollViewDefaultProps): BaseScrollView };
    initialOffset?: number;
    initialRenderIndex?: number;
    scrollThrottle?: number;
    canChangeSize?: boolean;
    distanceFromWindow?: number;
    useWindowScroll?: boolean;
    disableRecycling?: boolean;
    forceNonDeterministicRendering?: boolean;
    extendedState?: object;
    itemAnimator?: ItemAnimator;
    flag?: string;

    //For all props that need to be proxied to inner/external scrollview. Put them in an object and they'll be spread
    //and passed down. For better typescript support.
    scrollViewProps?: object;
    onRefresh?: any;
    onLoadMore?: any;
    useLoadMore?: boolean;
}

export interface RecyclerListViewState {
    renderStack: RenderStack;
}

export default class RecyclerListView extends React.Component<RecyclerListViewProps, RecyclerListViewState> {
    public static defaultProps = {
        canChangeSize: false,
        disableRecycling: false,
        initialOffset: 0,
        initialRenderIndex: 0,
        isHorizontal: false,
        onEndReachedThreshold: 0,
        renderAheadOffset: IS_WEB ? 1000 : 250,
    };

    public static propTypes = {};

    private _onEndReachedCalled = false;

    private _virtualRenderer: VirtualRenderer;

    private _initComplete = false;
    private _relayoutReqIndex: number = -1;
    private _params: RenderStackParams = {
        initialOffset: 0,
        initialRenderIndex: 0,
        isHorizontal: false,
        itemCount: 0,
        renderAheadOffset: 250,
    };
    private _layout: Dimension = {height: 0, width: 0};
    private _pendingScrollToOffset: Point | null = null;
    private _tempDim: Dimension = {height: 0, width: 0};
    private _initialOffset = 0;
    private _cachedLayouts?: Rect[];
    private _scrollComponent: any;

    private _defaultItemAnimator: ItemAnimator = new DefaultItemAnimator();
    private default_props;

    constructor(props: RecyclerListViewProps) {
        super(props);
        this.default_props = {
            refreshedText: '释放立即刷新',
            refreshingText: '正在刷新数据中..',
            refreshText: '下拉可以刷新',
            endText: '',
            noDataText: '',
            endingText: '',
            indicatorArrowImg: {
                style: [],
                url: ''
            },
            indicatorImg: {
                style: [],
                url: ''
            },
            refreshType: 'normal',
            onRefresh: props.onRefresh,
            useLoadMore: props.useLoadMore,
            stickyHeaderIndices: null,
            onSizeChanged: null,
            canChangeSize: false,
            isHorizontal: false,
            contentHeight: 0,
            contentWidth: 0,
            onLoadMore: props.onEndReached,
            externalScrollView: TSCast.cast(ScrollView), //TSI
            scrollThrottle: 16,
        };
        this._onScroll = this._onScroll.bind(this);
        this._onSizeChanged = this._onSizeChanged.bind(this);
        this._dataHasChanged = this._dataHasChanged.bind(this);
        this.scrollToOffset = this.scrollToOffset.bind(this);
        this._renderStackWhenReady = this._renderStackWhenReady.bind(this);
        this._onViewContainerSizeChange = this._onViewContainerSizeChange.bind(this);

        this._virtualRenderer = new VirtualRenderer(this._renderStackWhenReady, (offset) => {
            this._pendingScrollToOffset = offset;
        }, !props.disableRecycling);

        this.state = {
            renderStack: {},
        };
    }

    public componentWillReceiveProps(newProps: RecyclerListViewProps): void {
        this._assertDependencyPresence(newProps);
        this._checkAndChangeLayouts(newProps);
        if (!this.props.onVisibleIndexesChanged) {
            this._virtualRenderer.removeVisibleItemsListener();
        } else {
            this._virtualRenderer.attachVisibleItemsListener(this.props.onVisibleIndexesChanged);
        }
    }

    public componentDidUpdate(): void {
        if (this._pendingScrollToOffset) {
            const offset = this._pendingScrollToOffset;

            this._pendingScrollToOffset = null;
            if (this.props.isHorizontal) {
                offset.y = 0;
            } else {
                offset.x = 0;
            }
            setTimeout(() => {
                this.scrollToOffset(offset.x, offset.y, false);
            }, 0);
        }
        this._processOnEndReached();
        this._checkAndChangeLayouts(this.props);
    }

    public componentWillUnmount(): void {
        if (this.props.contextProvider) {
            const uniqueKey = this.props.contextProvider.getUniqueKey();

            if (uniqueKey) {
                this.props.contextProvider.save(uniqueKey, this.getCurrentScrollOffset());
                if (this.props.forceNonDeterministicRendering) {
                    if (this._virtualRenderer) {
                        const layoutManager = this._virtualRenderer.getLayoutManager();

                        if (layoutManager) {
                            const layoutsToCache = layoutManager.getLayouts();

                            this.props.contextProvider.save(uniqueKey + "_layouts", JSON.stringify({layoutArray: layoutsToCache}));
                        }
                    }
                }
            }
        }
    }

    public componentWillMount(): void {
        if (this.props.contextProvider) {
            const uniqueKey = this.props.contextProvider.getUniqueKey();

            if (uniqueKey) {
                const offset = this.props.contextProvider.get(uniqueKey);

                if (typeof offset === "number" && offset > 0) {
                    this._initialOffset = offset;
                }
                if (this.props.forceNonDeterministicRendering) {
                    const cachedLayouts = this.props.contextProvider.get(uniqueKey + "_layouts") as string;

                    if (cachedLayouts && typeof cachedLayouts === "string") {
                        this._cachedLayouts = JSON.parse(cachedLayouts).layoutArray;
                    }
                }
                this.props.contextProvider.remove(uniqueKey);
            }
        }
    }

    public scrollToIndex(index: number, animate?: boolean): void {
        const layoutManager = this._virtualRenderer.getLayoutManager();

        if (layoutManager) {
            const offsets = layoutManager.getOffsetForIndex(index);

            this.scrollToOffset(offsets.x, offsets.y, animate);
        } else {
            console.warn(Messages.WARN_SCROLL_TO_INDEX); //tslint:disable-line
        }
    }

    public scrollToItem(data: any, animate?: boolean): void {
        const count = this.props.dataProvider.getSize();

        for (let i = 0; i < count; i++) {
            if (this.props.dataProvider.getDataForIndex(i) === data) {
                this.scrollToIndex(i, animate);
                break;
            }
        }
    }

    public scrollToTop(animate?: boolean): void {
        this.scrollToOffset(0, 0, animate);
    }

    public scrollToEnd(animate?: boolean): void {
        const lastIndex = this.props.dataProvider.getSize() - 1;

        this.scrollToIndex(lastIndex, animate);
    }

    public scrollToOffset(x: number, y: number, animate: boolean = false): void {
        if (this._scrollComponent) {
            this._scrollComponent.scrollTo(x, y, animate);
        }
    }

    public getCurrentScrollOffset(): number {
        const viewabilityTracker = this._virtualRenderer.getViewabilityTracker();

        return viewabilityTracker ? viewabilityTracker.getLastOffset() : 0;
    }

    public findApproxFirstVisibleIndex(): number {
        const viewabilityTracker = this._virtualRenderer.getViewabilityTracker();

        return viewabilityTracker ? viewabilityTracker.findFirstLogicallyVisibleIndex() : 0;
    }

    public render(): JSX.Element {

        return (
            <PullRefreshScrollView
                ref={(scrollComponent) => {
                    //@ts-ignore
                    this._scrollComponent = scrollComponent;
                    return this._scrollComponent;
                }}
                {...this.props}
                {...this.props.scrollViewProps}
                {...this.default_props}
                onScroll={this._onScroll}
                onSizeChanged={this._onSizeChanged}
                contentHeight={this._initComplete ? this._virtualRenderer.getLayoutDimension().height : 0}
                contentWidth={this._initComplete ? this._virtualRenderer.getLayoutDimension().width : 0}>
                {this._generateRenderStack()}
            </PullRefreshScrollView>
        );

    }

    renderBottomContent() {
        let jsx = [];
        let indicatorStyle = {
            position: 'absolute',
            left: -40,
            top: -1,
            width: 16,
            height: 16
        };

        jsx.push(<Text key={2} style={{color: '#979aa0'}}>{'加载跟多'}</Text>);

        return (jsx);
    }

    renderIndicatorContentBottom() {
        let jsx = [this.renderBottomContent()];

        return (
            <View style={styles.loadMore}>

                {jsx.map((item, index) => {
                    return <View key={index}>{item}</View>
                })}
            </View>
        );
    }


    /**
     * @author 何晏波
     * @QQ 1054539528
     * @date 2018/9/29
     * @function: 终止刷新
     */
    onRefreshEnd = () => {
        this._scrollComponent.onRefreshEnd();
    }


    /**
     * @author 何晏波
     * @QQ 1054539528
     * @date 2018/9/29
     * @function: 数据加载完成
     */
    onLoadFinish() {
        this._scrollComponent.onLoadFinish();
    }


    /**
     * @author 何晏波
     * @QQ 1054539528
     * @date 2018/9/29
     * @function: 没有数据可加载
     */
    onNoDataToLoad() {
        this._scrollComponent.onNoDataToLoad();
    }

    private _checkAndChangeLayouts(newProps: RecyclerListViewProps, forceFullRender?: boolean): void {
        this._params.isHorizontal = newProps.isHorizontal;
        this._params.itemCount = newProps.dataProvider.getSize();
        this._virtualRenderer.setParamsAndDimensions(this._params, this._layout);
        if (forceFullRender || this.props.layoutProvider !== newProps.layoutProvider || this.props.isHorizontal !== newProps.isHorizontal) {
            //TODO:Talha use old layout manager
            this._virtualRenderer.setLayoutManager(new LayoutManager(newProps.layoutProvider, this._layout, newProps.isHorizontal));
            this._virtualRenderer.refreshWithAnchor();
            this._refreshViewability();
        } else if (this.props.dataProvider !== newProps.dataProvider) {
            const layoutManager = this._virtualRenderer.getLayoutManager();

            if (layoutManager) {
                layoutManager.reLayoutFromIndex(newProps.dataProvider.getFirstIndexToProcessInternal(), newProps.dataProvider.getSize());
                this._virtualRenderer.refresh();
            }
        } else if (this._relayoutReqIndex >= 0) {
            const layoutManager = this._virtualRenderer.getLayoutManager();

            if (layoutManager) {
                layoutManager.reLayoutFromIndex(this._relayoutReqIndex, newProps.dataProvider.getSize());
                this._relayoutReqIndex = -1;
                this._refreshViewability();
            }
        }
    }

    private _refreshViewability(): void {
        this._virtualRenderer.refresh();
        this._queueStateRefresh();

    }

    private _queueStateRefresh(): void {
        refreshRequestDebouncer(() => {
            this.setState((prevState) => {
                return prevState;
            });
        });
    }

    private _onSizeChanged(layout: Dimension): void {
        const hasHeightChanged = this._layout.height !== layout.height,
            hasWidthChanged = this._layout.width !== layout.width;

        this._layout.height = layout.height;
        this._layout.width = layout.width;
        if (layout.height === 0 || layout.width === 0) {
            throw new CustomError(RecyclerListViewExceptions.layoutException);
        }
        if (!this._initComplete) {
            this._initComplete = true;
            this._initTrackers();
            this._processOnEndReached();
        } else if ((hasHeightChanged && hasWidthChanged) ||
            (hasHeightChanged && this.props.isHorizontal) ||
            (hasWidthChanged && !this.props.isHorizontal)) {
            this._checkAndChangeLayouts(this.props, true);
        } else {
            this._refreshViewability();
        }
    }

    private _renderStackWhenReady(stack: RenderStack): void {
        this.setState(() => {
            return {renderStack: stack};
        });
    }

    private _initTrackers(): void {
        this._assertDependencyPresence(this.props);
        if (this.props.onVisibleIndexesChanged) {
            this._virtualRenderer.attachVisibleItemsListener(this.props.onVisibleIndexesChanged);
        }
        this._params = {
            initialOffset: this.props.initialOffset ? this.props.initialOffset : this._initialOffset,
            initialRenderIndex: this.props.initialRenderIndex,
            isHorizontal: this.props.isHorizontal,
            itemCount: this.props.dataProvider.getSize(),
            renderAheadOffset: this.props.renderAheadOffset,
        };
        this._virtualRenderer.setParamsAndDimensions(this._params, this._layout);
        this._virtualRenderer.setLayoutManager(new LayoutManager(this.props.layoutProvider, this._layout, this.props.isHorizontal, this._cachedLayouts));
        this._virtualRenderer.setLayoutProvider(this.props.layoutProvider);
        this._virtualRenderer.init();
        const offset = this._virtualRenderer.getInitialOffset();

        if (offset.y > 0 || offset.x > 0) {
            this._pendingScrollToOffset = offset;
            this.setState({});
        } else {
            this._virtualRenderer.startViewabilityTracker();
        }
    }

    private _assertDependencyPresence(props: RecyclerListViewProps): void {
        if (!props.dataProvider || !props.layoutProvider) {
            throw new CustomError(RecyclerListViewExceptions.unresolvedDependenciesException);
        }
    }

    private _assertType(type: string | number): void {
        if (!type && type !== 0) {
            throw new CustomError(RecyclerListViewExceptions.itemTypeNullException);
        }
    }

    private _dataHasChanged(row1: any, row2: any): boolean {
        return this.props.dataProvider.rowHasChanged(row1, row2);
    }

    private _renderRowUsingMeta(itemMeta: RenderStackItem): JSX.Element | null {
        const dataSize = this.props.dataProvider.getSize(),
            dataIndex = itemMeta.dataIndex;

        if (!ObjectUtil.isNullOrUndefined(dataIndex) && dataIndex < dataSize) {
            const itemRect = (this._virtualRenderer.getLayoutManager() as LayoutManager).getLayouts()[dataIndex],
                data = this.props.dataProvider.getDataForIndex(dataIndex),
                type = this.props.layoutProvider.getLayoutTypeForIndex(dataIndex);

            this._assertType(type);
            if (!this.props.forceNonDeterministicRendering) {
                this._checkExpectedDimensionDiscrepancy(itemRect, type, dataIndex);
            }
            return (
                <ViewRenderer key={itemMeta.key} data={data}
                              dataHasChanged={this._dataHasChanged}
                              x={itemRect.x}
                              y={itemRect.y}
                              layoutType={type}
                              index={dataIndex}
                              layoutProvider={this.props.layoutProvider}
                              forceNonDeterministicRendering={this.props.forceNonDeterministicRendering}
                              isHorizontal={this.props.isHorizontal}
                              onSizeChanged={this._onViewContainerSizeChange}
                              childRenderer={this.props.rowRenderer}
                              height={itemRect.height}
                              width={itemRect.width}
                              itemAnimator={Default.value<ItemAnimator>(this.props.itemAnimator, this._defaultItemAnimator)}
                              extendedState={this.props.extendedState}/>
            );
        }
        return null;
    }

    private _onViewContainerSizeChange(dim: Dimension, index: number): void {
        //Cannot be null here
        (this._virtualRenderer.getLayoutManager() as LayoutManager).overrideLayout(index, dim);
        if (this._relayoutReqIndex === -1) {
            this._relayoutReqIndex = index;
        } else {
            this._relayoutReqIndex = Math.min(this._relayoutReqIndex, index);
        }
        this._queueStateRefresh();
    }

    private _checkExpectedDimensionDiscrepancy(itemRect: Dimension, type: string | number, index: number): void {
        //Cannot be null here
        const layoutManager = this._virtualRenderer.getLayoutManager() as LayoutManager;

        layoutManager.setMaxBounds(this._tempDim);
        this.props.layoutProvider.setLayoutForType(type, this._tempDim, index);

        //TODO:Talha calling private method, find an alternative and remove this
        layoutManager.setMaxBounds(this._tempDim);
        if (itemRect.height !== this._tempDim.height || itemRect.width !== this._tempDim.width) {
            if (this._relayoutReqIndex === -1) {
                this._relayoutReqIndex = index;
            } else {
                this._relayoutReqIndex = Math.min(this._relayoutReqIndex, index);
            }
        }
    }

    private _generateRenderStack(): Array<JSX.Element | null> {
        const renderedItems = [];

        for (const key in this.state.renderStack) {
            if (this.state.renderStack.hasOwnProperty(key)) {
                renderedItems.push(this._renderRowUsingMeta(this.state.renderStack[key]));

            }
        }
        return renderedItems;
    }

    private _onScroll(offsetX: number, offsetY: number, rawEvent: ScrollEvent): void {
        this._virtualRenderer.updateOffset(offsetX, offsetY);
        if (this.props.onScroll) {
            this.props.onScroll(rawEvent, offsetX, offsetY);
        }
        this._processOnEndReached();
    }

    private _processOnEndReached(): void {
        if (this.props.onEndReached && this._virtualRenderer) {
            const layout = this._virtualRenderer.getLayoutDimension(),
                windowBound = this.props.isHorizontal ? layout.width - this._layout.width : layout.height - this._layout.height,
                viewabilityTracker = this._virtualRenderer.getViewabilityTracker(),
                lastOffset = viewabilityTracker ? viewabilityTracker.getLastOffset() : 0;

            if (windowBound - lastOffset <= Default.value<number>(this.props.onEndReachedThreshold, 0)) {
                if (!this._onEndReachedCalled) {
                    this._onEndReachedCalled = true;
                    this.props.onEndReached();
                }
            } else {
                this._onEndReachedCalled = false;
            }
        }
    }
}

RecyclerListView.propTypes = {

    //Refer the sample
    layoutProvider: PropTypes.instanceOf(LayoutProvider).isRequired,

    //Refer the sample
    dataProvider: PropTypes.instanceOf(DataProvider).isRequired,

    //Used to maintain scroll position in case view gets destroyed e.g, cases of back navigation
    contextProvider: PropTypes.instanceOf(ContextProvider),

    //Methods which returns react component to be rendered. You get type of view and data in the callback.
    rowRenderer: PropTypes.func.isRequired,

    //Initial offset you want to start rendering from, very useful if you want to maintain scroll context across pages.
    initialOffset: PropTypes.number,

    //Specify how many pixels in advance do you want views to be rendered. Increasing this value can help reduce blanks (if any). However keeping this as low
    //as possible should be the intent. Higher values also increase re-render compute
    renderAheadOffset: PropTypes.number,

    //Whether the listview is horizontally scrollable. Both use staggeredGrid implementation
    isHorizontal: PropTypes.bool,

    //On scroll callback onScroll(rawEvent, offsetX, offsetY), note you get offsets no need to read scrollTop/scrollLeft
    onScroll: PropTypes.func,

    //Provide your own ScrollView Component. The contract for the scroll event should match the native scroll event contract, i.e.
    // scrollEvent = { nativeEvent: { contentOffset: { x: offset, y: offset } } }
    //Note: Please extend BaseScrollView to achieve expected behaviour
    externalScrollView: PropTypes.func,

    //Callback given when user scrolls to the end of the list or footer just becomes visible, useful in incremental loading scenarios
    onEndReached: PropTypes.func,

    //Specify how many pixels in advance you onEndReached callback
    onEndReachedThreshold: PropTypes.number,

    //Provides visible index, helpful in sending impression events etc, onVisibleIndexesChanged(all, now, notNow)
    onVisibleIndexesChanged: PropTypes.func,

    //Provide this method if you want to render a footer. Helpful in showing a loader while doing incremental loads.
    renderFooter: PropTypes.func,

    //Specify the initial item index you want rendering to start from. Preferred over initialOffset if both are specified.
    initialRenderIndex: PropTypes.number,

    //iOS only. Scroll throttle duration.
    scrollThrottle: PropTypes.number,

    //Specify if size can change, listview will automatically relayout items. For web, works only with useWindowScroll = true
    canChangeSize: PropTypes.bool,

    //Web only. Specify how far away the first list item is from window top. This is an adjustment for better optimization.
    distanceFromWindow: PropTypes.number,

    //Web only. Layout elements in window instead of a scrollable div.
    useWindowScroll: PropTypes.bool,

    //Turns off recycling. You still get progressive rendering and all other features. Good for lazy rendering. This should not be used in most cases.
    disableRecycling: PropTypes.bool,

    //Default is false, if enabled dimensions provided in layout provider will not be strictly enforced.
    //Rendered dimensions will be used to relayout items. Slower if enabled.
    forceNonDeterministicRendering: PropTypes.bool,

    //In some cases the data passed at row level may not contain all the info that the item depends upon, you can keep all other info
    //outside and pass it down via this prop. Changing this object will cause everything to re-render. Make sure you don't change
    //it often to ensure performance. Re-renders are heavy.
    extendedState: PropTypes.object,

    //Enables animating FindRecyclerListView item cells e.g, shift, add, remove etc. This prop can be used to pass an external item animation implementation.
    //Look into BaseItemAnimator/DefaultJSItemAnimator/DefaultNativeItemAnimator/DefaultWebItemAnimator for more info.
    //By default there are few animations, to disable completely simply pass blank new BaseItemAnimator() object. Remember, create
    //one object and keep it do not create multiple object of type BaseItemAnimator.
    //Note: You might want to look into DefaultNativeItemAnimator to check an implementation based on LayoutAnimation. By default,
    //animations are JS driven to avoid workflow interference. Also, please note LayoutAnimation is buggy on Android.
    itemAnimator: PropTypes.instanceOf(BaseItemAnimator),

    //For TS use case, not necessary with JS use.
    //For all props that need to be proxied to inner/external scrollview. Put them in an object and they'll be spread
    //and passed down.
    scrollViewProps: PropTypes.object,
};

const styles = StyleSheet.create({
    pullRefresh: {
        position: 'absolute',
        top: -69,
        left: 0,
        backfaceVisibility: 'hidden',
        right: 0,
        height: 70,
        backgroundColor: '#fafafa',
        alignItems: 'center',
        justifyContent: 'flex-end'
    },
    loadMore: {
        height: 35,
        backgroundColor: '#fafafa',
        alignItems: 'center',
        justifyContent: 'center'
    },
    text: {
        height: 70,
        backgroundColor: '#fafafa',
        color: '#979aa0'
    },
    prText: {
        marginBottom: 4,
        color: '#979aa0',
        fontSize: 12,
    },

    prState: {
        marginBottom: 4,
        fontSize: 12,
        color: '#979aa0',
    },
    lmState: {
        fontSize: 12,
    },
    indicatorContent: {
        flexDirection: 'row',
        marginBottom: 5
    },

});
