///|
pub(all) enum Sexp {
  Atom(String)
  List(Array[Sexp])
} derive(Debug, Eq, ToJson)

///|
/// TODO(upstream):
/// Warning: The type 'ParseError' does not occur in public signature of 
/// current package, consider marking it as `priv`
/// The above message will be misleading because ParseError is actually used
/// in the public signature.
/// Figure out the `pub`/abstract meaning of suberror
/// This generally makes sense but doesnot make sense for suberror since
/// It is an existential type
pub suberror ParseError {
  UnmatchedParens(String)
  UnexpectedParens(String)
  UnexpectedEOF
} derive(Debug, ToJson)

///|
priv enum Token {
  Lparen
  RParen
  Atom(String)
}

///|
impl ToJson for Token with to_json(self) {
  match self {
    Lparen => "("
    RParen => ")"
    Atom(s) => Json::string(s)
  }
}

///|
pub fn parse_sexp(input : String) -> Sexp raise {
  let tokens = Lexer::new(input).decode()
  let (sexp, remaining) = parse_tokens(tokens)
  guard remaining is [] else {
    raise UnmatchedParens("unexpected tokens at the end")
  }
  sexp
}

///|
fn parse_tokens(
  tokens : ArrayView[Token],
) -> (Sexp, ArrayView[Token]) raise ParseError {
  match tokens {
    [] => raise UnexpectedEOF
    [Lparen, .. rest] => {
      let subexps = []
      for view = rest {
        match view {
          [] => raise UnmatchedParens("missing closing parenthesis")
          [RParen, .. rest] => break (List(subexps), rest)
          _ as r => {
            let (subexp, remaining) = parse_tokens(r)
            subexps.push(subexp)
            continue remaining
          }
        }
      }
    }
    [RParen, ..] => raise UnexpectedParens("unexpected closing parenthesis")
    [Atom(token), .. rest] => (Sexp::Atom(token), rest)
  }
}

///|
priv struct Lexer {
  input : String
  mut pos : Int //
}

///|
fn Lexer::new(input : String) -> Lexer {
  { input, pos: 0 }
}

///|
fn Lexer::view(self : Lexer) -> StringView {
  let input = self.input
  let pos = self.pos
  input.view(start_offset=pos)
}

///|
fn Lexer::update_view(self : Lexer, view : StringView) -> Unit {
  self.pos = view.start_offset()
}

///|
fn Lexer::update_view_with_token(self : Lexer, view : StringView) -> String {
  let consumed = self.input.view(
    start_offset=self.pos,
    end_offset=view.start_offset(),
  )
  self.pos = view.start_offset()
  consumed.to_string()
}

///|
fn Lexer::decode(self : Lexer) -> Array[Token] raise {
  let tokens : Array[Token] = []
  for view = self.view() {
    match view {
      [] => break
      ['(', .. rest] => {
        tokens.push(Lparen)
        continue rest
      }
      [')', .. rest] => {
        tokens.push(RParen)
        continue rest
      }
      [' ' | '\n' | '\t', .. rest] => continue rest
      ['"', ..] as view => {
        self.update_view(view) // mark theh position, do the work
        tokens.push(self.decode_string())
        continue self.view()
      }
      _ as view => {
        self.update_view(view)
        tokens.push(self.decode_atom())
        continue self.view()
      }
    }
  }
  tokens
}

///|
fn Lexer::decode_string(self : Lexer) -> Token raise {
  // self.expect_char('"')
  let next = match self.view() {
    ['"', .. next] => next
    _ => fail("expected \" in the beginning of string")
  }
  let next = for view = next {
    match view {
      ['"', .. next] => break next
      ['\n', ..] => fail("unexpected newline in string")
      [_, .. rest] => continue rest
      [] => fail("unexpected end of input")
    }
  }
  Atom(self.update_view_with_token(next))
}

///|
test "decode_string" {
  fn decode_string(s) {
    Ok(Lexer::new(s).decode_string()) catch {
      @builtin.Failure(err) => Err(err.split("FAILED:")[1:].to_string()) // FIXME: upstream
      err => Err(err.to_string())
    }
  }

  json_inspect(decode_string("\"hello\""), content={ "Ok": "\"hello\"" })
  json_inspect(decode_string("\"hello\n\""), content={
    "Err": "[\" unexpected newline in string\"]",
  })
  json_inspect(
    decode_string(
      (
        #|"abc"
      ),
    ),
    content={ "Ok": "\"abc\"" },
  )
  json_inspect(decode_string("\"d..\""), content={ "Ok": "\"d..\"" })
}

///|
/// TODO(upstream): fmt add `///|`
fn Lexer::decode_atom(self : Lexer) -> Token {
  let next = for view = self.view() {
    match view {
      [] | ['(' | ')' | ' ' | '\n' | '\t', ..] as next => break next
      [_, .. rest] => continue rest
    }
  }
  Atom(self.update_view_with_token(next))
}

///|
test "decode_atom" {
  // only toplevel can have labelled arguments  
  // fn aux(s,content~) raise{
  //   json_inspect(s, content~)
  // }  
  fn decode_atom(s) {
    Lexer::new(s).decode_atom()
    // catch {
    //   @builtin.Failure(err) => Err(err.split("FAILED:")[1:].to_string()) // FIXME: upstream
    //   err => Err(err.to_string())
    // }
  }

  json_inspect(decode_atom("hello "), content="hello")
  json_inspect(decode_atom("hell("), content="hell")
  json_inspect(decode_atom("hello+ "), content="hello+")
}

