use crate::{
    LuaAstToken, LuaComment, LuaDocTagCallGeneric, LuaDocTypeList, LuaIndexToken, LuaKind,
    LuaLiteralToken, LuaSyntaxNode, LuaSyntaxToken, LuaTokenKind,
    kind::LuaSyntaxKind,
    syntax::{
        node::{LuaBinaryOpToken, LuaNameToken, LuaUnaryOpToken},
        traits::{LuaAstChildren, LuaAstNode, LuaCommentOwner},
    },
};

use super::{
    LuaBlock, LuaCallArgList, LuaIndexKey, LuaParamList, LuaTableField, path_trait::PathTrait,
};

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum LuaExpr {
    CallExpr(LuaCallExpr),
    TableExpr(LuaTableExpr),
    LiteralExpr(LuaLiteralExpr),
    BinaryExpr(LuaBinaryExpr),
    UnaryExpr(LuaUnaryExpr),
    ClosureExpr(LuaClosureExpr),
    ParenExpr(LuaParenExpr),
    NameExpr(LuaNameExpr),
    IndexExpr(LuaIndexExpr),
    TernaryExpr(LuaTernaryExpr),
}

impl LuaAstNode for LuaExpr {
    fn syntax(&self) -> &LuaSyntaxNode {
        match self {
            LuaExpr::CallExpr(node) => node.syntax(),
            LuaExpr::TableExpr(node) => node.syntax(),
            LuaExpr::LiteralExpr(node) => node.syntax(),
            LuaExpr::BinaryExpr(node) => node.syntax(),
            LuaExpr::UnaryExpr(node) => node.syntax(),
            LuaExpr::ClosureExpr(node) => node.syntax(),
            LuaExpr::ParenExpr(node) => node.syntax(),
            LuaExpr::NameExpr(node) => node.syntax(),
            LuaExpr::IndexExpr(node) => node.syntax(),
            LuaExpr::TernaryExpr(node) => node.syntax(),
        }
    }

    fn can_cast(kind: LuaSyntaxKind) -> bool
    where
        Self: Sized,
    {
        matches!(
            kind,
            LuaSyntaxKind::CallExpr
                | LuaSyntaxKind::AssertCallExpr
                | LuaSyntaxKind::ErrorCallExpr
                | LuaSyntaxKind::RequireCallExpr
                | LuaSyntaxKind::TypeCallExpr
                | LuaSyntaxKind::SetmetatableCallExpr
                | LuaSyntaxKind::TableArrayExpr
                | LuaSyntaxKind::TableObjectExpr
                | LuaSyntaxKind::TableEmptyExpr
                | LuaSyntaxKind::LiteralExpr
                | LuaSyntaxKind::BinaryExpr
                | LuaSyntaxKind::UnaryExpr
                | LuaSyntaxKind::ClosureExpr
                | LuaSyntaxKind::ParenExpr
                | LuaSyntaxKind::NameExpr
                | LuaSyntaxKind::IndexExpr
                | LuaSyntaxKind::SafeIndexExpr
                | LuaSyntaxKind::TernaryExpr
        )
    }

    fn cast(syntax: LuaSyntaxNode) -> Option<Self>
    where
        Self: Sized,
    {
        match syntax.kind().into() {
            LuaSyntaxKind::CallExpr
            | LuaSyntaxKind::AssertCallExpr
            | LuaSyntaxKind::ErrorCallExpr
            | LuaSyntaxKind::RequireCallExpr
            | LuaSyntaxKind::TypeCallExpr
            | LuaSyntaxKind::SetmetatableCallExpr => {
                LuaCallExpr::cast(syntax).map(LuaExpr::CallExpr)
            }
            LuaSyntaxKind::TableArrayExpr
            | LuaSyntaxKind::TableObjectExpr
            | LuaSyntaxKind::TableEmptyExpr => LuaTableExpr::cast(syntax).map(LuaExpr::TableExpr),
            LuaSyntaxKind::LiteralExpr => LuaLiteralExpr::cast(syntax).map(LuaExpr::LiteralExpr),
            LuaSyntaxKind::BinaryExpr => LuaBinaryExpr::cast(syntax).map(LuaExpr::BinaryExpr),
            LuaSyntaxKind::UnaryExpr => LuaUnaryExpr::cast(syntax).map(LuaExpr::UnaryExpr),
            LuaSyntaxKind::ClosureExpr => LuaClosureExpr::cast(syntax).map(LuaExpr::ClosureExpr),
            LuaSyntaxKind::ParenExpr => LuaParenExpr::cast(syntax).map(LuaExpr::ParenExpr),
            LuaSyntaxKind::NameExpr => LuaNameExpr::cast(syntax).map(LuaExpr::NameExpr),
            LuaSyntaxKind::IndexExpr => LuaIndexExpr::cast(syntax).map(LuaExpr::IndexExpr),
            LuaSyntaxKind::SafeIndexExpr => LuaIndexExpr::cast(syntax).map(LuaExpr::IndexExpr),
            LuaSyntaxKind::TernaryExpr => LuaTernaryExpr::cast(syntax).map(LuaExpr::TernaryExpr),
            _ => None,
        }
    }
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum LuaVarExpr {
    NameExpr(LuaNameExpr),
    IndexExpr(LuaIndexExpr),
}

impl LuaAstNode for LuaVarExpr {
    fn syntax(&self) -> &LuaSyntaxNode {
        match self {
            LuaVarExpr::NameExpr(node) => node.syntax(),
            LuaVarExpr::IndexExpr(node) => node.syntax(),
        }
    }

