use alloc::borrow::Cow;
use alloc::vec::Vec;

use crate::render::command::CompositeMode;
use crate::render::path::{Path, PathCmd};
use crate::render::raster::FillRule;
use crate::render::scene::{ResourceRef, Scene, SceneOp};
use crate::types::{Color, Fixed, Point, Rect, Transform};

impl From<mirx::Fixed> for Fixed {
    fn from(v: mirx::Fixed) -> Self {
        Fixed(v.raw())
    }
}

impl From<Fixed> for mirx::Fixed {
    fn from(v: Fixed) -> Self {
        mirx::Fixed(v.0)
    }
}

impl From<mirx::Point> for Point {
    fn from(p: mirx::Point) -> Self {
        Self {
            x: p.x.into(),
            y: p.y.into(),
        }
    }
}

impl From<Point> for mirx::Point {
    fn from(p: Point) -> Self {
        Self {
            x: p.x.into(),
            y: p.y.into(),
        }
    }
}

impl From<mirx::Rect> for Rect {
    fn from(r: mirx::Rect) -> Self {
        Self {
            x: r.x.into(),
            y: r.y.into(),
            w: r.w.into(),
            h: r.h.into(),
        }
    }
}

impl From<Rect> for mirx::Rect {
    fn from(r: Rect) -> Self {
        Self {
            x: r.x.into(),
            y: r.y.into(),
            w: r.w.into(),
            h: r.h.into(),
        }
    }
}

impl From<mirx::Transform> for Transform {
    fn from(t: mirx::Transform) -> Self {
        Self {
            m00: t.m00.into(),
            m01: t.m01.into(),
            tx: t.tx.into(),
            m10: t.m10.into(),
            m11: t.m11.into(),
            ty: t.ty.into(),
        }
    }
}

impl From<Transform> for mirx::Transform {
    fn from(t: Transform) -> Self {
        Self {
            m00: t.m00.into(),
            m01: t.m01.into(),
            tx: t.tx.into(),
            m10: t.m10.into(),
            m11: t.m11.into(),
            ty: t.ty.into(),
        }
    }
}

impl From<mirx::Color> for Color {
    fn from(c: mirx::Color) -> Self {
        Self {
            r: c.r,
            g: c.g,
            b: c.b,
            a: c.a,
        }
    }
}

impl From<Color> for mirx::Color {
    fn from(c: Color) -> Self {
        Self {
            r: c.r,
            g: c.g,
            b: c.b,
            a: c.a,
        }
    }
}

impl From<mirx::PathCmd> for PathCmd {
    fn from(c: mirx::PathCmd) -> Self {
        match c {
            mirx::PathCmd::MoveTo(p) => Self::MoveTo(p.into()),
            mirx::PathCmd::LineTo(p) => Self::LineTo(p.into()),
            mirx::PathCmd::QuadTo { ctrl, end } => Self::QuadTo {
                ctrl: ctrl.into(),
                end: end.into(),
            },
            mirx::PathCmd::CubicTo { ctrl1, ctrl2, end } => Self::CubicTo {
                ctrl1: ctrl1.into(),
                ctrl2: ctrl2.into(),
                end: end.into(),
            },
            mirx::PathCmd::Close => Self::Close,
        }
    }
}

impl From<PathCmd> for mirx::PathCmd {
    fn from(c: PathCmd) -> Self {
        match c {
            PathCmd::MoveTo(p) => Self::MoveTo(p.into()),
            PathCmd::LineTo(p) => Self::LineTo(p.into()),
            PathCmd::QuadTo { ctrl, end } => Self::QuadTo {
                ctrl: ctrl.into(),
                end: end.into(),
            },
            PathCmd::CubicTo { ctrl1, ctrl2, end } => Self::CubicTo {
                ctrl1: ctrl1.into(),
                ctrl2: ctrl2.into(),
                end: end.into(),
            },
            PathCmd::Close => Self::Close,
        }
    }
}

impl From<mirx::Path> for Path {
    fn from(p: mirx::Path) -> Self {
        let cmds: Vec<PathCmd> = p.cmds.into_iter().map(Into::into).collect();
        Self {
            cmds: Cow::Owned(cmds),
        }
    }
}

impl From<Path> for mirx::Path {
    fn from(p: Path) -> Self {
        let cmds: Vec<mirx::PathCmd> = p.cmds.into_owned().into_iter().map(Into::into).collect();
        Self::from_cmds(cmds)
    }
}

impl From<mirx::ResourceRef> for ResourceRef {
    fn from(r: mirx::ResourceRef) -> Self {
        match r {
            mirx::ResourceRef::Token(s) => Self::Token(Cow::Owned(s)),
            mirx::ResourceRef::Index(i) => Self::Index(i),
        }
    }
}

