/*
 * (c) 2023 by Panayotis Katsaloulis
 *
 * SPDX-License-Identifier: LGPL-3.0-only
 */

package crossmobile.ios.uikit;

import crossmobile.ios.coregraphics.CGPoint;
import crossmobile.ios.foundation.NSObject;
import org.crossmobile.bind.wrapper.NativeTouch;
import org.crossmobile.bridge.Native;
import org.crossmobile.bridge.ann.CMClass;
import org.crossmobile.bridge.ann.CMGetter;
import org.crossmobile.bridge.ann.CMSelector;

import java.util.*;

/**
 * UIEvent class defines an object known as a event that represents a change
 * triggered by the user, such as touching the screen or moving the device, or
 * generated by the system.
 */
@CMClass
public class UIEvent extends NSObject {
    private static final UITouch[] NO_TOUCH = new UITouch[]{};

    private final UITouch[] touches;
    private final double timestamp;
    final Object originalEvent;
    private final int type;
    private final int subtype;
    final int phase;

    UIEvent() {
        this(NO_TOUCH, null, 0);
    }

    UIEvent(UITouch[] touches, Object originalEvent, int phase) {
        this.originalEvent = originalEvent;
        this.phase = phase;
        this.touches = touches;
        this.timestamp = System.currentTimeMillis() / 1000d;
        this.type = UIEventType.Touches;
        this.subtype = UIEventSubtype.None;
    }

    /**
     * Returns all the touch objects related to this object.
     *
     * @return The touch objects of this object.
     */
    @CMSelector("- (NSSet<UITouch *> *)allTouches;")
    public Set<UITouch> allTouches() {
        return new HashSet<>(Arrays.asList(touches));
    }

    Map<UIView, Set<UITouch>> byViews() {
        Map<UIView, Set<UITouch>> sets = new HashMap<>();
        for (int i = 0; i < touches.length; i++) {
            UITouch t = touches[i];
            Set<UITouch> set = sets.get(t.view);
            if (set == null) {
                set = new LinkedHashSet<>();
                sets.put(t.view, set);
            }
            set.add(t);
        }
        return sets;
    }

    /**
     * Returns the timestamp of the event.
     *
     * @return The timestamp of the event.
     */
    @CMGetter("@property(nonatomic, readonly) NSTimeInterval timestamp;")
    public double timestamp() {
        return timestamp;
    }

    /**
     * Returns the type of the event.
     *
     * @return The type of the event.
     */
    @CMGetter("@property(nonatomic, readonly) UIEventType type;")
    public int type() {
        return type;
    }

    /**
     * Returns the subtype of the event.
     *
     * @return The subtype of the event.
     */
    @CMGetter("@property(nonatomic, readonly) UIEventSubtype subtype;")
    public int subtype() {
        return subtype;
    }

    @Override
    public String toString() {
        StringBuilder out = new StringBuilder();
        out.append("UIEvent");
        for (UITouch active : touches) {
            out.append(" [").append(active.toString()).append("]");
        }
        return out.toString();
    }

    CGPoint[] hardwareLocations(UIView view) {
        UITouch[] _touches = this.touches;  // better be thread safe
        CGPoint[] hardware = new CGPoint[_touches.length];
        CGPoint location;
        for (int i = 0; i < _touches.length; i++) {
            location = _touches[i].locationInView(view);
            hardware[i] = Native.graphics().metrics().getVirtualToHardware(location.getX(), location.getY());
        }
        return hardware;
    }

    NativeTouch[] getTouches(UIView referenceView) {
        NativeTouch[] ntouch = new NativeTouch[touches.length];
        for (int i = 0; i < touches.length; i++) {
            CGPoint vpoint = touches[i].locationInView(referenceView); // what if reference view is not the same with UITouch.view?
            ntouch[i] = new NativeTouch(Native.graphics().metrics().getVirtualToHardware(vpoint.getX(), vpoint.getY()), touches[i].pointerID);
        }
        return ntouch;
    }

}
