// Copyright 2025 International Digital Economy Academy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

///|
/// Ported from `cosmic-text/src/cursor.rs` (cosmic-text is dual-licensed MIT OR Apache-2.0).

///|
/// Whether to associate cursors placed at a boundary between runs with the run before or after it.
pub(all) enum Affinity {
  Before
  After
}

///|
pub impl Eq for Affinity with fn equal(self, other) {
  match (self, other) {
    (Before, Before) => true
    (After, After) => true
    _ => false
  }
}

///|
pub fn Affinity::before(self : Affinity) -> Bool {
  match self {
    Before => true
    After => false
  }
}

///|
pub fn Affinity::after(self : Affinity) -> Bool {
  match self {
    After => true
    Before => false
  }
}

///|
pub fn Affinity::from_before(before : Bool) -> Affinity {
  if before {
    Before
  } else {
    After
  }
}

///|
pub fn Affinity::from_after(after : Bool) -> Affinity {
  if after {
    After
  } else {
    Before
  }
}

///|
/// Current cursor location
pub struct Cursor {
  /// Index of BufferLine in Buffer::lines
  line : Int
  /// First-byte-index of glyph at cursor (represented as UTF-16 code-unit index).
  index : Int
  affinity : Affinity
}

///|
pub impl Eq for Cursor with fn equal(self, other) {
  self.line == other.line &&
  self.index == other.index &&
  self.affinity == other.affinity
}

///|
pub fn Cursor::new(line : Int, index : Int) -> Cursor {
  Cursor::new_with_affinity(line, index, Before)
}

///|
pub fn Cursor::new_with_affinity(
  line : Int,
  index : Int,
  affinity : Affinity,
) -> Cursor {
  Cursor::{ line, index, affinity }
}

///|
/// The position of a cursor within a Buffer.
pub struct LayoutCursor {
  line : Int
  layout : Int
  glyph : Int
}

///|
pub impl Eq for LayoutCursor with fn equal(self, other) {
  self.line == other.line &&
  self.layout == other.layout &&
  self.glyph == other.glyph
}

///|
pub fn LayoutCursor::new(line : Int, layout : Int, glyph : Int) -> LayoutCursor {
  LayoutCursor::{ line, layout, glyph }
}

///|
/// A motion to perform on a Cursor
pub(all) enum Motion {
  LayoutCursor(LayoutCursor)
  Previous
  Next
  Left
  Right
  Up
  Down
  Home
  SoftHome
  End
  ParagraphStart
  ParagraphEnd
  PageUp
  PageDown
  Vertical(Int)
  PreviousWord
  NextWord
  LeftWord
  RightWord
  BufferStart
  BufferEnd
  GotoLine(Int)
}

///|
pub impl Eq for Motion with fn equal(self, other) {
  match (self, other) {
    (LayoutCursor(a), LayoutCursor(b)) => a == b
    (Previous, Previous) => true
    (Next, Next) => true
    (Left, Left) => true
    (Right, Right) => true
    (Up, Up) => true
    (Down, Down) => true
    (Home, Home) => true
    (SoftHome, SoftHome) => true
    (End, End) => true
    (ParagraphStart, ParagraphStart) => true
    (ParagraphEnd, ParagraphEnd) => true
    (PageUp, PageUp) => true
    (PageDown, PageDown) => true
    (Vertical(a), Vertical(b)) => a == b
    (PreviousWord, PreviousWord) => true
    (NextWord, NextWord) => true
    (LeftWord, LeftWord) => true
    (RightWord, RightWord) => true
    (BufferStart, BufferStart) => true
    (BufferEnd, BufferEnd) => true
    (GotoLine(a), GotoLine(b)) => a == b
    _ => false
  }
}

///|
/// Scroll position in Buffer
pub struct Scroll {
  line : Int
  vertical : Float
  horizontal : Float
}

///|
pub fn Scroll::default() -> Scroll {
  Scroll::{ line: 0, vertical: 0.0F, horizontal: 0.0F }
}

///|
pub fn Scroll::new(line : Int, vertical : Float, horizontal : Float) -> Scroll {
  Scroll::{ line, vertical, horizontal }
}