impl From<ResourceRef> for mirx::ResourceRef {
    fn from(r: ResourceRef) -> Self {
        match r {
            ResourceRef::Token(s) => Self::Token(s.into_owned()),
            ResourceRef::Index(i) => Self::Index(i),
        }
    }
}

impl From<mirx::FillRule> for FillRule {
    fn from(r: mirx::FillRule) -> Self {
        match r {
            mirx::FillRule::EvenOdd => Self::EvenOdd,
            mirx::FillRule::NonZero => Self::NonZero,
        }
    }
}

impl From<FillRule> for mirx::FillRule {
    fn from(r: FillRule) -> Self {
        match r {
            FillRule::EvenOdd => Self::EvenOdd,
            FillRule::NonZero => Self::NonZero,
        }
    }
}

impl From<mirx::CompositeMode> for CompositeMode {
    fn from(m: mirx::CompositeMode) -> Self {
        match m {
            mirx::CompositeMode::SourceOver => Self::SourceOver,
            mirx::CompositeMode::Add => Self::Add,
            mirx::CompositeMode::Screen => Self::Screen,
            mirx::CompositeMode::Multiply => Self::Multiply,
            mirx::CompositeMode::Darken => Self::Darken,
            mirx::CompositeMode::Lighten => Self::Lighten,
            mirx::CompositeMode::Difference => Self::Difference,
        }
    }
}

impl From<CompositeMode> for mirx::CompositeMode {
    fn from(m: CompositeMode) -> Self {
        match m {
            CompositeMode::SourceOver => Self::SourceOver,
            CompositeMode::Add => Self::Add,
            CompositeMode::Screen => Self::Screen,
            CompositeMode::Multiply => Self::Multiply,
            CompositeMode::Darken => Self::Darken,
            CompositeMode::Lighten => Self::Lighten,
            CompositeMode::Difference => Self::Difference,
        }
    }
}

impl From<mirx::SceneOp> for SceneOp {
    fn from(op: mirx::SceneOp) -> Self {
        match op {
            mirx::SceneOp::GroupBegin {
                transform,
                opacity,
                clip,
                mask,
                filter,
                disjoint_hint,
            } => Self::GroupBegin {
                transform: transform.map(Into::into),
                opacity,
                clip: clip.map(Into::into),
                mask: mask.map(Into::into),
                filter: filter.map(Into::into),
                disjoint_hint,
            },
            mirx::SceneOp::GroupEnd => Self::GroupEnd,
            mirx::SceneOp::FillPath {
                path,
                transform,
                color,
                opa,
                fill_rule,
            } => Self::FillPath {
                path: path.into(),
                transform: transform.into(),
                color: color.into(),
                opa,
                fill_rule: fill_rule.into(),
            },
            mirx::SceneOp::StrokePath {
                path,
                transform,
                color,
                width,
                opa,
                line_cap,
                line_join,
                miter_limit,
            } => Self::StrokePath {
                path: path.into(),
                transform: transform.into(),
                color: color.into(),
                width: width.into(),
                opa,
                line_cap,
                line_join,
                miter_limit: miter_limit.into(),
            },
            mirx::SceneOp::FillRect {
                area,
                transform,
                quad,
                color,
                radius,
                opa,
            } => Self::FillRect {
                area: area.into(),
                transform: transform.into(),
                quad: quad.map(|q| [q[0].into(), q[1].into(), q[2].into(), q[3].into()]),
                color: color.into(),
                radius: radius.into(),
                opa,
            },
            mirx::SceneOp::Border {
                area,
                transform,
                quad,
                color,
                width,
                radius,
                opa,
            } => Self::Border {
                area: area.into(),
                transform: transform.into(),
                quad: quad.map(|q| [q[0].into(), q[1].into(), q[2].into(), q[3].into()]),
                color: color.into(),
                width: width.into(),
                radius: radius.into(),
                opa,
            },
            mirx::SceneOp::Label {
                font,
                pos,
                transform,
                color,
                opa,
                text,
            } => Self::Label {
                font: font.into(),
                pos: pos.into(),
                transform: transform.into(),
                color: color.into(),
                opa,
                text: Cow::Owned(text),
            },
            mirx::SceneOp::Line {
                p1,
                p2,
                transform,
                color,
                width,
                opa,
            } => Self::Line {
                p1: p1.into(),
                p2: p2.into(),
                transform: transform.into(),
                color: color.into(),
                width: width.into(),
                opa,
            },
            mirx::SceneOp::Arc {
                center,
                transform,
                radius,
                start_angle,
                end_angle,
                color,
                width,
                opa,
            } => Self::Arc {
                center: center.into(),
                transform: transform.into(),
                radius: radius.into(),
                start_angle: start_angle.into(),
                end_angle: end_angle.into(),
                color: color.into(),
                width: width.into(),
                opa,
            },
            mirx::SceneOp::Blit {
                texture,
                pos,
                size,
                transform,
                quad,
                opa,
                radius,
                composite,
            } => Self::Blit {
                texture: texture.into(),
                pos: pos.into(),
                size: size.into(),
                transform: transform.into(),
                quad: quad.map(|q| [q[0].into(), q[1].into(), q[2].into(), q[3].into()]),
                opa,
                radius: radius.into(),
                composite: composite.into(),
            },
        }
    }
}

