<commit_before>package com.mogproject.mogami.playground.controller

import com.mogproject.mogami.util.Implicits._
import com.mogproject.mogami.core.state.StateCache.Implicits._
import com.mogproject.mogami.{BranchNo, _}
import com.mogproject.mogami.playground.api.google.URLShortener
import com.mogproject.mogami.playground.controller.mode._
import com.mogproject.mogami.playground.io.RecordFormat
import com.mogproject.mogami.playground.view.parts.settings.BoardSizeButton.{Automatic, PresetBoardSize}
import com.mogproject.mogami.playground.view.renderer.BoardRenderer.{DoubleBoard, FlipDisabled}
import com.mogproject.mogami.playground.view.renderer.Renderer
import org.scalajs.dom.Element

import scala.util.{Failure, Success, Try}

/**
  * logic controller
  */
object Controller {

  private[this] val urlShortener = URLShortener()

  // mutable reference
  private[this] var modeController: Option[ModeController] = None

  /**
    * Initialize the game and renderer
    *
    * @param elem parent HTML element
    * @param args Arguments instance
    */
  def initialize(elem: Element, args: Arguments): Unit = {
    val config = args.config

    // load game
    val game = createGameFromArgs(args)

    // create renderer
    val renderer = new Renderer(config.isMobile)
    renderer.initialize(elem, config)

    // update mode
    val isSnapshot = game.trunk.moves.isEmpty && game.trunk.finalAction.isEmpty && game.branches.isEmpty

    modeController = Some(isSnapshot.fold(
      PlayModeController(renderer, config, game, 0, 0),
      ViewModeController(renderer, config, game, args.gamePosition.branch, math.max(0, args.gamePosition.position - game.trunk.offset))
    ))

    // render all parts
    modeController.get.initialize()

    // create image if the action is ImageAction
    args.action match {
      case ImageAction => renderer.drawAsImage()
      case NotesAction => renderer.drawNotes(game, config.recordLang)
      case _ =>
    }
  }

  private[this] def createGameFromArgs(args: Arguments): Game = {
    def loadGame(game: => Game): Game = Try(game) match {
      case Success(g) => g
      case Failure(e) =>
        println(s"Failed to create a game: ${e}")
        Game()
    }

    val gg: Game = ((args.usen, args.sfen) match {
      case (Some(u), _) => loadGame(Game.parseUsenString(u)) // parse USEN string
      case (_, Some(s)) => loadGame(Game.parseSfenString(s)) // parse SFEN string
      case _ => Game()
    }).copy(newGameInfo = args.gameInfo)

    // update comments
    val comments = for {
      (b, m) <- args.comments
      (pos, c) <- m
      h <- gg.getHistoryHash(GamePosition(b, pos))
    } yield h -> c
    gg.copy(newComments = comments)
  }

  /**
    * Update mode controller and execute a call back method
    */
  private[this] def doAction(f: => ModeController => Option[ModeController], callback: ModeController => Unit): Unit = {
    val old = modeController
    modeController.flatMap(f) foreach { mc =>
      if (old.exists(_.mode != mc.mode)) {
        // mode change
        old.foreach(_.terminate())
        mc.initialize()
      } else {
        callback(mc)
      }
      modeController = Some(mc)
    }
  }

  /**
    * Update mode controller and render all components
    *
    * @param mc Option of the mode controller
    */
  def update(mc: Option[ModeController], callback: ModeController => Unit = _.renderAll()): Unit = {
    mc.foreach { m =>
      if (modeController.exists(_.mode != m.mode)) {
        // mode change
        modeController.foreach(_.terminate())
        m.initialize()
      } else {
        callback(m)
      }
      modeController = Some(m)
    }
  }

  // events
  def canActivate(cursor: Cursor): Boolean = modeController.get.canActivate(cursor)

  def canSelect(selected: Cursor): Boolean = modeController.get.canSelect(selected)

