// 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.

///|
/// Route types for edge path rendering
///
/// This module defines routing styles and waypoint management for edges.

///|
/// Route style for edge paths
pub(all) enum RouteStyle {
  /// Direct straight line between endpoints
  Straight
  /// Smooth bezier curve
  Curved
  /// Right-angle turns (Manhattan routing)
  Elbow
  /// Optimal axis-aligned routing that avoids obstacles
  Orthogonal
} derive(Eq, Debug)

///|
/// Parse route style from string
pub fn RouteStyle::from_string(s : String) -> RouteStyle {
  match s.to_lower() {
    "straight" | "line" | "direct" => Straight
    "curved" | "curve" | "bezier" => Curved
    "elbow" | "manhattan" => Elbow
    "orthogonal" | "ortho" => Orthogonal
    _ => Straight // Default
  }
}

///|
/// Convert route style to canonical string
pub fn RouteStyle::to_route_string(self : RouteStyle) -> String {
  match self {
    Straight => "straight"
    Curved => "curved"
    Elbow => "elbow"
    Orthogonal => "orthogonal"
  }
}

///|
/// Check if this style uses curves
pub fn RouteStyle::is_curved(self : RouteStyle) -> Bool {
  match self {
    Curved => true
    _ => false
  }
}

///|
/// Check if this style uses right angles
pub fn RouteStyle::is_orthogonal(self : RouteStyle) -> Bool {
  match self {
    Elbow | Orthogonal => true
    _ => false
  }
}

///|
/// A waypoint along an edge route
pub struct Waypoint {
  /// X coordinate
  x : Double
  /// Y coordinate
  y : Double
  /// Optional control point for curves (for bezier curves)
  control : (Double, Double)?
} derive(Debug)

///|
/// Create a simple waypoint
pub fn Waypoint::new(x : Double, y : Double) -> Waypoint {
  { x, y, control: None }
}

///|
/// Create a waypoint with a bezier control point
pub fn Waypoint::with_control(
  x : Double,
  y : Double,
  cx : Double,
  cy : Double,
) -> Waypoint {
  { x, y, control: Some((cx, cy)) }
}

///|
/// Convert waypoint to Point
pub fn Waypoint::to_point(self : Waypoint) -> Point {
  Point::new(self.x, self.y)
}

///|
/// Route configuration for an edge
pub struct Route {
  /// The routing style
  style : RouteStyle
  /// Waypoints along the route (including start and end)
  waypoints : Array[Waypoint]
  /// Source arrowhead type
  src_arrowhead : ArrowheadType
  /// Destination arrowhead type
  dst_arrowhead : ArrowheadType
} derive(Debug)

///|
/// Create a new empty route with default style
pub fn Route::new() -> Route {
  {
    style: Straight,
    waypoints: [],
    src_arrowhead: None,
    dst_arrowhead: Triangle,
  }
}

///|
/// Create a route with specified style
pub fn Route::with_style(style : RouteStyle) -> Route {
  { style, waypoints: [], src_arrowhead: None, dst_arrowhead: Triangle }
}

///|
/// Add a waypoint to the route
pub fn Route::add_waypoint(self : Route, wp : Waypoint) -> Unit {
  self.waypoints.push(wp)
}

///|
/// Add a simple point to the route
pub fn Route::add_point(self : Route, x : Double, y : Double) -> Unit {
  self.waypoints.push(Waypoint::new(x, y))
}

///|
/// Get all points as a simple array (for backward compatibility)
pub fn Route::to_points(self : Route) -> Array[Point] {
  let points : Array[Point] = []
  for wp in self.waypoints {
    points.push(wp.to_point())
  }
  points
}

///|
/// Check if route has any waypoints
pub fn Route::is_empty(self : Route) -> Bool {
  self.waypoints.is_empty()
}

///|
/// Get the number of waypoints
pub fn Route::length(self : Route) -> Int {
  self.waypoints.length()
}

///|
/// Get the starting point of the route
pub fn Route::start(self : Route) -> Waypoint? {
  if self.waypoints.is_empty() {
    None
  } else {
    Some(self.waypoints[0])
  }
}

///|
/// Get the ending point of the route
pub fn Route::end(self : Route) -> Waypoint? {
  if self.waypoints.is_empty() {
    None
  } else {
    Some(self.waypoints[self.waypoints.length() - 1])
  }
}

///|
/// Convert from Edge's simple route (Array[Point]) to Route
pub fn Route::from_points(
  points : Array[Point],
  style : RouteStyle,
  src : ArrowheadType,
  dst : ArrowheadType,
) -> Route {
  let waypoints : Array[Waypoint] = []
  for p in points {
    waypoints.push(Waypoint::new(p.x, p.y))
  }
  { style, waypoints, src_arrowhead: src, dst_arrowhead: dst }
}