    fn can_cast(kind: LuaSyntaxKind) -> bool
    where
        Self: Sized,
    {
        matches!(
            kind,
            LuaSyntaxKind::NameExpr | LuaSyntaxKind::IndexExpr | LuaSyntaxKind::SafeIndexExpr
        )
    }

    fn cast(syntax: LuaSyntaxNode) -> Option<Self>
    where
        Self: Sized,
    {
        match syntax.kind().into() {
            LuaSyntaxKind::NameExpr => LuaNameExpr::cast(syntax).map(LuaVarExpr::NameExpr),
            LuaSyntaxKind::IndexExpr | LuaSyntaxKind::SafeIndexExpr => {
                LuaIndexExpr::cast(syntax).map(LuaVarExpr::IndexExpr)
            }
            _ => None,
        }
    }
}

impl LuaVarExpr {
    pub fn to_expr(&self) -> LuaExpr {
        match self {
            LuaVarExpr::NameExpr(node) => LuaExpr::NameExpr(node.clone()),
            LuaVarExpr::IndexExpr(node) => LuaExpr::IndexExpr(node.clone()),
        }
    }
}

impl From<LuaVarExpr> for LuaExpr {
    fn from(expr: LuaVarExpr) -> Self {
        match expr {
            LuaVarExpr::NameExpr(node) => LuaExpr::NameExpr(node),
            LuaVarExpr::IndexExpr(node) => LuaExpr::IndexExpr(node),
        }
    }
}

impl PathTrait for LuaVarExpr {}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum LuaSingleArgExpr {
    TableExpr(LuaTableExpr),
    LiteralExpr(LuaLiteralExpr),
}

impl LuaAstNode for LuaSingleArgExpr {
    fn syntax(&self) -> &LuaSyntaxNode {
        match self {
            LuaSingleArgExpr::TableExpr(node) => node.syntax(),
            LuaSingleArgExpr::LiteralExpr(node) => node.syntax(),
        }
    }

    fn can_cast(kind: LuaSyntaxKind) -> bool
    where
        Self: Sized,
    {
        matches!(
            kind,
            LuaSyntaxKind::TableArrayExpr
                | LuaSyntaxKind::TableObjectExpr
                | LuaSyntaxKind::TableEmptyExpr
                | LuaSyntaxKind::LiteralExpr
        )
    }

    fn cast(syntax: LuaSyntaxNode) -> Option<Self>
    where
        Self: Sized,
    {
        match syntax.kind().into() {
            LuaSyntaxKind::TableArrayExpr
            | LuaSyntaxKind::TableObjectExpr
            | LuaSyntaxKind::TableEmptyExpr => {
                LuaTableExpr::cast(syntax).map(LuaSingleArgExpr::TableExpr)
            }
            LuaSyntaxKind::LiteralExpr => {
                LuaLiteralExpr::cast(syntax).map(LuaSingleArgExpr::LiteralExpr)
            }
            _ => None,
        }
    }
}

impl From<LuaSingleArgExpr> for LuaExpr {
    fn from(expr: LuaSingleArgExpr) -> Self {
        match expr {
            LuaSingleArgExpr::TableExpr(node) => LuaExpr::TableExpr(node),
            LuaSingleArgExpr::LiteralExpr(node) => LuaExpr::LiteralExpr(node),
        }
    }
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct LuaNameExpr {
    syntax: LuaSyntaxNode,
}

impl LuaAstNode for LuaNameExpr {
    fn syntax(&self) -> &LuaSyntaxNode {
        &self.syntax
    }