  def canInvokeWithoutSelection(cursor: Cursor): Boolean = modeController.get.canInvokeWithoutSelection(cursor)

  def invokeCursor(selected: Cursor, invoked: Cursor, isFlipped: Boolean): Unit = doAction(_.invokeCursor(selected, invoked, isFlipped), _.renderAll())

  def invokeHoldEvent(invoked: Cursor, isFlipped: Boolean): Unit = doAction(_.invokeHoldEvent(invoked, isFlipped), _.renderAll())

  def processMouseUp(selected: Cursor, released: Cursor): Option[Cursor] = modeController.get.processMouseUp(selected, released)

  // actions
  def setMode(mode: Mode): Unit = if (!modeController.exists(_.mode == mode)) doAction(_.setMode(mode), _ => {})

  def setRecord(index: Int): Unit = doAction(_.setRecord(index), _.renderAll())

  def setControl(controlType: Int): Unit = update(modeController.flatMap(_.setControl(controlType)))

  def setEditTurn(player: Player): Unit = doAction(_.setEditTurn(player), _.renderAll())

  def setEditInitialState(initialState: State, isHandicap: Boolean): Unit =
    doAction(_.setEditInitialState(initialState, isHandicap), _.renderAll())

  def setGameInfo(gameInfo: GameInfo): Unit = doAction(_.setGameInfo(gameInfo), _.renderAll())

  def toggleFlip(): Unit = doAction(_.toggleFlip(), _.refreshBoard())

  def showMenu(): Unit = modeController.get.renderer.showMenuModal()

  def shortenSnapshotUrl(): Unit = modeController match {
    case Some(gc: GameController) => gc.shortenSnapshotUrl(urlShortener)
    case _ =>
  }
<commit_msg>added focusing on the long selector after cursor invocation<commit_after>package com.mogproject.mogami.playground.controller

import com.mogproject.mogami.util.Implicits._
import com.mogproject.mogami.core.state.StateCache.Implicits._
import com.mogproject.mogami.{BranchNo, _}
import com.mogproject.mogami.playground.api.google.URLShortener
import com.mogproject.mogami.playground.controller.mode._
import com.mogproject.mogami.playground.io.RecordFormat
import com.mogproject.mogami.playground.view.parts.settings.BoardSizeButton.{Automatic, PresetBoardSize}
import com.mogproject.mogami.playground.view.renderer.BoardRenderer.{DoubleBoard, FlipDisabled}
import com.mogproject.mogami.playground.view.renderer.Renderer
import org.scalajs.dom.Element

import scala.util.{Failure, Success, Try}

/**
  * logic controller
  */
object Controller {

  private[this] val urlShortener = URLShortener()

  // mutable reference
  private[this] var modeController: Option[ModeController] = None

  /**
    * Initialize the game and renderer
    *
    * @param elem parent HTML element
    * @param args Arguments instance
    */
  def initialize(elem: Element, args: Arguments): Unit = {
    val config = args.config

    // load game
    val game = createGameFromArgs(args)

    // create renderer
    val renderer = new Renderer(config.isMobile)
    renderer.initialize(elem, config)

    // update mode
    val isSnapshot = game.trunk.moves.isEmpty && game.trunk.finalAction.isEmpty && game.branches.isEmpty

    modeController = Some(isSnapshot.fold(
      PlayModeController(renderer, config, game, 0, 0),
      ViewModeController(renderer, config, game, args.gamePosition.branch, math.max(0, args.gamePosition.position - game.trunk.offset))
    ))

    // render all parts
    modeController.get.initialize()

    // create image if the action is ImageAction
    args.action match {
      case ImageAction => renderer.drawAsImage()
      case NotesAction => renderer.drawNotes(game, config.recordLang)
      case _ =>
    }
  }

