///|
/// JSON value representation
pub(all) enum JsonValue {
  Null
  Bool(Bool)
  Number(Double)
  String(String)
  Array(Array[JsonValue])
  Object(Map[String, JsonValue])
} derive(Eq)

///|
/// Custom Show implementation for JsonValue
pub impl Show for JsonValue with output(self, logger) {
  match self {
    Null => logger.write_string("null")
    Bool(b) => logger.write_string(b.to_string())
    Number(n) => logger.write_string(n.to_string())
    String(s) => {
      logger.write_string("\"")
      logger.write_string(s)
      logger.write_string("\"")
    }
    Array(arr) => {
      logger.write_string("[")
      for i, v in arr {
        if i > 0 {
          logger.write_string(", ")
        }
        v.output(logger)
      }
      logger.write_string("]")
    }
    Object(obj) => {
      logger.write_string("{")
      let mut first = true
      for k, v in obj {
        if not(first) {
          logger.write_string(", ")
        }
        first = false
        logger.write_string("\"")
        logger.write_string(k)
        logger.write_string("\": ")
        v.output(logger)
      }
      logger.write_string("}")
    }
  }
}

///|
/// Position in source for error reporting
pub(all) struct Position {
  line : Int
  column : Int
} derive(Show, Eq)

///|
/// JSON parse errors
pub(all) suberror JsonError {
  UnexpectedChar(Position, Char)
  UnexpectedToken(Position, String)
  UnexpectedEof(Position)
  InvalidNumber(Position, String)
  InvalidEscape(Position, String)
  InvalidUnicode(Position, String)
  TrailingComma(Position)
  DuplicateKey(Position, String)
} derive(Show, Eq)

///|
/// Helper to get object from JsonValue
pub fn JsonValue::as_object(self : JsonValue) -> Map[String, JsonValue]? {
  match self {
    Object(obj) => Some(obj)
    _ => None
  }
}

///|
/// Helper to get array from JsonValue
pub fn JsonValue::as_array(self : JsonValue) -> Array[JsonValue]? {
  match self {
    Array(arr) => Some(arr)
    _ => None
  }
}

///|
/// Helper to get string from JsonValue
pub fn JsonValue::as_string(self : JsonValue) -> String? {
  match self {
    String(s) => Some(s)
    _ => None
  }
}

///|
/// Helper to get number from JsonValue
pub fn JsonValue::as_number(self : JsonValue) -> Double? {
  match self {
    Number(n) => Some(n)
    _ => None
  }
}

///|
/// Helper to get boolean from JsonValue
pub fn JsonValue::as_bool(self : JsonValue) -> Bool? {
  match self {
    Bool(b) => Some(b)
    _ => None
  }
}

///|
/// Check if value is null
pub fn JsonValue::is_null(self : JsonValue) -> Bool {
  match self {
    Null => true
    _ => false
  }
}