    fn can_cast(kind: LuaSyntaxKind) -> bool
    where
        Self: Sized,
    {
        kind == LuaSyntaxKind::NameExpr
    }

    fn cast(syntax: LuaSyntaxNode) -> Option<Self>
    where
        Self: Sized,
    {
        if Self::can_cast(syntax.kind().into()) {
            Some(Self { syntax })
        } else {
            None
        }
    }
}

impl LuaCommentOwner for LuaNameExpr {}

impl LuaNameExpr {
    pub fn get_name_token(&self) -> Option<LuaNameToken> {
        self.token()
    }

    pub fn get_name_text(&self) -> Option<String> {
        self.get_name_token()
            .map(|it| it.get_name_text().to_string())
    }
}

impl PathTrait for LuaNameExpr {}

impl From<LuaNameExpr> for LuaVarExpr {
    fn from(expr: LuaNameExpr) -> Self {
        LuaVarExpr::NameExpr(expr)
    }
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct LuaIndexExpr {
    syntax: LuaSyntaxNode,
}

impl LuaAstNode for LuaIndexExpr {
    fn syntax(&self) -> &LuaSyntaxNode {
        &self.syntax
    }

    fn can_cast(kind: LuaSyntaxKind) -> bool
    where
        Self: Sized,
    {
        kind == LuaSyntaxKind::IndexExpr || kind == LuaSyntaxKind::SafeIndexExpr
    }

    fn cast(syntax: LuaSyntaxNode) -> Option<Self>
    where
        Self: Sized,
    {
        if Self::can_cast(syntax.kind().into()) {
            Some(Self { syntax })
        } else {
            None
        }
    }
}

impl LuaIndexExpr {
    pub fn get_prefix_expr(&self) -> Option<LuaExpr> {
        self.child()
    }

    pub fn get_index_token(&self) -> Option<LuaIndexToken> {
        let mut it = self
            .syntax()
            .children_with_tokens()
            .filter_map(|it| it.into_token().and_then(LuaIndexToken::cast));
        let first = it.next()?;
        if first.is_safe_navigation() {
            let second = it.next();
            if second.is_none() {
                return first.into();
            }

            return second;
        }

        first.into()
    }

    pub fn get_index_key(&self) -> Option<LuaIndexKey> {
        let mut meet_left_bracket = false;
        for child in self.syntax.children_with_tokens() {
            if meet_left_bracket {
                match child {
                    rowan::NodeOrToken::Node(node) => {
                        if LuaLiteralExpr::can_cast(node.kind().into()) {
                            let literal_expr = LuaLiteralExpr::cast(node.clone()).unwrap();
                            if let Some(literal_token) = literal_expr.get_literal() {
                                match literal_token {
                                    LuaLiteralToken::String(token) => {
                                        return Some(LuaIndexKey::String(token.clone()));
                                    }
                                    LuaLiteralToken::Number(token) => {
                                        return Some(LuaIndexKey::Integer(token.clone()));
                                    }
                                    _ => {}
                                }
                            }
                        }

                        return Some(LuaIndexKey::Expr(LuaExpr::cast(node).unwrap()));
                    }
                    _ => {
                        if let Some(token) = child.as_token()
                            && matches!(
                                token.kind().to_token(),
                                LuaTokenKind::TkWhitespace
                                    | LuaTokenKind::TkEndOfLine
                                    | LuaTokenKind::TkShortComment
                                    | LuaTokenKind::TkLongComment
                            )
                        {
                            continue;
                        }
                        return None;
                    }
                }
            } else if let Some(token) = child.as_token() {
                if token.kind() == LuaTokenKind::TkLeftBracket.into() {
                    meet_left_bracket = true;
                } else if token.kind() == LuaTokenKind::TkName.into() {
                    return Some(LuaIndexKey::Name(
                        LuaNameToken::cast(token.clone()).unwrap(),
                    ));
                }
            }
        }

        None
    }

    pub fn get_index_name_token(&self) -> Option<LuaSyntaxToken> {
        let index_token = self.get_index_token()?;
        index_token.syntax().next_token()
    }

    pub fn get_name_token(&self) -> Option<LuaNameToken> {
        self.token()
    }

