#![allow(dead_code)]
use super::check::MethodInfo;
use super::lookup::MethodSet;
use super::obj::fmt_obj;
use super::objects::TCObjects;
use super::objects::{ObjKey, ScopeKey, TypeKey};
use super::operand::{fmt_expr, Operand};
use super::scope::fmt_scope_full;
use super::selection::Selection;
use super::typ::fmt_type;
use goscript_parser::ast::Node;
use goscript_parser::objects::Objects as AstObjects;
use goscript_parser::{ast::Expr, Pos};
use std::fmt;

pub fn type_str(t: &TypeKey, tco: &TCObjects) -> String {
    format!("{}", Displayer::new(t, None, Some(tco)))
}

pub trait Display {
    fn format(
        &self,
        f: &mut fmt::Formatter,
        asto: Option<&AstObjects>,
        tco: Option<&TCObjects>,
    ) -> fmt::Result;

    fn position(&self, _: Option<&AstObjects>, _: Option<&TCObjects>) -> Pos {
        unreachable!()
    }
}

pub struct Displayer<'a> {
    obj: &'a dyn Display,
    asto: Option<&'a AstObjects>,
    tco: Option<&'a TCObjects>,
}

impl<'a> Displayer<'a> {
    pub fn new(
        obj: &'a dyn Display,
        asto: Option<&'a AstObjects>,
        tco: Option<&'a TCObjects>,
    ) -> Displayer<'a> {
        Displayer {
            obj: obj,
            asto: asto,
            tco: tco,
        }
    }

    pub fn pos(&self) -> Pos {
        self.obj.position(self.asto, self.tco)
    }
}

impl<'a> fmt::Display for Displayer<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.obj.format(f, self.asto, self.tco)
    }
}

impl<'a> Display for Expr {
    fn format(
        &self,
        f: &mut fmt::Formatter,
        asto: Option<&AstObjects>,
        _: Option<&TCObjects>,
    ) -> fmt::Result {
        fmt_expr(self, f, asto.as_ref().unwrap())
    }

    fn position(&self, asto: Option<&AstObjects>, _: Option<&TCObjects>) -> Pos {
        self.pos(asto.as_ref().unwrap())
    }
}

impl<'a> Display for Operand {
    fn format(
        &self,
        f: &mut fmt::Formatter,
        asto: Option<&AstObjects>,
        tco: Option<&TCObjects>,
    ) -> fmt::Result {
        self.fmt(f, tco.as_ref().unwrap(), asto.as_ref().unwrap())
    }

    fn position(&self, asto: Option<&AstObjects>, _: Option<&TCObjects>) -> Pos {
        self.pos(asto.as_ref().unwrap())
    }
}

impl<'a> Display for ObjKey {
    fn format(
        &self,
        f: &mut fmt::Formatter,
        _: Option<&AstObjects>,
        tco: Option<&TCObjects>,
    ) -> fmt::Result {
        fmt_obj(*self, f, tco.as_ref().unwrap())
    }

    fn position(&self, _: Option<&AstObjects>, tco: Option<&TCObjects>) -> Pos {
        tco.as_ref().unwrap().lobjs[*self].pos()
    }
}

impl<'a> Display for TypeKey {
    fn format(
        &self,
        f: &mut fmt::Formatter,
        _: Option<&AstObjects>,
        tco: Option<&TCObjects>,
    ) -> fmt::Result {
        fmt_type(Some(*self), f, tco.as_ref().unwrap())
    }
}

impl<'a> Display for MethodInfo {
    fn format(
        &self,
        f: &mut fmt::Formatter,
        asto: Option<&AstObjects>,
        tco: Option<&TCObjects>,
    ) -> fmt::Result {
        self.fmt(f, tco.as_ref().unwrap(), asto.as_ref().unwrap())
    }
}

impl<'a> Display for Selection {
    fn format(
        &self,
        f: &mut fmt::Formatter,
        _: Option<&AstObjects>,
        tco: Option<&TCObjects>,
    ) -> fmt::Result {
        self.fmt(f, tco.as_ref().unwrap())
    }
}

impl<'a> Display for MethodSet {
    fn format(
        &self,
        f: &mut fmt::Formatter,
        _: Option<&AstObjects>,
        tco: Option<&TCObjects>,
    ) -> fmt::Result {
        self.fmt(f, tco.as_ref().unwrap())
    }
}

impl<'a> Display for ScopeKey {
    fn format(
        &self,
        f: &mut fmt::Formatter,
        _: Option<&AstObjects>,
        tco: Option<&TCObjects>,
    ) -> fmt::Result {
        fmt_scope_full(self, f, 0, tco.as_ref().unwrap())
    }
}