impl From<SceneOp> for mirx::SceneOp {
    fn from(op: SceneOp) -> Self {
        match op {
            SceneOp::GroupBegin {
                transform,
                opacity,
                clip,
                mask,
                filter,
                disjoint_hint,
            } => Self::GroupBegin {
                transform: transform.map(Into::into),
                opacity,
                clip: clip.map(Into::into),
                mask: mask.map(Into::into),
                filter: filter.map(Into::into),
                disjoint_hint,
            },
            SceneOp::GroupEnd => Self::GroupEnd,
            SceneOp::FillPath {
                path,
                transform,
                color,
                opa,
                fill_rule,
            } => Self::FillPath {
                path: path.into(),
                transform: transform.into(),
                color: color.into(),
                opa,
                fill_rule: fill_rule.into(),
            },
            SceneOp::StrokePath {
                path,
                transform,
                color,
                width,
                opa,
                line_cap,
                line_join,
                miter_limit,
            } => Self::StrokePath {
                path: path.into(),
                transform: transform.into(),
                color: color.into(),
                width: width.into(),
                opa,
                line_cap,
                line_join,
                miter_limit: miter_limit.into(),
            },
            SceneOp::FillRect {
                area,
                transform,
                quad,
                color,
                radius,
                opa,
            } => Self::FillRect {
                area: area.into(),
                transform: transform.into(),
                quad: quad.map(|q| [q[0].into(), q[1].into(), q[2].into(), q[3].into()]),
                color: color.into(),
                radius: radius.into(),
                opa,
            },
            SceneOp::Border {
                area,
                transform,
                quad,
                color,
                width,
                radius,
                opa,
            } => Self::Border {
                area: area.into(),
                transform: transform.into(),
                quad: quad.map(|q| [q[0].into(), q[1].into(), q[2].into(), q[3].into()]),
                color: color.into(),
                width: width.into(),
                radius: radius.into(),
                opa,
            },
            SceneOp::Label {
                font,
                pos,
                transform,
                color,
                opa,
                text,
            } => Self::Label {
                font: font.into(),
                pos: pos.into(),
                transform: transform.into(),
                color: color.into(),
                opa,
                text: text.into_owned(),
            },
            SceneOp::Line {
                p1,
                p2,
                transform,
                color,
                width,
                opa,
            } => Self::Line {
                p1: p1.into(),
                p2: p2.into(),
                transform: transform.into(),
                color: color.into(),
                width: width.into(),
                opa,
            },
            SceneOp::Arc {
                center,
                transform,
                radius,
                start_angle,
                end_angle,
                color,
                width,
                opa,
            } => Self::Arc {
                center: center.into(),
                transform: transform.into(),
                radius: radius.into(),
                start_angle: start_angle.into(),
                end_angle: end_angle.into(),
                color: color.into(),
                width: width.into(),
                opa,
            },
            SceneOp::Blit {
                texture,
                pos,
                size,
                transform,
                quad,
                opa,
                radius,
                composite,
            } => Self::Blit {
                texture: texture.into(),
                pos: pos.into(),
                size: size.into(),
                transform: transform.into(),
                quad: quad.map(|q| [q[0].into(), q[1].into(), q[2].into(), q[3].into()]),
                opa,
                radius: radius.into(),
                composite: composite.into(),
            },
        }
    }
}

impl From<mirx::Scene> for Scene {
    fn from(s: mirx::Scene) -> Self {
        let ops: Vec<SceneOp> = s.ops.into_iter().map(Into::into).collect();
        Self { ops }
    }
}

impl From<Scene> for mirx::Scene {
    fn from(s: Scene) -> Self {
        let ops: Vec<mirx::SceneOp> = s.ops.into_iter().map(Into::into).collect();
        Self::from_ops(ops)
    }
}
