"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const react_1 = require("react");
const react_native_1 = require("react-native");
const react_native_gesture_handler_1 = require("react-native-gesture-handler");
const react_native_reanimated_1 = require("react-native-reanimated");
const react_native_redash_1 = require("react-native-redash");
const defaultProps = {
    containerColor: 'black',
    imageBackdropColor: 'black',
};
const { Value, event, block, set, cond, eq, and, greaterThan, greaterOrEq, lessThan, add, sub, multiply, divide, call, } = react_native_reanimated_1.default;
const styles = react_native_1.StyleSheet.create({
    panGestureInner: Object.assign(Object.assign({}, react_native_1.StyleSheet.absoluteFillObject), { alignItems: 'center', justifyContent: 'center' }),
    imageWrapper: {
        overflow: 'hidden',
        alignItems: 'center',
        justifyContent: 'center',
    },
    image: {},
});
class ImageViewer extends react_1.Component {
    constructor(props) {
        super(props);
        this.handleMove = (args) => {
            const { onMove } = this.props;
            const positionX = args[0];
            const positionY = args[1];
            const scale = args[2];
            onMove({ positionX, positionY, scale });
        };
        const { areaWidth, areaHeight, imageWidth, imageHeight, minScale } = props;
        this.pinchRef = react_1.default.createRef();
        this.dragRef = react_1.default.createRef();
        this.translateX = new Value(0);
        this.translateY = new Value(0);
        this.scale = new Value(minScale);
        this.isAlignEnabled = new Value(1);
        const timingDefaultParams = {
            duration: 200,
            easing: react_native_reanimated_1.Easing.linear,
        };
        const maxScale = minScale + 3;
        const offsetX = new Value(0);
        const offsetY = new Value(0);
        const offsetZ = new Value(minScale);
        const viewerAreaWidth = new Value(areaWidth);
        const viewerAreaHeight = new Value(areaHeight);
        const viewerImageWidth = new Value(imageWidth);
        const viewerImageHeight = new Value(imageHeight);
        const maxX = new Value(0);
        const negMaxX = new Value(0);
        const maxY = new Value(0);
        const negMaxY = new Value(0);
        const horizontalMax = divide(divide(sub(multiply(viewerImageWidth, this.scale), viewerAreaWidth), 2), this.scale);
        const verticalMax = divide(divide(sub(multiply(viewerImageHeight, this.scale), viewerAreaHeight), 2), this.scale);
        const startHorizontalMax = (imageWidth * minScale - areaWidth) / 2 / minScale;
        const startVerticalMax = (imageHeight * minScale - areaHeight) / 2 / minScale;
        const startNegHorizontalMax = startHorizontalMax * -1;
        const startNegVerticalMax = startVerticalMax * -1;
        const scaledWidth = multiply(viewerImageWidth, this.scale);
        const scaledHeight = multiply(viewerImageHeight, this.scale);
        this.onTapGestureEvent = event([
            {
                nativeEvent: ({ state }) => block([
                    cond(eq(state, react_native_gesture_handler_1.State.END), [
                        set(offsetZ, new Value(minScale)),
                        set(offsetX, new Value(0)),
                        set(offsetY, new Value(0)),
                        set(this.scale, react_native_redash_1.timing(Object.assign({ from: this.scale, to: minScale }, timingDefaultParams))),
                        set(this.translateX, react_native_redash_1.timing(Object.assign({ from: this.translateX, to: 0 }, timingDefaultParams))),
                        set(this.translateY, react_native_redash_1.timing(Object.assign({ from: this.translateY, to: 0 }, timingDefaultParams))),
                    ]),
                ]),
            },
        ]);
        this.onPanGestureEvent = event([
            {
                nativeEvent: ({ translationX, translationY, state, }) => block([
                    cond(eq(state, react_native_gesture_handler_1.State.ACTIVE), [
                        set(this.translateX, add(divide(translationX, this.scale), offsetX)),
                        set(this.translateY, add(divide(translationY, this.scale), offsetY)),
                        set(maxX, horizontalMax),
                        set(negMaxX, multiply(horizontalMax, new Value(-1))),
                        set(maxY, verticalMax),
                        set(negMaxY, multiply(verticalMax, new Value(-1))),
                    ]),
                    cond(eq(this.isAlignEnabled, 0), [
                        cond(greaterThan(this.translateX, new Value(startHorizontalMax)), set(this.translateX, react_native_redash_1.timing(Object.assign({ from: this.translateX, to: startHorizontalMax }, timingDefaultParams)))),
                        cond(lessThan(this.translateX, new Value(startNegHorizontalMax)), set(this.translateX, react_native_redash_1.timing(Object.assign({ from: this.translateX, to: startNegHorizontalMax }, timingDefaultParams)))),
                        cond(greaterThan(this.translateY, new Value(startVerticalMax)), set(this.translateY, react_native_redash_1.timing(Object.assign({ from: this.translateY, to: startVerticalMax }, timingDefaultParams)))),
                        cond(lessThan(this.translateY, new Value(startNegVerticalMax)), set(this.translateY, react_native_redash_1.timing(Object.assign({ from: this.translateY, to: startNegVerticalMax }, timingDefaultParams)))),
                        react_native_redash_1.delay(set(offsetX, this.translateX), 200),
                        react_native_redash_1.delay(set(offsetY, this.translateY), 200),
                        react_native_redash_1.delay(set(this.isAlignEnabled, 1), 200),
                    ]),
                    cond(and(eq(state, react_native_gesture_handler_1.State.END), greaterOrEq(scaledWidth, viewerAreaWidth), greaterOrEq(this.scale, new Value(minScale)), eq(this.isAlignEnabled, 1)), cond(and(lessThan(this.translateX, negMaxX), greaterOrEq(this.scale, new Value(minScale))), [
                        set(this.translateX, react_native_redash_1.timing(Object.assign({ from: this.translateX, to: negMaxX }, timingDefaultParams))),
                    ], cond(and(greaterThan(this.translateX, maxX), greaterOrEq(this.scale, new Value(minScale))), [
                        set(this.translateX, react_native_redash_1.timing(Object.assign({ from: this.translateX, to: maxX }, timingDefaultParams))),
                    ]))),
                    cond(and(eq(state, react_native_gesture_handler_1.State.END), greaterOrEq(scaledHeight, viewerAreaHeight), greaterOrEq(this.scale, new Value(minScale)), eq(this.isAlignEnabled, 1)), cond(and(lessThan(this.translateY, negMaxY), greaterOrEq(this.scale, new Value(minScale))), [
                        set(negMaxY, multiply(verticalMax, new Value(-1))),
                        set(this.translateY, react_native_redash_1.timing(Object.assign({ from: this.translateY, to: negMaxY }, timingDefaultParams))),
                    ], cond(and(greaterThan(this.translateY, maxY), greaterOrEq(this.scale, new Value(minScale))), [
                        set(maxY, verticalMax),
                        set(this.translateY, react_native_redash_1.timing(Object.assign({ from: this.translateY, to: maxY }, timingDefaultParams))),
                    ]))),
                    cond(and(eq(state, react_native_gesture_handler_1.State.END), greaterOrEq(this.scale, new Value(minScale))), [set(offsetX, this.translateX), set(offsetY, this.translateY)]),
                ]),
            },
        ]);
        this.onPinchGestureEvent = event([
            {
                nativeEvent: ({ scale, state }) => block([
                    cond(eq(state, react_native_gesture_handler_1.State.ACTIVE), set(this.scale, multiply(offsetZ, scale))),
                    cond(eq(state, react_native_gesture_handler_1.State.END), [
                        set(offsetZ, this.scale),
                        cond(lessThan(this.scale, new Value(minScale)), cond(eq(viewerImageWidth, viewerImageHeight), [
                            set(maxX, new Value(0)),
                            set(negMaxX, new Value(0)),
                            set(maxY, new Value(0)),
                            set(negMaxY, new Value(0)),
                        ]), [
                            set(maxX, horizontalMax),
                            set(negMaxX, multiply(horizontalMax, new Value(-1))),
                            set(maxY, verticalMax),
                            set(negMaxY, multiply(verticalMax, new Value(-1))),
                        ]),
                    ]),
                    cond(and(eq(state, react_native_gesture_handler_1.State.END), greaterThan(this.scale, new Value(maxScale))), [
                        set(offsetZ, new Value(maxScale)),
                        set(this.scale, react_native_redash_1.timing(Object.assign({ from: this.scale, to: maxScale }, timingDefaultParams))),
                    ]),
                    cond(and(eq(state, react_native_gesture_handler_1.State.END), lessThan(this.scale, new Value(minScale))), [
                        set(offsetZ, new Value(minScale)),
                        set(this.scale, react_native_redash_1.timing(Object.assign({ from: this.scale, to: minScale }, timingDefaultParams))),
                        set(this.isAlignEnabled, 0),
                    ]),
                ]),
            },
        ]);
    }
    render() {
        const { image, imageWidth, imageHeight, areaWidth, areaHeight, containerColor, imageBackdropColor, } = this.props;
        const imageSrc = {
            uri: image,
        };
        const containerStyles = [
            styles.panGestureInner,
            {
                backgroundColor: containerColor,
            },
        ];
        const areaStyles = {
            width: areaWidth,
            height: areaHeight,
            backgroundColor: imageBackdropColor,
        };
        const imageWrapperStyles = [styles.imageWrapper, areaStyles];
        const imageStyles = [
            styles.image,
            {
                width: imageWidth,
                height: imageHeight,
                transform: [
                    {
                        scale: this.scale,
                    },
                    {
                        translateX: this.translateX,
                    },
                    {
                        translateY: this.translateY,
                    },
                ],
            },
        ];
        return (<>
        <react_native_reanimated_1.default.Code>
          {() => block([
            call([this.translateX, this.translateY, this.scale], this.handleMove),
        ])}
        </react_native_reanimated_1.default.Code>
        <react_native_gesture_handler_1.PanGestureHandler ref={this.dragRef} simultaneousHandlers={this.pinchRef} minPointers={1} maxPointers={2} avgTouches onGestureEvent={this.onPanGestureEvent} onHandlerStateChange={this.onPanGestureEvent}>
          <react_native_reanimated_1.default.View style={containerStyles}>
            <react_native_gesture_handler_1.TapGestureHandler numberOfTaps={2} onHandlerStateChange={this.onTapGestureEvent}>
              <react_native_reanimated_1.default.View style={areaStyles}>
                <react_native_gesture_handler_1.PinchGestureHandler ref={this.pinchRef} onGestureEvent={this.onPinchGestureEvent} onHandlerStateChange={this.onPinchGestureEvent}>
                  <react_native_reanimated_1.default.View style={imageWrapperStyles} collapsable={false}>
                    <react_native_reanimated_1.default.Image style={imageStyles} source={imageSrc}/>
                  </react_native_reanimated_1.default.View>
                </react_native_gesture_handler_1.PinchGestureHandler>
              </react_native_reanimated_1.default.View>
            </react_native_gesture_handler_1.TapGestureHandler>
          </react_native_reanimated_1.default.View>
        </react_native_gesture_handler_1.PanGestureHandler>
      </>);
    }
}
ImageViewer.defaultProps = defaultProps;
exports.default = ImageViewer;
