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

///|
/// Position in source code (0-based)
///
/// `offset` is a 0-based codepoint offset within the source string.
pub struct Position {
  line : Int // 0-based line
  column : Int // 0-based column (in characters)
  offset : Int // 0-based codepoint offset
} derive(Eq, Debug)

///|
/// A range in source code from start to end position.
/// `path` may be empty for in-memory sources.
pub struct Range {
  path : String
  start : Position
  end : Position
} derive(Eq, Debug)

///|
/// Token types for the diago language
pub enum TokenKind {
  // Literals
  Ident(String) // identifiers and unquoted strings
  StringLit(StringType, String) // string literals with type
  Number(String) // numeric literals (kept as string for precision)

  // Keywords / special identifiers handled at parse level

  // Operators and punctuation
  Colon // :
  Semicolon // ;
  Dot // .
  Comma // ,
  LeftBrace // {
  RightBrace // }
  LeftBracket // [
  RightBracket // ]
  LeftParen // (
  RightParen // )
  Pipe // |
  Ampersand // &

  // Arrows
  Arrow // ->
  ReverseArrow // <-
  BidirectionalArrow // <->
  DoubleDash // --

  // Comments
  Comment(String) // # single line comment
  BlockComment(String) // /* block comment */

  // Special
  At // @ for imports
  DotDotDot // ... for spread imports
  Dollar // $ for substitutions
  DollarBrace // ${ for substitutions
  Star // * for glob patterns
  DoubleStar // ** for glob patterns
  TripleStar // *** for glob patterns

  // Whitespace and structure
  Newline // significant newlines

  // End of file
  Eof
} derive(Eq, Debug)

///|
/// String literal types
pub enum StringType {
  SingleQuoted
  DoubleQuoted
} derive(Eq, Debug)

///|
/// A token with its kind, range, and raw text
pub struct Token {
  kind : TokenKind
  range : Range
  raw : String // original source text
} derive(Eq, Debug)

///|
pub fn Position::new(line : Int, column : Int, offset : Int) -> Position {
  { line, column, offset }
}

///|
pub fn Range::new(start : Position, end : Position) -> Range {
  { path: "", start, end }
}

///|
pub fn Range::with_path(
  path : String,
  start : Position,
  end : Position,
) -> Range {
  { path, start, end }
}

///|
/// Create a range from the start of `a` to the end of `b`.
pub fn Range::between(a : Range, b : Range) -> Range {
  let path = if a.path != "" { a.path } else { b.path }
  { path, start: a.start, end: b.end }
}

///|
pub fn Token::new(kind : TokenKind, range : Range, raw : String) -> Token {
  { kind, range, raw }
}

///|
/// Create a simple position at the origin
pub fn Position::zero() -> Position {
  { line: 0, column: 0, offset: 0 }
}

///|
/// Check if a token is a trivia token (whitespace, comments)
pub fn Token::is_trivia(self : Token) -> Bool {
  match self.kind {
    Comment(_) | BlockComment(_) | Newline => true
    _ => false
  }
}

///|
/// Check if a token is an arrow
pub fn Token::is_arrow(self : Token) -> Bool {
  match self.kind {
    Arrow | ReverseArrow | BidirectionalArrow => true
    _ => false
  }
}

///|
/// Check if a token can start an edge
pub fn Token::is_edge_op(self : Token) -> Bool {
  match self.kind {
    Arrow | ReverseArrow | BidirectionalArrow | DoubleDash => true
    _ => false
  }
}