    pub fn is_safe_index(&self) -> bool {
        self.syntax().kind() == LuaSyntaxKind::SafeIndexExpr.into()
    }
}

impl PathTrait for LuaIndexExpr {}

impl From<LuaIndexExpr> for LuaVarExpr {
    fn from(expr: LuaIndexExpr) -> Self {
        LuaVarExpr::IndexExpr(expr)
    }
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct LuaCallExpr {
    syntax: LuaSyntaxNode,
}

impl LuaAstNode for LuaCallExpr {
    fn syntax(&self) -> &LuaSyntaxNode {
        &self.syntax
    }

    fn can_cast(kind: LuaSyntaxKind) -> bool
    where
        Self: Sized,
    {
        kind == LuaSyntaxKind::CallExpr
            || kind == LuaSyntaxKind::AssertCallExpr
            || kind == LuaSyntaxKind::ErrorCallExpr
            || kind == LuaSyntaxKind::RequireCallExpr
            || kind == LuaSyntaxKind::TypeCallExpr
            || kind == LuaSyntaxKind::SetmetatableCallExpr
    }

    fn cast(syntax: LuaSyntaxNode) -> Option<Self>
    where
        Self: Sized,
    {
        if Self::can_cast(syntax.kind().into()) {
            Some(Self { syntax })
        } else {
            None
        }
    }
}

impl LuaCallExpr {
    pub fn get_prefix_expr(&self) -> Option<LuaExpr> {
        self.child()
    }

    pub fn get_args_list(&self) -> Option<LuaCallArgList> {
        self.child()
    }

    pub fn get_args_count(&self) -> Option<usize> {
        self.get_args_list().map(|it| it.get_args().count())
    }

    pub fn is_colon_call(&self) -> bool {
        if let Some(index_token) = self.get_colon_token() {
            return index_token.is_colon();
        }
        false
    }

    pub fn get_colon_token(&self) -> Option<LuaIndexToken> {
        self.get_prefix_expr().and_then(|prefix| match prefix {
            LuaExpr::IndexExpr(index_expr) => index_expr.get_index_token(),
            _ => None,
        })
    }

    pub fn is_require(&self) -> bool {
        self.syntax().kind() == LuaSyntaxKind::RequireCallExpr.into()
    }

    pub fn is_error(&self) -> bool {
        self.syntax().kind() == LuaSyntaxKind::ErrorCallExpr.into()
    }

    pub fn is_assert(&self) -> bool {
        self.syntax().kind() == LuaSyntaxKind::AssertCallExpr.into()
    }

    pub fn is_type(&self) -> bool {
        self.syntax().kind() == LuaSyntaxKind::TypeCallExpr.into()
    }

    pub fn is_setmetatable(&self) -> bool {
        self.syntax().kind() == LuaSyntaxKind::SetmetatableCallExpr.into()
    }

    pub fn get_call_generic_type_list(&self) -> Option<LuaDocTypeList> {
        let mut current_node = self.syntax().first_child()?.next_sibling();

        while let Some(node) = &current_node {
            match node.kind() {
                LuaKind::Syntax(LuaSyntaxKind::Comment) => {
                    let comment = LuaComment::cast(node.clone())?;
                    let call_generic = comment.child::<LuaDocTagCallGeneric>()?;
                    return call_generic.get_type_list();
                }
                LuaKind::Syntax(LuaSyntaxKind::CallArgList) => {
                    return None;
                }
                _ => {}
            }
            current_node = node.next_sibling();
        }

        None
    }

    pub fn has_safe_navigation(&self) -> bool {
        self.token_by_kind(LuaTokenKind::TkSafeNavigation).is_some()
    }
}

impl PathTrait for LuaCallExpr {}

impl From<LuaCallExpr> for LuaExpr {
    fn from(expr: LuaCallExpr) -> Self {
        LuaExpr::CallExpr(expr)
    }
}

/// In Lua, tables are a fundamental data structure that can be used to represent arrays, objects,
/// and more. To facilitate parsing and handling of different table structures, we categorize tables
/// into three types: `TableArrayExpr`, `TableObjectExpr`, and `TableEmptyExpr`.
///
/// - `TableArrayExpr`: Represents a table used as an array, where elements are indexed by integers.
/// - `TableObjectExpr`: Represents a table used as an object, where elements are indexed by strings or other keys.
/// - `TableEmptyExpr`: Represents an empty table with no elements.
///
/// This categorization helps in accurately parsing and processing Lua code by distinguishing between
/// different uses of tables, thereby enabling more precise syntax analysis and manipulation.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct LuaTableExpr {
    syntax: LuaSyntaxNode,
}

impl LuaAstNode for LuaTableExpr {
    fn syntax(&self) -> &LuaSyntaxNode {
        &self.syntax
    }