  private[this] def createGameFromArgs(args: Arguments): Game = {
    def loadGame(game: => Game): Game = Try(game) match {
      case Success(g) => g
      case Failure(e) =>
        println(s"Failed to create a game: ${e}")
        Game()
    }

    val gg: Game = ((args.usen, args.sfen) match {
      case (Some(u), _) => loadGame(Game.parseUsenString(u)) // parse USEN string
      case (_, Some(s)) => loadGame(Game.parseSfenString(s)) // parse SFEN string
      case _ => Game()
    }).copy(newGameInfo = args.gameInfo)

    // update comments
    val comments = for {
      (b, m) <- args.comments
      (pos, c) <- m
      h <- gg.getHistoryHash(GamePosition(b, pos))
    } yield h -> c
    gg.copy(newComments = comments)
  }

  /**
    * Update mode controller and execute a call back method
    */
  private[this] def doAction(f: => ModeController => Option[ModeController], callback: ModeController => Unit): Unit = {
    val old = modeController
    modeController.flatMap(f) foreach { mc =>
      if (old.exists(_.mode != mc.mode)) {
        // mode change
        old.foreach(_.terminate())
        mc.initialize()
      } else {
        callback(mc)
      }
      modeController = Some(mc)
    }
  }

  /**
    * Update mode controller and render all components
    *
    * @param mc Option of the mode controller
    */
  def update(mc: Option[ModeController], callback: ModeController => Unit = _.renderAll()): Unit = {
    mc.foreach { m =>
      if (modeController.exists(_.mode != m.mode)) {
        // mode change
        modeController.foreach(_.terminate())
        m.initialize()
      } else {
        callback(m)
      }
      modeController = Some(m)
    }
  }

  // events
  def canActivate(cursor: Cursor): Boolean = modeController.get.canActivate(cursor)

  def canSelect(selected: Cursor): Boolean = modeController.get.canSelect(selected)

  def canInvokeWithoutSelection(cursor: Cursor): Boolean = modeController.get.canInvokeWithoutSelection(cursor)

  def invokeCursor(selected: Cursor, invoked: Cursor, isFlipped: Boolean): Unit = doAction(
    _.invokeCursor(selected, invoked, isFlipped), { mc => mc.renderAll(); mc.renderer.focusLongSelector() }
  )

  def invokeHoldEvent(invoked: Cursor, isFlipped: Boolean): Unit = doAction(_.invokeHoldEvent(invoked, isFlipped), _.renderAll())

  def processMouseUp(selected: Cursor, released: Cursor): Option[Cursor] = modeController.get.processMouseUp(selected, released)

  // actions
  def setMode(mode: Mode): Unit = if (!modeController.exists(_.mode == mode)) doAction(_.setMode(mode), _ => {})

  def setRecord(index: Int): Unit = doAction(_.setRecord(index), _.renderAll())

  def setControl(controlType: Int): Unit = update(modeController.flatMap(_.setControl(controlType)))

  def setEditTurn(player: Player): Unit = doAction(_.setEditTurn(player), _.renderAll())

  def setEditInitialState(initialState: State, isHandicap: Boolean): Unit =
    doAction(_.setEditInitialState(initialState, isHandicap), _.renderAll())

  def setGameInfo(gameInfo: GameInfo): Unit = doAction(_.setGameInfo(gameInfo), _.renderAll())

  def toggleFlip(): Unit = doAction(_.toggleFlip(), _.refreshBoard())

  def showMenu(): Unit = modeController.get.renderer.showMenuModal()

  def shortenSnapshotUrl(): Unit = modeController match {
    case Some(gc: GameController) => gc.shortenSnapshotUrl(urlShortener)
    case _ =>
  }

{'fraction_non_alphanumeric': 0.12357981002048798, 'fraction_numerical': 0.0005587632706276774, 'mean_word_length': 3.6328731665228644, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 42, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1696833', 'n_tokens_mistral': 3307, 'n_tokens_neox': 3202, 'n_words': 731}