/**
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @flow
 */

import type {
  Interaction,
  MouseDownInteraction,
  MouseMoveInteraction,
  MouseUpInteraction,
} from '../useCanvasInteraction';
import type {Rect, Size} from '../geometry';
import type {ViewRefs} from '../Surface';

import {BORDER_SIZE, COLORS} from '../../content-views/constants';
import {drawText} from '../../content-views/utils/text';
import {Surface} from '../Surface';
import {View} from '../View';
import {rectContainsPoint} from '../geometry';
import {noopLayout} from '../layouter';

type ResizeBarState = 'normal' | 'hovered' | 'dragging';

const RESIZE_BAR_DOT_RADIUS = 1;
const RESIZE_BAR_DOT_SPACING = 4;
const RESIZE_BAR_HEIGHT = 8;
const RESIZE_BAR_WITH_LABEL_HEIGHT = 16;

export class ResizeBarView extends View {
  _interactionState: ResizeBarState = 'normal';
  _label: string;

  showLabel: boolean = false;

  constructor(surface: Surface, frame: Rect, label: string) {
    super(surface, frame, noopLayout);

    this._label = label;
  }

  desiredSize(): Size {
    return this.showLabel
      ? {height: RESIZE_BAR_WITH_LABEL_HEIGHT, width: 0}
      : {height: RESIZE_BAR_HEIGHT, width: 0};
  }

  draw(context: CanvasRenderingContext2D, viewRefs: ViewRefs) {
    const {frame} = this;
    const {x, y} = frame.origin;
    const {width, height} = frame.size;

    const isActive =
      this._interactionState === 'dragging' ||
      (this._interactionState === 'hovered' && viewRefs.activeView === null);

    context.fillStyle = isActive
      ? COLORS.REACT_RESIZE_BAR_ACTIVE
      : COLORS.REACT_RESIZE_BAR;
    context.fillRect(x, y, width, height);

    context.fillStyle = COLORS.REACT_RESIZE_BAR_BORDER;
    context.fillRect(x, y, width, BORDER_SIZE);
    context.fillRect(x, y + height - BORDER_SIZE, width, BORDER_SIZE);

    const horizontalCenter = x + width / 2;
    const verticalCenter = y + height / 2;

    if (this.showLabel) {
      // When the resize view is collapsed entirely,
      // rather than showing a resize bar– this view displays a label.
      const labelRect: Rect = {
        origin: {
          x: 0,
          y: y + height - RESIZE_BAR_WITH_LABEL_HEIGHT,
        },
        size: {
          width: frame.size.width,
          height: RESIZE_BAR_WITH_LABEL_HEIGHT,
        },
      };

      drawText(this._label, context, labelRect, frame, {
        fillStyle: COLORS.REACT_RESIZE_BAR_DOT,
        textAlign: 'center',
      });
    } else {
      // Otherwise draw horizontally centered resize bar dots
      context.beginPath();
      context.fillStyle = COLORS.REACT_RESIZE_BAR_DOT;
      context.arc(
        horizontalCenter,
        verticalCenter,
        RESIZE_BAR_DOT_RADIUS,
        0,
        2 * Math.PI,
      );
      context.arc(
        horizontalCenter + RESIZE_BAR_DOT_SPACING,
        verticalCenter,
        RESIZE_BAR_DOT_RADIUS,
        0,
        2 * Math.PI,
      );
      context.arc(
        horizontalCenter - RESIZE_BAR_DOT_SPACING,
        verticalCenter,
        RESIZE_BAR_DOT_RADIUS,
        0,
        2 * Math.PI,
      );
      context.fill();
    }
  }

  _setInteractionState(state: ResizeBarState) {
    if (this._interactionState === state) {
      return;
    }
    this._interactionState = state;
    this.setNeedsDisplay();
  }

  _handleMouseDown(interaction: MouseDownInteraction, viewRefs: ViewRefs) {
    const cursorInView = rectContainsPoint(
      interaction.payload.location,
      this.frame,
    );
    if (cursorInView) {
      this._setInteractionState('dragging');
      viewRefs.activeView = this;
    }
  }

  _handleMouseMove(interaction: MouseMoveInteraction, viewRefs: ViewRefs) {
    const cursorInView = rectContainsPoint(
      interaction.payload.location,
      this.frame,
    );

    if (viewRefs.activeView === this) {
      // If we're actively dragging this resize bar,
      // show the cursor even if the pointer isn't hovering over this view.
      this.currentCursor = 'ns-resize';
    } else if (cursorInView) {
      if (this.showLabel) {
        this.currentCursor = 'pointer';
      } else {
        this.currentCursor = 'ns-resize';
      }
    }

    if (cursorInView) {
      viewRefs.hoveredView = this;
    }

    if (this._interactionState === 'dragging') {
      return;
    }
    this._setInteractionState(cursorInView ? 'hovered' : 'normal');
  }

  _handleMouseUp(interaction: MouseUpInteraction, viewRefs: ViewRefs) {
    const cursorInView = rectContainsPoint(
      interaction.payload.location,
      this.frame,
    );
    if (this._interactionState === 'dragging') {
      this._setInteractionState(cursorInView ? 'hovered' : 'normal');
    }

    if (viewRefs.activeView === this) {
      viewRefs.activeView = null;
    }
  }

  handleInteraction(interaction: Interaction, viewRefs: ViewRefs) {
    switch (interaction.type) {
      case 'mousedown':
        this._handleMouseDown(interaction, viewRefs);
        break;
      case 'mousemove':
        this._handleMouseMove(interaction, viewRefs);
        break;
      case 'mouseup':
        this._handleMouseUp(interaction, viewRefs);
        break;
    }
  }
}