    fn can_cast(kind: LuaSyntaxKind) -> bool
    where
        Self: Sized,
    {
        kind == LuaSyntaxKind::TableArrayExpr
            || kind == LuaSyntaxKind::TableObjectExpr
            || kind == LuaSyntaxKind::TableEmptyExpr
    }

    fn cast(syntax: LuaSyntaxNode) -> Option<Self>
    where
        Self: Sized,
    {
        if Self::can_cast(syntax.kind().into()) {
            Some(Self { syntax })
        } else {
            None
        }
    }
}

impl LuaCommentOwner for LuaTableExpr {}

impl LuaTableExpr {
    pub fn is_empty(&self) -> bool {
        self.syntax().kind() == LuaSyntaxKind::TableEmptyExpr.into()
    }

    pub fn is_array(&self) -> bool {
        self.syntax().kind() == LuaSyntaxKind::TableArrayExpr.into()
    }

    pub fn is_object(&self) -> bool {
        self.syntax().kind() == LuaSyntaxKind::TableObjectExpr.into()
    }

    pub fn get_fields(&self) -> LuaAstChildren<LuaTableField> {
        self.children()
    }

    pub fn get_fields_with_keys(&self) -> Vec<(LuaTableField, LuaIndexKey)> {
        let mut sequence_index = 0usize;
        self.get_fields()
            .filter_map(|field| {
                if field.is_value_field() {
                    sequence_index += 1;
                    return Some((field, LuaIndexKey::Idx(sequence_index)));
                }

                field.get_field_key().map(|key| (field, key))
            })
            .collect()
    }
}

impl From<LuaTableExpr> for LuaSingleArgExpr {
    fn from(expr: LuaTableExpr) -> Self {
        LuaSingleArgExpr::TableExpr(expr)
    }
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct LuaLiteralExpr {
    syntax: LuaSyntaxNode,
}

impl LuaAstNode for LuaLiteralExpr {
    fn syntax(&self) -> &LuaSyntaxNode {
        &self.syntax
    }

    fn can_cast(kind: LuaSyntaxKind) -> bool
    where
        Self: Sized,
    {
        kind == LuaSyntaxKind::LiteralExpr
    }

    fn cast(syntax: LuaSyntaxNode) -> Option<Self>
    where
        Self: Sized,
    {
        if Self::can_cast(syntax.kind().into()) {
            Some(Self { syntax })
        } else {
            None
        }
    }
}

impl LuaLiteralExpr {
    pub fn get_literal(&self) -> Option<LuaLiteralToken> {
        self.token()
    }
}

impl From<LuaLiteralExpr> for LuaSingleArgExpr {
    fn from(expr: LuaLiteralExpr) -> Self {
        LuaSingleArgExpr::LiteralExpr(expr)
    }
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct LuaBinaryExpr {
    syntax: LuaSyntaxNode,
}

impl LuaAstNode for LuaBinaryExpr {
    fn syntax(&self) -> &LuaSyntaxNode {
        &self.syntax
    }

    fn can_cast(kind: LuaSyntaxKind) -> bool
    where
        Self: Sized,
    {
        kind == LuaSyntaxKind::BinaryExpr
    }

    fn cast(syntax: LuaSyntaxNode) -> Option<Self>
    where
        Self: Sized,
    {
        if Self::can_cast(syntax.kind().into()) {
            Some(Self { syntax })
        } else {
            None
        }
    }
}

impl LuaBinaryExpr {
    pub fn get_exprs(&self) -> Option<(LuaExpr, LuaExpr)> {
        let exprs = self.children::<LuaExpr>().collect::<Vec<_>>();
        if exprs.len() == 2 {
            Some((exprs[0].clone(), exprs[1].clone()))
        } else {
            None
        }
    }

    pub fn get_op_token(&self) -> Option<LuaBinaryOpToken> {
        self.token()
    }

    pub fn get_left_expr(&self) -> Option<LuaExpr> {
        self.child()
    }
}

impl From<LuaBinaryExpr> for LuaExpr {
    fn from(expr: LuaBinaryExpr) -> Self {
        LuaExpr::BinaryExpr(expr)
    }
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct LuaUnaryExpr {
    syntax: LuaSyntaxNode,
}

impl LuaAstNode for LuaUnaryExpr {
    fn syntax(&self) -> &LuaSyntaxNode {
        &self.syntax
    }