///|
/// TODO(upstream):
/// support regular expression # to remove some regex
/// native support of end of input
// for input = input {
//   match input using longest {
//     (re"[ \n \t]+", rest) => continue rest
//     (re"\(", rest) => { tokens.push("("); continue rest }
//     (re"\)", rest) => { tokens.push(")"); continue rest }
//     (re"(?[a-zA-Z_0-9\+\-\*]+)", rest) => {tokens.push(id.to_string()); continue rest}
//     // (re"", _) => break // empty input
//     (re"",rest) => if rest is [] {break} else {fail("Unexpected token: \"\{input}\"")}
//     _ => fail("Unexpected token: \"\{input}\"")
//   }
// }
// pub fn tokenize(input : StringView) -> Array[StringView] raise {
//   if false {
//     fail("may fail")
//   }
//   let tokens : Array[StringView] = []
//   outer~: loop input {
//     [] => ()
//     ['(', .. rest] => {
//       tokens.push("(")
//       continue rest
//     }
//     [')', .. rest] => {
//       tokens.push(")")
//       continue rest
//     }
//     [' ' | '\n' | '\t', .. rest] => continue rest
//     [_, ..] as oid =>
//       for id = oid, count = 0 {
//         match id {
//           [] | ['(' | ')' | ' ' | '\n' | '\t', ..] => {
//             tokens.push(oid.view(end_offset=count))
//             continue outer~ id
//           }
//           [c, .. rest] =>
//             continue rest, count + (if c > '\u{ffff}' { 2 } else { 1 })
//         }
//       }
//   }
//   tokens
// }
test "decode" {
  fn tokenize(s) {
    Ok(Lexer::new(s).decode()) catch {
      @builtin.Failure(err) => Err(err.split("FAILED:")[1:].to_string()) // FIXME: upstream
      err => Err(err.to_string())
    }
  }

  json_inspect(tokenize("(hello world)"), content={
    "Ok": ["(", "hello", "world", ")"],
  })
  json_inspect(tokenize("(hello \"world\")"), content={
    "Ok": ["(", "hello", "\"world\"", ")"],
  })
  json_inspect(tokenize("(hello \"world\")"), content={
    "Ok": ["(", "hello", "\"world\"", ")"],
  })
}
// [ .. "("  , .. rest] we need support 
// [ .. "xx" as c ] so that we can get the view
// but it is constant so it does not matter that much?
// let mut curr_tokens = rest
// while curr_tokens is [next, .. _] && next != ")" {
//   let (subexp, remaining) = parse_tokens!(curr_tokens)
//   subexps.push(subexp)
//   curr_tokens = remaining
// }
// TODO: finish for loop is scope issue
// guard curr_tokens is [_, .. remaining_view] else {
//   raise UnmatchedParens("missing closing parenthesis")
// }
// (Sexp::List(subexps), remaining_view)

///|
test {
  fn tokenize(s) {
    Ok(Lexer::new(s).decode()) catch {
      @builtin.Failure(err) => Err(err.split("FAILED:")[1:].to_string()) // FIXME: upstream
      err => Err(err.to_string())
    }
  }

  let tokens = tokenize("(begin (define x 10) (define y 5) (+ x y))")
  // let tokens = tokenize(v)
  json_inspect(tokens, content={
    "Ok": [
      "(", "begin", "(", "define", "x", "10", ")", "(", "define", "y", "5", ")",
      "(", "+", "x", "y", ")", ")",
    ],
  })
  let tokens = tokenize("(if (> 5 2) 42 0)")
  json_inspect(tokens, content={
    "Ok": ["(", "if", "(", ">", "5", "2", ")", "42", "0", ")"],
  })
  let tokens = tokenize("(= 5 5)")
  json_inspect(tokens, content={ "Ok": ["(", "=", "5", "5", ")"] })
}

///|
test "tokenize" {
  let input = "(hello world)"
  let tokens = Lexer::new(input).decode()
  json_inspect(tokens, content=["(", "hello", "world", ")"])
}

///|
test "tokenize unicode" {
  let input = "(你好 世界)"
  let tokens = Lexer::new(input).decode()
  json_inspect(tokens, content=["(", "你好", "世界", ")"])
}

///|
test "tokenize unicode emoji" {
  let input = "(👋🏻 世界)"
  let tokens = Lexer::new(input).decode()
  json_inspect(tokens, content=["(", "👋🏻", "世界", ")"])
}

///|
// #cfg(false)
// fn Lexer::expect_char(self : Lexer, ch : Char) -> Unit raise {
//   match self.view() {
//     [] => fail("unexpected end of input")
//     [next, .. rest] => {
//       if next != ch {
//         fail("expected char \{ch} ,but got \{next}")
//       }
//       self.update_view(rest)
//     }
//   }
// }

///|
pub impl Show for Sexp with output(self, logger) {
  logger.write_string(@debug.render(self.to_repr(), max_depth=2147483647))
}

///|
pub impl Show for ParseError with output(self, logger) {
  logger.write_string(@debug.render(self.to_repr(), max_depth=2147483647))
}