use crate::YaxColors;

use core::fmt;
use core::num::Wrapping;
use core::ops::Neg;

pub enum NumberStyleHint {
    Signed,
    HexSigned,
    SignedWithSign,
    HexSignedWithSign,
    SignedWithSignSplit,
    HexSignedWithSignSplit,
    Unsigned,
    HexUnsigned,
    UnsignedWithSign,
    HexUnsignedWithSign
}

pub fn format_number_i32<W: fmt::Write, Y: YaxColors>(colors: &Y, f: &mut W, i: i32, hint: NumberStyleHint) -> fmt::Result {
    match hint {
        NumberStyleHint::Signed => {
            write!(f, "{}", colors.number(i))
        },
        NumberStyleHint::HexSigned => {
            write!(f, "{}", colors.number(signed_i32_hex(i)))
        },
        NumberStyleHint::Unsigned => {
            write!(f, "{}", colors.number(i as u32))
        },
        NumberStyleHint::HexUnsigned => {
            write!(f, "{}", colors.number(u32_hex(i as u32)))
        },
        NumberStyleHint::SignedWithSignSplit => {
            if i == core::i32::MIN {
                write!(f, "- {}", colors.number("2147483647"))
            } else if i < 0 {
                write!(f, "- {}", colors.number(-Wrapping(i)))
            } else {
                write!(f, "+ {}", colors.number(i))
            }
        }
        NumberStyleHint::HexSignedWithSignSplit => {
            if i == core::i32::MIN {
                write!(f, "- {}", colors.number("0x7fffffff"))
            } else if i < 0 {
                write!(f, "- {}", colors.number(u32_hex((-Wrapping(i)).0 as u32)))
            } else {
                write!(f, "+ {}", colors.number(u32_hex(i as u32)))
            }
        },
        NumberStyleHint::HexSignedWithSign => {
            write!(f, "{}", signed_i32_hex(i))
        },
        NumberStyleHint::SignedWithSign => {
            write!(f, "{:+}", i)
        }
        NumberStyleHint::HexUnsignedWithSign => {
            write!(f, "{:+#x}", i as u32)
        },
        NumberStyleHint::UnsignedWithSign => {
            write!(f, "{:+}", i as u32)
        }
    }
}

pub struct SignedHexDisplay<T: core::fmt::LowerHex + Neg> {
    value: T,
    negative: bool
}

impl<T: fmt::LowerHex + Neg + Copy> fmt::Display for SignedHexDisplay<T> where Wrapping<T>: Neg, <Wrapping<T> as Neg>::Output: fmt::LowerHex {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        if self.negative {
            write!(f, "-{:#x}", -Wrapping(self.value))
        } else {
            write!(f, "{:#x}", self.value)
        }
    }
}

pub fn u8_hex(value: u8) -> SignedHexDisplay<i8> {
    SignedHexDisplay {
        value: value as i8,
        negative: false,
    }
}

pub fn signed_i8_hex(imm: i8) -> SignedHexDisplay<i8> {
    SignedHexDisplay {
        value: imm,
        negative: imm < 0,
    }
}

pub fn u16_hex(value: u16) -> SignedHexDisplay<i16> {
    SignedHexDisplay {
        value: value as i16,
        negative: false,
    }
}

pub fn signed_i16_hex(imm: i16) -> SignedHexDisplay<i16> {
    SignedHexDisplay {
        value: imm,
        negative: imm < 0,
    }
}

pub fn u32_hex(value: u32) -> SignedHexDisplay<i32> {
    SignedHexDisplay {
        value: value as i32,
        negative: false,
    }
}

pub fn signed_i32_hex(imm: i32) -> SignedHexDisplay<i32> {
    SignedHexDisplay {
        value: imm,
        negative: imm < 0,
    }
}

pub fn u64_hex(value: u64) -> SignedHexDisplay<i64> {
    SignedHexDisplay {
        value: value as i64,
        negative: false,
    }
}

pub fn signed_i64_hex(imm: i64) -> SignedHexDisplay<i64> {
    SignedHexDisplay {
        value: imm,
        negative: imm < 0,
    }
}