    fn can_cast(kind: LuaSyntaxKind) -> bool
    where
        Self: Sized,
    {
        kind == LuaSyntaxKind::UnaryExpr
    }

    fn cast(syntax: LuaSyntaxNode) -> Option<Self>
    where
        Self: Sized,
    {
        if Self::can_cast(syntax.kind().into()) {
            Some(Self { syntax })
        } else {
            None
        }
    }
}

impl LuaUnaryExpr {
    pub fn get_expr(&self) -> Option<LuaExpr> {
        self.child()
    }

    pub fn get_op_token(&self) -> Option<LuaUnaryOpToken> {
        self.token()
    }
}

impl From<LuaUnaryExpr> for LuaExpr {
    fn from(expr: LuaUnaryExpr) -> Self {
        LuaExpr::UnaryExpr(expr)
    }
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct LuaClosureExpr {
    syntax: LuaSyntaxNode,
}

impl LuaAstNode for LuaClosureExpr {
    fn syntax(&self) -> &LuaSyntaxNode {
        &self.syntax
    }

    fn can_cast(kind: LuaSyntaxKind) -> bool
    where
        Self: Sized,
    {
        kind == LuaSyntaxKind::ClosureExpr
    }

    fn cast(syntax: LuaSyntaxNode) -> Option<Self>
    where
        Self: Sized,
    {
        if Self::can_cast(syntax.kind().into()) {
            Some(Self { syntax })
        } else {
            None
        }
    }
}

impl LuaClosureExpr {
    pub fn get_block(&self) -> Option<LuaBlock> {
        self.child()
    }

    pub fn get_params_list(&self) -> Option<LuaParamList> {
        self.child()
    }

    pub fn is_short_closure(&self) -> bool {
        self.token_by_kind(LuaTokenKind::TkArrow).is_some()
    }

    pub fn has_do_block(&self) -> bool {
        self.token_by_kind(LuaTokenKind::TkDo).is_some()
    }
}

impl From<LuaClosureExpr> for LuaExpr {
    fn from(expr: LuaClosureExpr) -> Self {
        LuaExpr::ClosureExpr(expr)
    }
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct LuaParenExpr {
    syntax: LuaSyntaxNode,
}

impl LuaAstNode for LuaParenExpr {
    fn syntax(&self) -> &LuaSyntaxNode {
        &self.syntax
    }

    fn can_cast(kind: LuaSyntaxKind) -> bool
    where
        Self: Sized,
    {
        kind == LuaSyntaxKind::ParenExpr
    }

    fn cast(syntax: LuaSyntaxNode) -> Option<Self>
    where
        Self: Sized,
    {
        if Self::can_cast(syntax.kind().into()) {
            Some(Self { syntax })
        } else {
            None
        }
    }
}

impl LuaParenExpr {
    pub fn get_expr(&self) -> Option<LuaExpr> {
        self.child()
    }
}

impl From<LuaParenExpr> for LuaExpr {
    fn from(expr: LuaParenExpr) -> Self {
        LuaExpr::ParenExpr(expr)
    }
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct LuaTernaryExpr {
    syntax: LuaSyntaxNode,
}

impl LuaAstNode for LuaTernaryExpr {
    fn syntax(&self) -> &LuaSyntaxNode {
        &self.syntax
    }

    fn can_cast(kind: LuaSyntaxKind) -> bool
    where
        Self: Sized,
    {
        kind == LuaSyntaxKind::TernaryExpr
    }

    fn cast(syntax: LuaSyntaxNode) -> Option<Self>
    where
        Self: Sized,
    {
        if Self::can_cast(syntax.kind().into()) {
            Some(Self { syntax })
        } else {
            None
        }
    }
}

impl LuaTernaryExpr {
    pub fn get_condition_expr(&self) -> Option<LuaExpr> {
        let mut exprs = self.children::<LuaExpr>();
        exprs.next()
    }

    pub fn get_true_false_exprs(&self) -> Option<(LuaExpr, LuaExpr)> {
        let mut exprs = self.children::<LuaExpr>();
        exprs.next();
        let true_expr = exprs.next()?;
        let false_expr = exprs.next()?;
        Some((true_expr, false_expr))
    }

    pub fn get_exprs(&self) -> Option<(LuaExpr, LuaExpr, LuaExpr)> {
        let mut exprs = self.children::<LuaExpr>();
        let condition_expr = exprs.next()?;
        let true_expr = exprs.next()?;
        let false_expr = exprs.next()?;
        Some((condition_expr, true_expr, false_expr))
    }
}
